VirtualBox

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

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

Shared Clipboard/Transfers: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 83.4 KB
Line 
1/* $Id: VBoxSharedClipboardSvc.cpp 81960 2019-11-18 19:00:50Z 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 is the host half of the clibpoard proxying
22 * between the host and the guest. The guest parts live in VBoxClient, VBoxTray
23 * and VBoxService depending on the OS, with code shared between host and guest
24 * under src/VBox/GuestHost/SharedClipboard/.
25 *
26 * The service is split into a platform-independent core and platform-specific
27 * backends. The service defines two communication protocols - one to
28 * communicate with the clipboard service running on the guest, and one to
29 * communicate with the backend. These will be described in a very skeletal
30 * fashion here.
31 *
32 * r=bird: The "two communication protocols" does not seems to be factual, there
33 * is only one protocol, the first one mentioned. It cannot be backend
34 * specific, because the guest/host protocol is platform and backend agnostic in
35 * nature. You may call it versions, but I take a great dislike to "protocol
36 * versions" here, as you've just extended the existing protocol with a feature
37 * that allows to transfer files and directories too. See @bugref{9437#c39}.
38 *
39 *
40 * @section sec_hostclip_guest_proto The guest communication protocol
41 *
42 * The guest clipboard service communicates with the host service over HGCM
43 * (the host is a HGCM service). HGCM is connection based, so the guest side
44 * has to connect before anything else can be done. (Windows hosts currently
45 * only support one simultaneous connection.) Once it has connected, it can
46 * send messages to the host services, some of which will receive immediate
47 * replies from the host, others which will block till a reply becomes
48 * available. The latter is because HGCM does't allow the host to initiate
49 * communication, it must be guest triggered. The HGCM service is single
50 * threaded, so it doesn't matter if the guest tries to send lots of requests in
51 * parallel, the service will process them one at the time.
52 *
53 * There are currently four messages defined. The first is
54 * VBOX_SHCL_GUEST_FN_MSG_GET / VBOX_SHCL_GUEST_FN_GET_HOST_MSG_OLD, which waits
55 * for a message from the host. If a host message is sent while the guest is
56 * not waiting, it will be queued until the guest requests it. The host code
57 * only supports a single simultaneous GET call from one client guest.
58 *
59 * The second guest message is VBOX_SHCL_GUEST_FN_FORMATS_REPORT, which tells
60 * the host that the guest has new clipboard data available. The third is
61 * VBOX_SHCL_GUEST_FN_DATA_READ, which asks the host to send its clipboard data
62 * and waits until it arrives. The host supports at most one simultaneous
63 * VBOX_SHCL_GUEST_FN_DATA_READ call from a guest - if a second call is made
64 * before the first has returned, the first will be aborted.
65 *
66 * The last guest message is VBOX_SHCL_GUEST_FN_DATA_WRITE, which is used to
67 * send the contents of the guest clipboard to the host. This call should be
68 * used after the host has requested data from the guest.
69 *
70 *
71 * @section sec_hostclip_backend_proto The communication protocol with the
72 * platform-specific backend
73 *
74 * The initial protocol implementation (called protocol v0) was very simple,
75 * and could only handle simple data (like copied text and so on). It also
76 * was limited to two (2) fixed parameters at all times.
77 *
78 * Since VBox 6.1 a newer protocol (v1) has been established to also support
79 * file transfers. This protocol uses a (per-client) message queue instead
80 * (see VBOX_SHCL_GUEST_FN_GET_HOST_MSG_OLD vs. VBOX_SHCL_GUEST_FN_GET_HOST_MSG).
81 *
82 * To distinguish the old (legacy) or new(er) protocol, the VBOX_SHCL_GUEST_FN_CONNECT
83 * message has been introduced. If an older guest does not send this message,
84 * an appropriate translation will be done to serve older Guest Additions (< 6.1).
85 *
86 * The protocol also support out-of-order messages by using so-called "context IDs",
87 * which are generated by the host. A context ID consists of a so-called "source event ID"
88 * and a so-called "event ID". Each HGCM client has an own, random, source event ID and
89 * generates non-deterministic event IDs so that the guest side does not known what
90 * comes next; the guest side has to reply with the same conext ID which was sent by
91 * the host request.
92 *
93 * Also see the protocol changelog at VBoxShClSvc.h.
94 *
95 *
96 * @section sec_uri_intro Transferring files
97 *
98 * Since VBox x.x.x transferring files via Shared Clipboard is supported.
99 * See the VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS define for supported / enabled
100 * platforms. This is called "Shared Clipboard transfers".
101 *
102 * Copying files / directories from guest A to guest B requires the host
103 * service to act as a proxy and cache, as we don't allow direct VM-to-VM
104 * communication. Copying from / to the host also is taken into account.
105 *
106 * At the moment a transfer is a all-or-nothing operation, e.g. it either
107 * completes or fails completely. There might be callbacks in the future
108 * to e.g. skip failing entries.
109 *
110 * Known limitations:
111 *
112 * - Support for VRDE (VRDP) is not implemented yet (see #9498).
113 * - Unicode support on Windows hosts / guests is not enabled (yet).
114 * - Symbolic links / Windows junctions are not allowed.
115 * - Windows alternate data streams (ADS) are not allowed.
116 * - No support for ACLs yet.
117 * - No (maybe never) support for NT4.
118 *
119 * @section sec_transfers_areas Clipboard areas.
120 *
121 * For larger / longer transfers there might be file data
122 * temporarily cached on the host, which has not been transferred to the
123 * destination yet. Such a cache is called a "Shared Clipboard Area", which
124 * in turn is identified by a unique ID across all VMs running on the same
125 * host. To control the access (and needed cleanup) of such clipboard areas,
126 * VBoxSVC (Main) is used for this task. A Shared Clipboard client can register,
127 * unregister, attach to and detach from a clipboard area. If all references
128 * to a clipboard area are released, a clipboard area gets detroyed automatically
129 * by VBoxSVC.
130 *
131 * By default a clipboard area lives in the user's temporary directory in the
132 * sub folder "VirtualBox Shared Clipboards/clipboard-<ID>". VBoxSVC does not
133 * do any file locking in a clipboard area, but keeps the clipboard areas's
134 * directory open to prevent deletion by third party processes.
135 *
136 * @todo We might use some VFS / container (IPRT?) for this instead of the
137 * host's file system directly?
138 * bird> Yes, but may take some work as we don't have the pick and choose
139 * kind of VFS containers implemented yet.
140 *
141 * @section sec_transfer_structure Transfer handling structure
142 *
143 * All structures / classes are designed for running on both, on the guest
144 * (via VBoxTray / VBoxClient) or on the host (host service) to avoid code
145 * duplication where applicable.
146 *
147 * Per HGCM client there is a so-called "transfer context", which in turn can
148 * have one or mulitple so-called "Shared Clipboard transfer" objects. At the
149 * moment we only support on concurrent Shared Clipboard transfer per transfer
150 * context. It's being used for reading from a source or writing to destination,
151 * depening on its direction. An Shared Clipboard transfer can have optional
152 * callbacks which might be needed by various implementations. Also, transfers
153 * optionally can run in an asynchronous thread to prevent blocking the UI while
154 * running.
155 *
156 * A Shared Clipboard transfer can maintain its own clipboard area; for the host
157 * service such a clipboard area is coupled to a clipboard area registered or
158 * attached with VBoxSVC. This is needed because multiple transfers from
159 * multiple VMs (n:n) can rely on the same clipboard area, so there needs a
160 * master keeping tracking of a clipboard area. To minimize IPC traffic only the
161 * minimum de/attaching is done at the moment. A clipboard area gets cleaned up
162 * (i.e. physically deleted) if no references are held to it anymore, or if
163 * VBoxSVC goes down.
164 *
165 * @section sec_transfer_providers Transfer providers
166 *
167 * For certain implementations (for example on Windows guests / hosts, using
168 * IDataObject and IStream objects) a more flexible approach reqarding reading /
169 * writing is needed. For this so-called transfer providers abstract the way of how
170 * data is being read / written in the current context (host / guest), while
171 * the rest of the code stays the same.
172 *
173 * @section sec_transfer_protocol Transfer protocol
174 *
175 * The host service issues commands which the guest has to respond with an own
176 * message to. The protocol itself is designed so that it has primitives to list
177 * directories and open/close/read/write file system objects.
178 *
179 * Note that this is different from the DnD approach, as Shared Clipboard transfers
180 * need to be deeper integrated within the host / guest OS (i.e. for progress UI),
181 * and this might require non-monolithic / random access APIs to achieve.
182 *
183 * As there can be multiple file system objects (fs objects) selected for transfer,
184 * a transfer can be queried for its root entries, which then contains the top-level
185 * elements. Based on these elements, (a) (recursive) listing(s) can be performed
186 * to (partially) walk down into directories and query fs object information. The
187 * provider provides appropriate interface for this, even if not all implementations
188 * might need this mechanism.
189 *
190 * An Shared Clipboard transfer has three stages:
191 * - 1. Announcement: An Shared Clipboard transfer-compatible format (currently only one format available)
192 * has been announced, the destination side creates a transfer object, which then,
193 * depending on the actual implementation, can be used to tell the OS that
194 * there is transfer (file) data available.
195 * At this point this just acts as a (kind-of) promise to the OS that we
196 * can provide (file) data at some later point in time.
197 *
198 * - 2. Initialization: As soon as the OS requests the (file) data, mostly triggered
199 * by the user starting a paste operation (CTRL + V), the transfer get initialized
200 * on the destination side, which in turn lets the source know that a transfer
201 * is going to happen.
202 *
203 * - 3. Transfer: At this stage the actual transfer from source to the destination takes
204 * place. How the actual transfer is structurized (e.g. which files / directories
205 * are transferred in which order) depends on the destination implementation. This
206 * is necessary in order to fulfill requirements on the destination side with
207 * regards to ETA calculation or other dependencies.
208 * Both sides can abort or cancel the transfer at any time.
209 */
210
211
212/*********************************************************************************************************************************
213* Header Files *
214*********************************************************************************************************************************/
215#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
216#include <VBox/log.h>
217
218#include <VBox/AssertGuest.h>
219#include <VBox/GuestHost/clipboard-helper.h>
220#include <VBox/HostServices/Service.h>
221#include <VBox/HostServices/VBoxClipboardSvc.h>
222#include <VBox/HostServices/VBoxClipboardExt.h>
223
224#include <iprt/alloc.h>
225#include <iprt/string.h>
226#include <iprt/assert.h>
227#include <iprt/critsect.h>
228#include <iprt/rand.h>
229
230#include <VBox/err.h>
231#include <VBox/vmm/ssm.h>
232
233#include "VBoxSharedClipboardSvc-internal.h"
234#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
235# include "VBoxSharedClipboardSvc-transfers.h"
236#endif
237
238using namespace HGCM;
239
240
241/*********************************************************************************************************************************
242* Global Variables *
243*********************************************************************************************************************************/
244PVBOXHGCMSVCHELPERS g_pHelpers;
245
246static RTCRITSECT g_CritSect;
247/** Global Shared Clipboard mode. */
248static uint32_t g_uMode = VBOX_SHCL_MODE_OFF;
249#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
250/** Global Shared Clipboard (file) transfer mode. */
251uint32_t g_fTransferMode = VBOX_SHCL_TRANSFER_MODE_DISABLED;
252#endif
253
254/** Is the clipboard running in headless mode? */
255static bool g_fHeadless = false;
256
257/** Holds the service extension state. */
258SHCLEXTSTATE g_ExtState = { 0 };
259
260/** Global map of all connected clients. */
261ClipboardClientMap g_mapClients;
262
263/** Global list of all clients which are queued up (deferred return) and ready
264 * to process new commands. The key is the (unique) client ID. */
265ClipboardClientQueue g_listClientsDeferred;
266
267/** Host feature mask for VBOX_SHCL_GUEST_FN_REPORT_FEATURES/VBOX_SHCL_GUEST_FN_QUERY_FEATURES. */
268static uint64_t const g_fHostFeatures0 = VBOX_SHCL_HF_NONE;
269
270
271/**
272 * Returns the current Shared Clipboard service mode.
273 *
274 * @returns Current Shared Clipboard service mode.
275 */
276uint32_t ShClSvcGetMode(void)
277{
278 return g_uMode;
279}
280
281/**
282 * Getter for headless setting. Also needed by testcase.
283 *
284 * @returns Whether service currently running in headless mode or not.
285 */
286bool ShClSvcGetHeadless(void)
287{
288 return g_fHeadless;
289}
290
291static int shClSvcModeSet(uint32_t uMode)
292{
293 int rc = VERR_NOT_SUPPORTED;
294
295 switch (uMode)
296 {
297 case VBOX_SHCL_MODE_OFF:
298 RT_FALL_THROUGH();
299 case VBOX_SHCL_MODE_HOST_TO_GUEST:
300 RT_FALL_THROUGH();
301 case VBOX_SHCL_MODE_GUEST_TO_HOST:
302 RT_FALL_THROUGH();
303 case VBOX_SHCL_MODE_BIDIRECTIONAL:
304 {
305 g_uMode = uMode;
306
307 rc = VINF_SUCCESS;
308 break;
309 }
310
311 default:
312 {
313 g_uMode = VBOX_SHCL_MODE_OFF;
314 break;
315 }
316 }
317
318 LogFlowFuncLeaveRC(rc);
319 return rc;
320}
321
322bool ShClSvcLock(void)
323{
324 return RT_SUCCESS(RTCritSectEnter(&g_CritSect));
325}
326
327void ShClSvcUnlock(void)
328{
329 int rc2 = RTCritSectLeave(&g_CritSect);
330 AssertRC(rc2);
331}
332
333/**
334 * Resets a client's state message queue.
335 *
336 * @param pClient Pointer to the client data structure to reset message queue for.
337 */
338void shClSvcMsgQueueReset(PSHCLCLIENT pClient)
339{
340 LogFlowFuncEnter();
341
342 while (!pClient->queueMsg.isEmpty())
343 {
344 RTMemFree(pClient->queueMsg.last());
345 pClient->queueMsg.removeLast();
346 }
347}
348
349/**
350 * Allocates a new clipboard message.
351 *
352 * @returns Allocated clipboard message, or NULL on failure.
353 * @param uMsg Message type of message to allocate.
354 * @param cParms Number of HGCM parameters to allocate.
355 */
356PSHCLCLIENTMSG shClSvcMsgAlloc(uint32_t uMsg, uint32_t cParms)
357{
358 PSHCLCLIENTMSG pMsg = (PSHCLCLIENTMSG)RTMemAlloc(sizeof(SHCLCLIENTMSG));
359 if (pMsg)
360 {
361 pMsg->paParms = (PVBOXHGCMSVCPARM)RTMemAllocZ(sizeof(VBOXHGCMSVCPARM) * cParms);
362 if (pMsg->paParms)
363 {
364 pMsg->cParms = cParms;
365 pMsg->uMsg = uMsg;
366
367 return pMsg;
368 }
369 }
370
371 RTMemFree(pMsg);
372 return NULL;
373}
374
375/**
376 * Frees a formerly allocated clipboard message.
377 *
378 * @param pMsg Clipboard message to free.
379 * The pointer will be invalid after calling this function.
380 */
381void shClSvcMsgFree(PSHCLCLIENTMSG pMsg)
382{
383 if (!pMsg)
384 return;
385
386 if (pMsg->paParms)
387 RTMemFree(pMsg->paParms);
388
389 RTMemFree(pMsg);
390 pMsg = NULL;
391}
392
393/**
394 * Sets the VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT and VBOX_SHCL_GUEST_FN_MSG_PEEK_NOWAIT
395 * return parameters.
396 *
397 * @param pMsg Message to set return parameters to.
398 * @param paDstParms The peek parameter vector.
399 * @param cDstParms The number of peek parameters (at least two).
400 * @remarks ASSUMES the parameters has been cleared by clientMsgPeek.
401 */
402void shClSvcMsgSetPeekReturn(PSHCLCLIENTMSG pMsg, PVBOXHGCMSVCPARM paDstParms, uint32_t cDstParms)
403{
404 Assert(cDstParms >= 2);
405 if (paDstParms[0].type == VBOX_HGCM_SVC_PARM_32BIT)
406 paDstParms[0].u.uint32 = pMsg->uMsg;
407 else
408 paDstParms[0].u.uint64 = pMsg->uMsg;
409 paDstParms[1].u.uint32 = pMsg->cParms;
410
411 uint32_t i = RT_MIN(cDstParms, pMsg->cParms + 2);
412 while (i-- > 2)
413 switch (pMsg->paParms[i - 2].type)
414 {
415 case VBOX_HGCM_SVC_PARM_32BIT: paDstParms[i].u.uint32 = ~(uint32_t)sizeof(uint32_t); break;
416 case VBOX_HGCM_SVC_PARM_64BIT: paDstParms[i].u.uint32 = ~(uint32_t)sizeof(uint64_t); break;
417 case VBOX_HGCM_SVC_PARM_PTR: paDstParms[i].u.uint32 = pMsg->paParms[i - 2].u.pointer.size; break;
418 }
419}
420
421/**
422 * Sets the VBOX_SHCL_GUEST_FN_GET_HOST_MSG_OLD return parameters.
423 *
424 * This function does the necessary translation between the legacy protocol (<= VBox 6.0) and the new protocols (>= VBox 6.1),
425 * as messages are always stored as >= v1 messages in the message queue.
426 *
427 * @returns VBox status code.
428 * @param pMsg Message to set return parameters to.
429 * @param paDstParms The peek parameter vector.
430 * @param cDstParms The number of peek parameters (at least two).
431 * @param pfRemove Returns whether the message can be removed from the queue or not.
432 * This is needed for certain messages which need to stay around for more than one (guest) call.
433 */
434int shClSvcMsgSetGetHostMsgOldReturn(PSHCLCLIENTMSG pMsg, PVBOXHGCMSVCPARM paDstParms, uint32_t cDstParms,
435 bool *pfRemove)
436{
437 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
438 AssertPtrReturn(paDstParms, VERR_INVALID_POINTER);
439 AssertReturn (cDstParms >= 2, VERR_INVALID_PARAMETER);
440 AssertPtrReturn(pfRemove, VERR_INVALID_POINTER);
441
442 int rc = VINF_SUCCESS;
443
444 bool fRemove = true;
445
446 LogFlowFunc(("uMsg=%RU32 (%s), cParms=%RU32\n",
447 pMsg->uMsg, ShClHostMsgToStr(pMsg->uMsg), pMsg->cParms));
448
449 switch (pMsg->uMsg)
450 {
451 case VBOX_SHCL_HOST_MSG_QUIT:
452 {
453 HGCMSvcSetU32(&paDstParms[0], VBOX_SHCL_HOST_MSG_QUIT);
454 HGCMSvcSetU32(&paDstParms[1], 0 /* Not used */);
455 break;
456 }
457
458 case VBOX_SHCL_HOST_MSG_READ_DATA:
459 {
460 HGCMSvcSetU32(&paDstParms[0], VBOX_SHCL_HOST_MSG_READ_DATA);
461 AssertBreakStmt(pMsg->cParms >= 2, rc = VERR_INVALID_PARAMETER); /* Paranoia. */
462 uint32_t uSrcFmt = VBOX_SHCL_FMT_NONE;
463 uint32_t uDstFmt = VBOX_SHCL_FMT_NONE;
464 rc = HGCMSvcGetU32(&pMsg->paParms[2] /* uFormat */, &uSrcFmt);
465 if (RT_SUCCESS(rc))
466 {
467 if (uSrcFmt & VBOX_SHCL_FMT_UNICODETEXT)
468 uDstFmt = VBOX_SHCL_FMT_UNICODETEXT;
469 else if (uSrcFmt & VBOX_SHCL_FMT_BITMAP)
470 uDstFmt = VBOX_SHCL_FMT_BITMAP;
471 else if (uSrcFmt & VBOX_SHCL_FMT_HTML)
472 uDstFmt = VBOX_SHCL_FMT_HTML;
473 else
474 AssertStmt(uSrcFmt == VBOX_SHCL_FMT_NONE, uSrcFmt = VBOX_SHCL_FMT_NONE);
475
476 LogFlowFunc(("uSrcFmt=0x%x, uDstFmt=0x%x\n", uSrcFmt, uDstFmt));
477
478 /* Remove format we're going to return from the queued message. */
479 uSrcFmt &= ~uDstFmt;
480 HGCMSvcSetU32(&pMsg->paParms[2], uSrcFmt);
481
482 /* Only report back one format at a time. */
483 HGCMSvcSetU32(&paDstParms[1], uDstFmt);
484
485 /* If no more formats are left, the message can be removed finally. */
486 fRemove = uSrcFmt == VBOX_SHCL_FMT_NONE;
487
488 LogFlowFunc(("uSrcFmt=0x%x, fRemove=%RTbool\n", uSrcFmt, fRemove));
489 }
490 break;
491 }
492
493 case VBOX_SHCL_HOST_MSG_FORMATS_REPORT:
494 {
495 HGCMSvcSetU32(&paDstParms[0], VBOX_SHCL_HOST_MSG_FORMATS_REPORT);
496 AssertBreakStmt(pMsg->cParms >= 2, rc = VERR_INVALID_PARAMETER); /* Paranoia. */
497 uint32_t uFmts;
498 rc = HGCMSvcGetU32(&pMsg->paParms[1] /* uFormats */, &uFmts);
499 if (RT_SUCCESS(rc))
500 HGCMSvcSetU32(&paDstParms[1], uFmts);
501 break;
502 }
503
504 default:
505 AssertFailed(); /* Not supported by legacy protocol. */
506 rc = VERR_NOT_SUPPORTED;
507 break;
508 }
509
510 *pfRemove = fRemove;
511
512 LogFlowFuncLeaveRC(rc);
513 return rc;
514}
515
516/**
517 * Adds a new message to a client'S message queue.
518 *
519 * @returns IPRT status code.
520 * @param pClient Pointer to the client data structure to add new message to.
521 * @param pMsg Pointer to message to add. The queue then owns the pointer.
522 * @param fAppend Whether to append or prepend the message to the queue.
523 */
524int shClSvcMsgAdd(PSHCLCLIENT pClient, PSHCLCLIENTMSG pMsg, bool fAppend)
525{
526 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
527
528 LogFlowFunc(("uMsg=%RU32 (%s), cParms=%RU32, fAppend=%RTbool\n",
529 pMsg->uMsg, ShClHostMsgToStr(pMsg->uMsg), pMsg->cParms, fAppend));
530
531 if (fAppend)
532 pClient->queueMsg.append(pMsg);
533 else
534 pClient->queueMsg.prepend(pMsg);
535
536 /** @todo Catch / handle OOM? */
537
538 return VINF_SUCCESS;
539}
540
541/**
542 * Initializes a Shared Clipboard client.
543 *
544 * @param pClient Client to initialize.
545 * @param uClientID HGCM client ID to assign client to.
546 */
547int shClSvcClientInit(PSHCLCLIENT pClient, uint32_t uClientID)
548{
549 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
550
551 /* Assign the client ID. */
552 pClient->State.uClientID = uClientID;
553
554 LogFlowFunc(("[Client %RU32]\n", pClient->State.uClientID));
555
556 int rc = RTCritSectInit(&pClient->CritSect);
557 if (RT_SUCCESS(rc))
558 {
559 /* Create the client's own event source. */
560 rc = ShClEventSourceCreate(&pClient->Events, 0 /* ID, ignored */);
561 if (RT_SUCCESS(rc))
562 {
563 LogFlowFunc(("[Client %RU32] Using event source %RU32\n", uClientID, pClient->Events.uID));
564
565 /* Reset the client state. */
566 shclSvcClientStateReset(&pClient->State);
567
568 /* (Re-)initialize the client state. */
569 rc = shClSvcClientStateInit(&pClient->State, uClientID);
570
571#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
572 if (RT_SUCCESS(rc))
573 rc = ShClTransferCtxInit(&pClient->TransferCtx);
574#endif
575 }
576 }
577
578 LogFlowFuncLeaveRC(rc);
579 return rc;
580}
581
582/**
583 * Destroys a Shared Clipboard client.
584 *
585 * @param pClient Client to destroy.
586 */
587void shClSvcClientDestroy(PSHCLCLIENT pClient)
588{
589 AssertPtrReturnVoid(pClient);
590
591 LogFlowFunc(("[Client %RU32]\n", pClient->State.uClientID));
592
593 /* Make sure to send a quit message to the guest so that it can terminate gracefully. */
594 if (pClient->Pending.uType)
595 {
596 if (pClient->Pending.cParms >= 2)
597 {
598 HGCMSvcSetU32(&pClient->Pending.paParms[0], VBOX_SHCL_HOST_MSG_QUIT);
599 HGCMSvcSetU32(&pClient->Pending.paParms[1], 0);
600 }
601 g_pHelpers->pfnCallComplete(pClient->Pending.hHandle, VINF_SUCCESS);
602 pClient->Pending.uType = 0;
603 }
604
605 ShClEventSourceDestroy(&pClient->Events);
606
607 shClSvcClientStateDestroy(&pClient->State);
608
609 int rc2 = RTCritSectDelete(&pClient->CritSect);
610 AssertRC(rc2);
611
612 ClipboardClientMap::iterator itClient = g_mapClients.find(pClient->State.uClientID);
613 if (itClient != g_mapClients.end())
614 {
615 g_mapClients.erase(itClient);
616 }
617 else
618 AssertFailed();
619
620 LogFlowFuncLeave();
621}
622
623/**
624 * Resets a Shared Clipboard client.
625 *
626 * @param pClient Client to reset.
627 */
628void shClSvcClientReset(PSHCLCLIENT pClient)
629{
630 if (!pClient)
631 return;
632
633 LogFlowFunc(("[Client %RU32]\n", pClient->State.uClientID));
634
635 /* Reset message queue. */
636 shClSvcMsgQueueReset(pClient);
637
638 /* Reset event source. */
639 ShClEventSourceReset(&pClient->Events);
640
641 /* Reset pending state. */
642 RT_ZERO(pClient->Pending);
643
644#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
645 shClSvcClientTransfersReset(pClient);
646#endif
647
648 shclSvcClientStateReset(&pClient->State);
649}
650
651/**
652 * Implements VBOX_SHCL_GUEST_FN_REPORT_FEATURES.
653 *
654 * @returns VBox status code.
655 * @retval VINF_HGCM_ASYNC_EXECUTE on success (we complete the message here).
656 * @retval VERR_ACCESS_DENIED if not master
657 * @retval VERR_INVALID_PARAMETER if bit 63 in the 2nd parameter isn't set.
658 * @retval VERR_WRONG_PARAMETER_COUNT
659 *
660 * @param pClient The client state.
661 * @param hCall The client's call handle.
662 * @param cParms Number of parameters.
663 * @param paParms Array of parameters.
664 */
665int shClSvcClientReportFeatures(PSHCLCLIENT pClient, VBOXHGCMCALLHANDLE hCall,
666 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
667{
668 /*
669 * Validate the request.
670 */
671 ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
672 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
673 uint64_t const fFeatures0 = paParms[0].u.uint64;
674 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
675 uint64_t const fFeatures1 = paParms[1].u.uint64;
676 ASSERT_GUEST_RETURN(fFeatures1 & VBOX_SHCL_GF_1_MUST_BE_ONE, VERR_INVALID_PARAMETER);
677
678 /*
679 * Do the work.
680 */
681 paParms[0].u.uint64 = g_fHostFeatures0;
682 paParms[1].u.uint64 = 0;
683
684 int rc = g_pHelpers->pfnCallComplete(hCall, VINF_SUCCESS);
685 if (RT_SUCCESS(rc))
686 {
687 pClient->State.fGuestFeatures0 = fFeatures0;
688 pClient->State.fGuestFeatures1 = fFeatures1;
689 Log(("[Client %RU32] features: %#RX64 %#RX64\n", pClient->State.uClientID, fFeatures0, fFeatures1));
690
691 /*
692 * Forward the info to Main.
693 */
694 /** @todo Not needed yet. */
695 }
696 else
697 LogFunc(("pfnCallComplete -> %Rrc\n", rc));
698
699 return VINF_HGCM_ASYNC_EXECUTE;
700}
701
702/**
703 * Implements VBOX_SHCL_GUEST_FN_QUERY_FEATURES.
704 *
705 * @returns VBox status code.
706 * @retval VINF_HGCM_ASYNC_EXECUTE on success (we complete the message here).
707 * @retval VERR_WRONG_PARAMETER_COUNT
708 *
709 * @param hCall The client's call handle.
710 * @param cParms Number of parameters.
711 * @param paParms Array of parameters.
712 */
713int shClSvcClientQueryFeatures(VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
714{
715 /*
716 * Validate the request.
717 */
718 ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
719 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
720 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
721 ASSERT_GUEST(paParms[1].u.uint64 & RT_BIT_64(63));
722
723 /*
724 * Do the work.
725 */
726 paParms[0].u.uint64 = g_fHostFeatures0;
727 paParms[1].u.uint64 = 0;
728 int rc = g_pHelpers->pfnCallComplete(hCall, VINF_SUCCESS);
729 if (RT_FAILURE(rc))
730 LogFunc(("pfnCallComplete -> %Rrc\n", rc));
731
732 return VINF_HGCM_ASYNC_EXECUTE;
733}
734
735/**
736 * Implements VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT and VBOX_SHCL_GUEST_FN_MSG_PEEK_NOWAIT.
737 *
738 * @returns VBox status code.
739 * @retval VINF_SUCCESS if a message was pending and is being returned.
740 * @retval VERR_TRY_AGAIN if no message pending and not blocking.
741 * @retval VERR_RESOURCE_BUSY if another read already made a waiting call.
742 * @retval VINF_HGCM_ASYNC_EXECUTE if message wait is pending.
743 *
744 * @param pClient The client state.
745 * @param hCall The client's call handle.
746 * @param cParms Number of parameters.
747 * @param paParms Array of parameters.
748 * @param fWait Set if we should wait for a message, clear if to return
749 * immediately.
750 */
751int shClSvcMsgPeek(PSHCLCLIENT pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[],
752 bool fWait)
753{
754 /*
755 * Validate the request.
756 */
757 ASSERT_GUEST_MSG_RETURN(cParms >= 2, ("cParms=%u!\n", cParms), VERR_WRONG_PARAMETER_COUNT);
758
759 uint64_t idRestoreCheck = 0;
760 uint32_t i = 0;
761 if (paParms[i].type == VBOX_HGCM_SVC_PARM_64BIT)
762 {
763 idRestoreCheck = paParms[0].u.uint64;
764 paParms[0].u.uint64 = 0;
765 i++;
766 }
767 for (; i < cParms; i++)
768 {
769 ASSERT_GUEST_MSG_RETURN(paParms[i].type == VBOX_HGCM_SVC_PARM_32BIT, ("#%u type=%u\n", i, paParms[i].type),
770 VERR_WRONG_PARAMETER_TYPE);
771 paParms[i].u.uint32 = 0;
772 }
773
774 /*
775 * Check restore session ID.
776 */
777 if (idRestoreCheck != 0)
778 {
779 uint64_t idRestore = g_pHelpers->pfnGetVMMDevSessionId(g_pHelpers);
780 if (idRestoreCheck != idRestore)
781 {
782 paParms[0].u.uint64 = idRestore;
783 LogFlowFunc(("[Client %RU32] VBOX_SHCL_GUEST_FN_MSG_PEEK_XXX -> VERR_VM_RESTORED (%#RX64 -> %#RX64)\n",
784 pClient->State.uClientID, idRestoreCheck, idRestore));
785 return VERR_VM_RESTORED;
786 }
787 Assert(!g_pHelpers->pfnIsCallRestored(hCall));
788 }
789
790 /*
791 * Return information about the first message if one is pending in the list.
792 */
793 if (!pClient->queueMsg.isEmpty())
794 {
795 PSHCLCLIENTMSG pFirstMsg = pClient->queueMsg.first();
796 if (pFirstMsg)
797 {
798 shClSvcMsgSetPeekReturn(pFirstMsg, paParms, cParms);
799 LogFlowFunc(("[Client %RU32] VBOX_SHCL_GUEST_FN_MSG_PEEK_XXX -> VINF_SUCCESS (idMsg=%u (%s), cParms=%u)\n",
800 pClient->State.uClientID, pFirstMsg->uMsg, ShClHostMsgToStr(pFirstMsg->uMsg),
801 pFirstMsg->cParms));
802 return VINF_SUCCESS;
803 }
804 }
805
806 /*
807 * If we cannot wait, fail the call.
808 */
809 if (!fWait)
810 {
811 LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_NOWAIT -> VERR_TRY_AGAIN\n", pClient->State.uClientID));
812 return VERR_TRY_AGAIN;
813 }
814
815 /*
816 * Wait for the host to queue a message for this client.
817 */
818 ASSERT_GUEST_MSG_RETURN(pClient->Pending.uType == 0, ("Already pending! (idClient=%RU32)\n",
819 pClient->State.uClientID), VERR_RESOURCE_BUSY);
820 pClient->Pending.hHandle = hCall;
821 pClient->Pending.cParms = cParms;
822 pClient->Pending.paParms = paParms;
823 pClient->Pending.uType = VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT;
824 LogFlowFunc(("[Client %RU32] Is now in pending mode...\n", pClient->State.uClientID));
825 return VINF_HGCM_ASYNC_EXECUTE;
826}
827
828/**
829 * Implements VBOX_SHCL_GUEST_FN_GET_HOST_MSG_OLD.
830 *
831 * @returns VBox status code.
832 * @retval VINF_SUCCESS if a message was pending and is being returned.
833 * @retval VINF_HGCM_ASYNC_EXECUTE if message wait is pending.
834 *
835 * @param pClient The client state.
836 * @param hCall The client's call handle.
837 * @param cParms Number of parameters.
838 * @param paParms Array of parameters.
839 */
840int shClSvcMsgGetOld(PSHCLCLIENT pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
841{
842 int rc;
843
844 if (cParms != VBOX_SHCL_CPARMS_GET_HOST_MSG_OLD)
845 {
846 rc = VERR_INVALID_PARAMETER;
847 }
848 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* msg */
849 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT) /* formats */
850 {
851 rc = VERR_INVALID_PARAMETER;
852 }
853 else
854 {
855 if (!pClient->queueMsg.isEmpty())
856 {
857 PSHCLCLIENTMSG pFirstMsg = pClient->queueMsg.first();
858 AssertPtr(pFirstMsg);
859
860 LogFlowFunc(("[Client %RU32] uMsg=%RU32 (%s), cParms=%RU32\n",
861 pClient->State.uClientID, pFirstMsg->uMsg, ShClHostMsgToStr(pFirstMsg->uMsg),
862 pFirstMsg->cParms));
863
864 bool fRemove;
865 rc = shClSvcMsgSetGetHostMsgOldReturn(pFirstMsg, paParms, cParms, &fRemove);
866 if (RT_SUCCESS(rc))
867 {
868 AssertPtr(g_pHelpers);
869 rc = g_pHelpers->pfnCallComplete(hCall, rc);
870 if ( rc != VERR_CANCELLED
871 && fRemove)
872 {
873 pClient->queueMsg.removeFirst();
874 shClSvcMsgFree(pFirstMsg);
875
876 rc = VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
877 }
878 }
879 }
880 else
881 {
882 ASSERT_GUEST_MSG_RETURN(pClient->Pending.uType == 0, ("Already pending! (idClient=%RU32)\n",
883 pClient->State.uClientID), VERR_RESOURCE_BUSY);
884
885 pClient->Pending.hHandle = hCall;
886 pClient->Pending.cParms = cParms;
887 pClient->Pending.paParms = paParms;
888 pClient->Pending.uType = VBOX_SHCL_GUEST_FN_GET_HOST_MSG_OLD;
889
890 rc = VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
891
892 LogFlowFunc(("[Client %RU32] Is now in pending mode...\n", pClient->State.uClientID));
893 }
894 }
895
896 LogFlowFunc(("[Client %RU32] rc=%Rrc\n", pClient->State.uClientID, rc));
897 return rc;
898}
899
900/**
901 * Implements VBOX_SHCL_GUEST_FN_MSG_GET.
902 *
903 * @returns VBox status code.
904 * @retval VINF_SUCCESS if message retrieved and removed from the pending queue.
905 * @retval VERR_TRY_AGAIN if no message pending.
906 * @retval VERR_BUFFER_OVERFLOW if a parmeter buffer is too small. The buffer
907 * size was updated to reflect the required size, though this isn't yet
908 * forwarded to the guest. (The guest is better of using peek with
909 * parameter count + 2 parameters to get the sizes.)
910 * @retval VERR_MISMATCH if the incoming message ID does not match the pending.
911 * @retval VINF_HGCM_ASYNC_EXECUTE if message was completed already.
912 *
913 * @param pClient The client state.
914 * @param hCall The client's call handle.
915 * @param cParms Number of parameters.
916 * @param paParms Array of parameters.
917 */
918int shClSvcMsgGet(PSHCLCLIENT pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
919{
920 /*
921 * Validate the request.
922 */
923 uint32_t const idMsgExpected = cParms > 0 && paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT ? paParms[0].u.uint32
924 : cParms > 0 && paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT ? paParms[0].u.uint64
925 : UINT32_MAX;
926
927 /*
928 * Return information about the first message if one is pending in the list.
929 */
930 if (!pClient->queueMsg.isEmpty())
931 {
932 PSHCLCLIENTMSG pFirstMsg = pClient->queueMsg.first();
933 if (pFirstMsg)
934 {
935 LogFlowFunc(("First message is: %RU32 (%s), cParms=%RU32\n",
936 pFirstMsg->uMsg, ShClHostMsgToStr(pFirstMsg->uMsg), pFirstMsg->cParms));
937
938 ASSERT_GUEST_MSG_RETURN(pFirstMsg->uMsg == idMsgExpected || idMsgExpected == UINT32_MAX,
939 ("idMsg=%u (%s) cParms=%u, caller expected %u (%s) and %u\n",
940 pFirstMsg->uMsg, ShClHostMsgToStr(pFirstMsg->uMsg), pFirstMsg->cParms,
941 idMsgExpected, ShClHostMsgToStr(idMsgExpected), cParms),
942 VERR_MISMATCH);
943 ASSERT_GUEST_MSG_RETURN(pFirstMsg->cParms == cParms,
944 ("idMsg=%u (%s) cParms=%u, caller expected %u (%s) and %u\n",
945 pFirstMsg->uMsg, ShClHostMsgToStr(pFirstMsg->uMsg), pFirstMsg->cParms,
946 idMsgExpected, ShClHostMsgToStr(idMsgExpected), cParms),
947 VERR_WRONG_PARAMETER_COUNT);
948
949 /* Check the parameter types. */
950 for (uint32_t i = 0; i < cParms; i++)
951 ASSERT_GUEST_MSG_RETURN(pFirstMsg->paParms[i].type == paParms[i].type,
952 ("param #%u: type %u, caller expected %u (idMsg=%u %s)\n", i, pFirstMsg->paParms[i].type,
953 paParms[i].type, pFirstMsg->uMsg, ShClHostMsgToStr(pFirstMsg->uMsg)),
954 VERR_WRONG_PARAMETER_TYPE);
955 /*
956 * Copy out the parameters.
957 *
958 * No assertions on buffer overflows, and keep going till the end so we can
959 * communicate all the required buffer sizes.
960 */
961 int rc = VINF_SUCCESS;
962 for (uint32_t i = 0; i < cParms; i++)
963 switch (pFirstMsg->paParms[i].type)
964 {
965 case VBOX_HGCM_SVC_PARM_32BIT:
966 paParms[i].u.uint32 = pFirstMsg->paParms[i].u.uint32;
967 break;
968
969 case VBOX_HGCM_SVC_PARM_64BIT:
970 paParms[i].u.uint64 = pFirstMsg->paParms[i].u.uint64;
971 break;
972
973 case VBOX_HGCM_SVC_PARM_PTR:
974 {
975 uint32_t const cbSrc = pFirstMsg->paParms[i].u.pointer.size;
976 uint32_t const cbDst = paParms[i].u.pointer.size;
977 paParms[i].u.pointer.size = cbSrc; /** @todo Check if this is safe in other layers...
978 * Update: Safe, yes, but VMMDevHGCM doesn't pass it along. */
979 if (cbSrc <= cbDst)
980 memcpy(paParms[i].u.pointer.addr, pFirstMsg->paParms[i].u.pointer.addr, cbSrc);
981 else
982 {
983 AssertMsgFailed(("#%u: cbSrc=%RU32 is bigger than cbDst=%RU32\n", i, cbSrc, cbDst));
984 rc = VERR_BUFFER_OVERFLOW;
985 }
986 break;
987 }
988
989 default:
990 AssertMsgFailed(("#%u: %u\n", i, pFirstMsg->paParms[i].type));
991 rc = VERR_INTERNAL_ERROR;
992 break;
993 }
994 if (RT_SUCCESS(rc))
995 {
996 /*
997 * Complete the message and remove the pending message unless the
998 * guest raced us and cancelled this call in the meantime.
999 */
1000 AssertPtr(g_pHelpers);
1001 rc = g_pHelpers->pfnCallComplete(hCall, rc);
1002
1003 LogFlowFunc(("[Client %RU32] pfnCallComplete -> %Rrc\n", pClient->State.uClientID, rc));
1004
1005 if (rc != VERR_CANCELLED)
1006 {
1007 pClient->queueMsg.removeFirst();
1008 shClSvcMsgFree(pFirstMsg);
1009 }
1010
1011 return VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
1012 }
1013
1014 LogFlowFunc(("[Client %RU32] Returning %Rrc\n", pClient->State.uClientID, rc));
1015 return rc;
1016 }
1017 }
1018
1019 paParms[0].u.uint32 = 0;
1020 paParms[1].u.uint32 = 0;
1021 LogFlowFunc(("[Client %RU32] -> VERR_TRY_AGAIN\n", pClient->State.uClientID));
1022 return VERR_TRY_AGAIN;
1023}
1024
1025/**
1026 * Wakes up a pending client (i.e. waiting for new messages).
1027 *
1028 * @returns VBox status code.
1029 * @retval VINF_NO_CHANGE if the client is not in pending mode.
1030 *
1031 * @param pClient Client to wake up.
1032 */
1033int shClSvcClientWakeup(PSHCLCLIENT pClient)
1034{
1035 int rc = VINF_NO_CHANGE;
1036
1037 if (pClient->Pending.uType)
1038 {
1039 LogFunc(("[Client %RU32] Waking up ...\n", pClient->State.uClientID));
1040
1041 rc = VINF_SUCCESS;
1042
1043 if (!pClient->queueMsg.isEmpty())
1044 {
1045 PSHCLCLIENTMSG pFirstMsg = pClient->queueMsg.first();
1046 if (pFirstMsg)
1047 {
1048 LogFunc(("[Client %RU32] Current host message is %RU32 (%s), cParms=%RU32\n",
1049 pClient->State.uClientID, pFirstMsg->uMsg, ShClHostMsgToStr(pFirstMsg->uMsg),
1050 pFirstMsg->cParms));
1051
1052 bool fDonePending = false;
1053
1054 if (pClient->Pending.uType == VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT)
1055 {
1056 shClSvcMsgSetPeekReturn(pFirstMsg, pClient->Pending.paParms, pClient->Pending.cParms);
1057 fDonePending = true;
1058 }
1059 else if (pClient->Pending.uType == VBOX_SHCL_GUEST_FN_GET_HOST_MSG_OLD) /* Legacy, Guest Additions < 6.1. */
1060 {
1061 bool fRemove;
1062 rc = shClSvcMsgSetGetHostMsgOldReturn(pFirstMsg, pClient->Pending.paParms, pClient->Pending.cParms,
1063 &fRemove);
1064 if (RT_SUCCESS(rc))
1065 {
1066 if (fRemove)
1067 {
1068 /* The old (legacy) protocol gets the message right when returning from peeking, so
1069 * remove the actual message from our queue right now. */
1070 pClient->queueMsg.removeFirst();
1071 shClSvcMsgFree(pFirstMsg);
1072 }
1073
1074 fDonePending = true;
1075 }
1076 }
1077
1078 if (fDonePending)
1079 {
1080 rc = g_pHelpers->pfnCallComplete(pClient->Pending.hHandle, VINF_SUCCESS);
1081
1082 pClient->Pending.hHandle = NULL;
1083 pClient->Pending.paParms = NULL;
1084 pClient->Pending.cParms = 0;
1085 pClient->Pending.uType = 0;
1086 }
1087 }
1088 else
1089 AssertFailed();
1090 }
1091 else
1092 AssertMsgFailed(("Waking up client ID=%RU32 with no host message in queue is a bad idea\n", pClient->State.uClientID));
1093
1094 return rc;
1095 }
1096 else
1097 LogFunc(("[Client %RU32] Not in pending state, skipping wakeup\n", pClient->State.uClientID));
1098
1099 return VINF_NO_CHANGE;
1100}
1101
1102/**
1103 * Requests to read clipboard data from the guest.
1104 *
1105 * @returns VBox status code.
1106 * @param pClient Client to request to read data form.
1107 * @param pDataReq Data request to send to the guest.
1108 * @param puEvent Event ID for waiting for new data. Optional.
1109 */
1110int ShClSvcDataReadRequest(PSHCLCLIENT pClient, PSHCLDATAREQ pDataReq,
1111 PSHCLEVENTID puEvent)
1112{
1113 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
1114 AssertPtrReturn(pDataReq, VERR_INVALID_POINTER);
1115 /* puEvent is optional. */
1116
1117 LogFlowFuncEnter();
1118
1119 int rc;
1120
1121 PSHCLCLIENTMSG pMsgReadData = shClSvcMsgAlloc(VBOX_SHCL_HOST_MSG_READ_DATA,
1122 VBOX_SHCL_CPARMS_READ_DATA_REQ);
1123 if (pMsgReadData)
1124 {
1125 const SHCLEVENTID uEvent = ShClEventIDGenerate(&pClient->Events);
1126
1127 LogFlowFunc(("uFmt=0x%x\n", pDataReq->uFmt));
1128
1129 HGCMSvcSetU64(&pMsgReadData->paParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
1130 pClient->Events.uID, uEvent));
1131 HGCMSvcSetU32(&pMsgReadData->paParms[1], 0 /* fFlags */);
1132 HGCMSvcSetU32(&pMsgReadData->paParms[2], pDataReq->uFmt);
1133 HGCMSvcSetU32(&pMsgReadData->paParms[3], pClient->State.cbChunkSize);
1134
1135 rc = shClSvcMsgAdd(pClient, pMsgReadData, true /* fAppend */);
1136 if (RT_SUCCESS(rc))
1137 {
1138 rc = ShClEventRegister(&pClient->Events, uEvent);
1139 if (RT_SUCCESS(rc))
1140 {
1141 rc = shClSvcClientWakeup(pClient);
1142 if (RT_SUCCESS(rc))
1143 {
1144 if (puEvent)
1145 *puEvent = uEvent;
1146 }
1147 else
1148 ShClEventUnregister(&pClient->Events, uEvent);
1149 }
1150 }
1151 }
1152 else
1153 rc = VERR_NO_MEMORY;
1154
1155 LogFlowFuncLeaveRC(rc);
1156 return rc;
1157}
1158
1159int ShClSvcDataReadSignal(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
1160 PSHCLDATABLOCK pData)
1161{
1162 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
1163 AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER);
1164 AssertPtrReturn(pData, VERR_INVALID_POINTER);
1165
1166 LogFlowFuncEnter();
1167
1168 SHCLEVENTID uEvent;
1169 if (!(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID)) /* Legacy, Guest Additions < 6.1. */
1170 {
1171 /* Older Guest Additions (<= VBox 6.0) did not have any context ID handling, so we ASSUME that the last event registered
1172 * is the one we want to handle (as this all was a synchronous protocol anyway). */
1173 uEvent = ShClEventGetLast(&pClient->Events);
1174 }
1175 else
1176 uEvent = VBOX_SHCL_CONTEXTID_GET_EVENT(pCmdCtx->uContextID);
1177
1178 int rc = VINF_SUCCESS;
1179
1180 PSHCLEVENTPAYLOAD pPayload = NULL;
1181 if (pData->cbData)
1182 rc = ShClPayloadAlloc(uEvent, pData->pvData, pData->cbData, &pPayload);
1183
1184 if (RT_SUCCESS(rc))
1185 {
1186 rc = ShClEventSignal(&pClient->Events, uEvent, pPayload);
1187 if (RT_FAILURE(rc))
1188 ShClPayloadFree(pPayload);
1189 }
1190
1191 LogFlowFuncLeaveRC(rc);
1192 return rc;
1193}
1194
1195/**
1196 * Reports available VBox clipboard formats to the guest.
1197 *
1198 * @returns VBox status code.
1199 * @param pClient Client to request to read data form.
1200 * @param pFormats Formats to report.
1201 */
1202int ShClSvcFormatsReport(PSHCLCLIENT pClient, PSHCLFORMATDATA pFormats)
1203{
1204 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
1205 AssertPtrReturn(pFormats, VERR_INVALID_POINTER);
1206
1207 LogFlowFuncEnter();
1208
1209 uint32_t fFormats = pFormats->Formats;
1210 uint32_t fFlags = pFormats->fFlags;
1211
1212#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1213 /* If transfer mode is set to disabled, don't report the URI list format to the guest. */
1214 if (!(g_fTransferMode & VBOX_SHCL_TRANSFER_MODE_ENABLED))
1215 fFormats &= ~VBOX_SHCL_FMT_URI_LIST;
1216#endif
1217
1218 LogFlowFunc(("fFormats=0x%x -> 0x%x\n", pFormats->Formats, fFormats));
1219
1220 /* Nothing to report? Bail out early. */
1221 if (fFormats == VBOX_SHCL_FMT_NONE)
1222 return VINF_SUCCESS;
1223
1224 LogRel2(("Shared Clipboard: Reporting formats 0x%x to guest\n", fFormats));
1225
1226 int rc;
1227
1228 PSHCLCLIENTMSG pMsg = shClSvcMsgAlloc(VBOX_SHCL_HOST_MSG_FORMATS_REPORT, 3);
1229 if (pMsg)
1230 {
1231 const SHCLEVENTID uEvent = ShClEventIDGenerate(&pClient->Events);
1232
1233 HGCMSvcSetU64(&pMsg->paParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
1234 pClient->Events.uID, uEvent));
1235 HGCMSvcSetU32(&pMsg->paParms[1], fFormats);
1236 HGCMSvcSetU32(&pMsg->paParms[2], fFlags);
1237
1238 rc = shClSvcMsgAdd(pClient, pMsg, true /* fAppend */);
1239 if (RT_SUCCESS(rc))
1240 {
1241#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1242 /* If we announce an URI list, create a transfer locally and also tell the guest to create
1243 * a transfer on the guest side. */
1244 if (fFormats & VBOX_SHCL_FMT_URI_LIST)
1245 {
1246 rc = shClSvcTransferStart(pClient, SHCLTRANSFERDIR_WRITE, SHCLSOURCE_LOCAL,
1247 NULL /* pTransfer */);
1248 if (RT_FAILURE(rc))
1249 LogRel(("Shared Clipboard: Initializing host write transfer failed with %Rrc\n", rc));
1250 }
1251 else
1252 {
1253#endif
1254 pClient->State.fFlags |= SHCLCLIENTSTATE_FLAGS_READ_ACTIVE;
1255
1256 rc = shClSvcClientWakeup(pClient);
1257#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1258 }
1259#endif
1260 }
1261 }
1262 else
1263 rc = VERR_NO_MEMORY;
1264
1265 LogFlowFuncLeaveRC(rc);
1266 return rc;
1267}
1268
1269int shClSvcGetDataRead(PSHCLCLIENT pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1270{
1271 LogFlowFuncEnter();
1272
1273 if ( ShClSvcGetMode() != VBOX_SHCL_MODE_HOST_TO_GUEST
1274 && ShClSvcGetMode() != VBOX_SHCL_MODE_BIDIRECTIONAL)
1275 {
1276 return VERR_ACCESS_DENIED;
1277 }
1278
1279 /* Is the guest supposed to read any clipboard data from the host? */
1280 if (!(pClient->State.fFlags & SHCLCLIENTSTATE_FLAGS_READ_ACTIVE))
1281 return VERR_WRONG_ORDER;
1282
1283 int rc;
1284
1285 SHCLCLIENTCMDCTX cmdCtx;
1286 RT_ZERO(cmdCtx);
1287
1288 SHCLDATABLOCK dataBlock;
1289 RT_ZERO(dataBlock);
1290
1291 if (!(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID)) /* Legacy, Guest Additions < 6.1. */
1292 {
1293 if (cParms != 3)
1294 {
1295 rc = VERR_INVALID_PARAMETER;
1296 }
1297 else
1298 {
1299 rc = HGCMSvcGetU32(&paParms[0], &dataBlock.uFormat);
1300 if (RT_SUCCESS(rc))
1301 {
1302 if (pClient->State.POD.uFormat == VBOX_SHCL_FMT_NONE)
1303 pClient->State.POD.uFormat = dataBlock.uFormat;
1304
1305 if (dataBlock.uFormat != pClient->State.POD.uFormat)
1306 {
1307 LogFlowFunc(("Invalid format (pClient->State.POD.uFormat=%RU32 vs dataBlock.uFormat=%RU32)\n",
1308 pClient->State.POD.uFormat, dataBlock.uFormat));
1309
1310 rc = VERR_INVALID_PARAMETER;
1311 }
1312 else
1313 {
1314 rc = HGCMSvcGetBuf(&paParms[1], &dataBlock.pvData, &dataBlock.cbData);
1315 }
1316 }
1317 }
1318 }
1319 else
1320 {
1321 if (cParms < VBOX_SHCL_CPARMS_READ_DATA)
1322 {
1323 rc = VERR_INVALID_PARAMETER;
1324 }
1325 else
1326 {
1327 /** @todo Handle paParms[1] flags. */
1328
1329 rc = HGCMSvcGetU32(&paParms[2], &dataBlock.uFormat);
1330 if (RT_SUCCESS(rc))
1331 {
1332 uint32_t cbData;
1333 rc = HGCMSvcGetU32(&paParms[3], &cbData);
1334 if (RT_SUCCESS(rc))
1335 {
1336 rc = HGCMSvcGetBuf(&paParms[4], &dataBlock.pvData, &dataBlock.cbData);
1337 if (RT_SUCCESS(rc))
1338 {
1339 if (cbData != dataBlock.cbData)
1340 {
1341 LogFlowFunc(("Invalid data (cbData=%RU32 vs dataBlock.cbData=%RU32)\n", cbData, dataBlock.cbData));
1342 rc = VERR_INVALID_PARAMETER;
1343 }
1344 }
1345 }
1346 }
1347 }
1348 }
1349
1350 if (RT_SUCCESS(rc))
1351 {
1352 uint32_t cbActual = 0;
1353
1354 /* If there is a service extension active, try reading data from it first. */
1355 if (g_ExtState.pfnExtension)
1356 {
1357 SHCLEXTPARMS parms;
1358 RT_ZERO(parms);
1359
1360 parms.uFormat = pClient->State.POD.uFormat;
1361 parms.u.pvData = dataBlock.pvData;
1362 parms.cbData = dataBlock.cbData;
1363
1364 g_ExtState.fReadingData = true;
1365
1366 /* Read clipboard data from the extension. */
1367 rc = g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_READ,
1368 &parms, sizeof(parms));
1369
1370 LogFlowFunc(("g_ExtState.fDelayedAnnouncement=%RTbool, g_ExtState.uDelayedFormats=0x%x\n",
1371 g_ExtState.fDelayedAnnouncement, g_ExtState.uDelayedFormats));
1372
1373 /* Did the extension send the clipboard formats yet?
1374 * Otherwise, do this now. */
1375 if (g_ExtState.fDelayedAnnouncement)
1376 {
1377 SHCLFORMATDATA formatData;
1378 RT_ZERO(formatData);
1379
1380 formatData.Formats = g_ExtState.uDelayedFormats;
1381 Assert(formatData.Formats != VBOX_SHCL_FMT_NONE); /* There better is *any* format here now. */
1382
1383 int rc2 = ShClSvcFormatsReport(pClient, &formatData);
1384 AssertRC(rc2);
1385
1386 g_ExtState.fDelayedAnnouncement = false;
1387 g_ExtState.uDelayedFormats = 0;
1388 }
1389
1390 g_ExtState.fReadingData = false;
1391
1392 if (RT_SUCCESS(rc))
1393 {
1394 cbActual = parms.cbData;
1395 }
1396 }
1397
1398 /* Note: The host clipboard *always* has precedence over the service extension above,
1399 * so data which has been read above might get overridden by the host clipboard eventually. */
1400
1401 if (RT_SUCCESS(rc))
1402 {
1403 rc = ShClSvcImplReadData(pClient, &cmdCtx, &dataBlock, &cbActual);
1404 if (RT_SUCCESS(rc))
1405 {
1406 LogFlowFunc(("cbData=%RU32, cbActual=%RU32\n", dataBlock.cbData, cbActual));
1407
1408 /* Return the actual size required to fullfil the request. */
1409 if (!(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID)) /* Legacy, Guest Additions < 6.1. */
1410 {
1411 HGCMSvcSetU32(&paParms[2], cbActual);
1412 }
1413 else
1414 {
1415 HGCMSvcSetU32(&paParms[3], cbActual);
1416 }
1417
1418 /* If the data to return exceeds the buffer the guest supplies, tell it (and let it try again). */
1419 if (cbActual >= dataBlock.cbData)
1420 rc = VINF_BUFFER_OVERFLOW;
1421
1422 if (rc == VINF_SUCCESS)
1423 {
1424 /* Only remove "read active" flag after successful read again. */
1425 pClient->State.fFlags &= ~SHCLCLIENTSTATE_FLAGS_READ_ACTIVE;
1426 }
1427 }
1428 }
1429 }
1430
1431 LogFlowFuncLeaveRC(rc);
1432 return rc;
1433}
1434
1435int shClSvcGetDataWrite(PSHCLCLIENT pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1436{
1437 LogFlowFuncEnter();
1438
1439 if ( ShClSvcGetMode() != VBOX_SHCL_MODE_GUEST_TO_HOST
1440 && ShClSvcGetMode() != VBOX_SHCL_MODE_BIDIRECTIONAL)
1441 {
1442 return VERR_ACCESS_DENIED;
1443 }
1444
1445 /* Is the guest supposed to write any clipboard data from the host? */
1446 if (!(pClient->State.fFlags & SHCLCLIENTSTATE_FLAGS_WRITE_ACTIVE))
1447 return VERR_WRONG_ORDER;
1448
1449 int rc;
1450
1451 SHCLDATABLOCK dataBlock;
1452 RT_ZERO(dataBlock);
1453
1454 SHCLCLIENTCMDCTX cmdCtx;
1455 RT_ZERO(cmdCtx);
1456
1457 if (!(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID)) /* Legacy, Guest Additions < 6.1. */
1458 {
1459 if (cParms != 2)
1460 {
1461 rc = VERR_INVALID_PARAMETER;
1462 }
1463 else
1464 {
1465 rc = HGCMSvcGetU32(&paParms[0], &dataBlock.uFormat);
1466 if (RT_SUCCESS(rc))
1467 {
1468 if (pClient->State.POD.uFormat == VBOX_SHCL_FMT_NONE)
1469 pClient->State.POD.uFormat = dataBlock.uFormat;
1470
1471 if ( dataBlock.uFormat == VBOX_SHCL_FMT_NONE
1472 || dataBlock.uFormat != pClient->State.POD.uFormat)
1473 {
1474 LogFunc(("Invalid format (client=%RU32 vs host=%RU32)\n", dataBlock.uFormat, pClient->State.POD.uFormat));
1475 rc = VERR_INVALID_PARAMETER;
1476 }
1477 else
1478 rc = HGCMSvcGetBuf(&paParms[1], &dataBlock.pvData, &dataBlock.cbData);
1479 }
1480 }
1481 }
1482 else
1483 {
1484 if (cParms != VBOX_SHCL_CPARMS_WRITE_DATA)
1485 {
1486 rc = VERR_INVALID_PARAMETER;
1487 }
1488 else
1489 {
1490 rc = HGCMSvcGetU64(&paParms[0], &cmdCtx.uContextID);
1491
1492 /** @todo Handle paParms[1] flags. */
1493
1494 if (RT_SUCCESS(rc))
1495 rc = HGCMSvcGetU32(&paParms[2], &dataBlock.uFormat);
1496 if (RT_SUCCESS(rc))
1497 rc = HGCMSvcGetBuf(&paParms[4], &dataBlock.pvData, &dataBlock.cbData);
1498 }
1499 }
1500
1501 if (RT_SUCCESS(rc))
1502 {
1503 if (g_ExtState.pfnExtension)
1504 {
1505 SHCLEXTPARMS parms;
1506 RT_ZERO(parms);
1507
1508 parms.uFormat = dataBlock.uFormat;
1509 parms.u.pvData = dataBlock.pvData;
1510 parms.cbData = dataBlock.cbData;
1511
1512 g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_WRITE, &parms, sizeof(parms));
1513 }
1514
1515 rc = ShClSvcImplWriteData(pClient, &cmdCtx, &dataBlock);
1516 if (RT_SUCCESS(rc))
1517 {
1518 /* Remove "write active" flag after successful read again. */
1519 pClient->State.fFlags &= ~SHCLCLIENTSTATE_FLAGS_WRITE_ACTIVE;
1520 }
1521 }
1522
1523 LogFlowFuncLeaveRC(rc);
1524 return rc;
1525}
1526
1527/**
1528 * Gets an error from HGCM service parameters.
1529 *
1530 * @returns VBox status code.
1531 * @param cParms Number of HGCM parameters supplied in \a paParms.
1532 * @param paParms Array of HGCM parameters.
1533 * @param pRc Where to store the received error code.
1534 */
1535static int shClSvcGetError(uint32_t cParms, VBOXHGCMSVCPARM paParms[], int *pRc)
1536{
1537 AssertPtrReturn(paParms, VERR_INVALID_PARAMETER);
1538 AssertPtrReturn(pRc, VERR_INVALID_PARAMETER);
1539
1540 int rc;
1541
1542 if (cParms == VBOX_SHCL_CPARMS_ERROR)
1543 {
1544 rc = HGCMSvcGetU32(&paParms[1], (uint32_t *)pRc); /** @todo int vs. uint32_t !!! */
1545 }
1546 else
1547 rc = VERR_INVALID_PARAMETER;
1548
1549 LogFlowFuncLeaveRC(rc);
1550 return rc;
1551}
1552
1553int shClSvcSetSource(PSHCLCLIENT pClient, SHCLSOURCE enmSource)
1554{
1555 if (!pClient) /* If no client connected (anymore), bail out. */
1556 return VINF_SUCCESS;
1557
1558 int rc = VINF_SUCCESS;
1559
1560 if (ShClSvcLock())
1561 {
1562 pClient->State.enmSource = enmSource;
1563
1564 LogFlowFunc(("Source of client %RU32 is now %RU32\n", pClient->State.uClientID, pClient->State.enmSource));
1565
1566 ShClSvcUnlock();
1567 }
1568
1569 LogFlowFuncLeaveRC(rc);
1570 return rc;
1571}
1572
1573static int svcInit(void)
1574{
1575 int rc = RTCritSectInit(&g_CritSect);
1576
1577 if (RT_SUCCESS(rc))
1578 {
1579 shClSvcModeSet(VBOX_SHCL_MODE_OFF);
1580
1581 rc = ShClSvcImplInit();
1582
1583 /* Clean up on failure, because 'svnUnload' will not be called
1584 * if the 'svcInit' returns an error.
1585 */
1586 if (RT_FAILURE(rc))
1587 {
1588 RTCritSectDelete(&g_CritSect);
1589 }
1590 }
1591
1592 return rc;
1593}
1594
1595static DECLCALLBACK(int) svcUnload(void *)
1596{
1597 LogFlowFuncEnter();
1598
1599 ShClSvcImplDestroy();
1600
1601 RTCritSectDelete(&g_CritSect);
1602
1603 return VINF_SUCCESS;
1604}
1605
1606static DECLCALLBACK(int) svcDisconnect(void *, uint32_t u32ClientID, void *pvClient)
1607{
1608 RT_NOREF(u32ClientID);
1609
1610 LogFunc(("u32ClientID=%RU32\n", u32ClientID));
1611
1612 PSHCLCLIENT pClient = (PSHCLCLIENT)pvClient;
1613 AssertPtr(pClient);
1614
1615#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1616 shClSvcClientTransfersReset(pClient);
1617#endif
1618
1619 ShClSvcImplDisconnect(pClient);
1620
1621 shClSvcClientDestroy(pClient);
1622
1623 return VINF_SUCCESS;
1624}
1625
1626static DECLCALLBACK(int) svcConnect(void *, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor, bool fRestoring)
1627{
1628 RT_NOREF(fRequestor, fRestoring);
1629
1630 PSHCLCLIENT pClient = (PSHCLCLIENT)pvClient;
1631 AssertPtr(pvClient);
1632
1633 int rc = shClSvcClientInit(pClient, u32ClientID);
1634 if (RT_SUCCESS(rc))
1635 {
1636 rc = ShClSvcImplConnect(pClient, ShClSvcGetHeadless());
1637 if (RT_SUCCESS(rc))
1638 {
1639 /* Sync the host clipboard content with the client. */
1640 rc = ShClSvcImplSync(pClient);
1641 if (rc == VINF_NO_CHANGE)
1642 {
1643 /*
1644 * The sync could return VINF_NO_CHANGE if nothing has changed on the host, but older
1645 * Guest Additions rely on the fact that only VINF_SUCCESS indicates a successful connect
1646 * to the host service (instead of using RT_SUCCESS()).
1647 *
1648 * So implicitly set VINF_SUCCESS here to not break older Guest Additions.
1649 */
1650 rc = VINF_SUCCESS;
1651 }
1652
1653 if (RT_SUCCESS(rc))
1654 {
1655 /* Assign weak pointer to client map .*/
1656 g_mapClients[u32ClientID] = pClient; /** @todo Handle OOM / collisions? */
1657
1658 /* For now we ASSUME that the first client ever connected is in charge for
1659 * communicating withe the service extension.
1660 *
1661 ** @todo This needs to be fixed ASAP w/o breaking older guest / host combos. */
1662 if (g_ExtState.uClientID == 0)
1663 g_ExtState.uClientID = u32ClientID;
1664 }
1665 }
1666 }
1667
1668 LogFlowFuncLeaveRC(rc);
1669 return rc;
1670}
1671
1672static DECLCALLBACK(void) svcCall(void *,
1673 VBOXHGCMCALLHANDLE callHandle,
1674 uint32_t u32ClientID,
1675 void *pvClient,
1676 uint32_t u32Function,
1677 uint32_t cParms,
1678 VBOXHGCMSVCPARM paParms[],
1679 uint64_t tsArrival)
1680{
1681 RT_NOREF(u32ClientID, pvClient, tsArrival);
1682
1683 int rc = VINF_SUCCESS;
1684
1685 PSHCLCLIENT pClient = (PSHCLCLIENT)pvClient;
1686 AssertPtr(pClient);
1687
1688 LogFunc(("u32ClientID=%RU32, fn=%RU32 (%s), cParms=%RU32, paParms=%p\n",
1689 u32ClientID, u32Function, ShClGuestMsgToStr(u32Function), cParms, paParms));
1690
1691#ifdef DEBUG
1692 uint32_t i;
1693
1694 for (i = 0; i < cParms; i++)
1695 {
1696 /** @todo parameters other than 32 bit */
1697 LogFunc((" paParms[%d]: type %RU32 - value %RU32\n", i, paParms[i].type, paParms[i].u.uint32));
1698 }
1699#endif
1700
1701 LogFunc(("Client state: fFlags=0x%x, fGuestFeatures0=0x%x, fGuestFeatures1=0x%x\n",
1702 pClient->State.fFlags, pClient->State.fGuestFeatures0, pClient->State.fGuestFeatures1));
1703
1704 switch (u32Function)
1705 {
1706 case VBOX_SHCL_GUEST_FN_GET_HOST_MSG_OLD:
1707 {
1708 rc = shClSvcMsgGetOld(pClient, callHandle, cParms, paParms);
1709 break;
1710 }
1711
1712 case VBOX_SHCL_GUEST_FN_CONNECT:
1713 {
1714 if (cParms != VBOX_SHCL_CPARMS_CONNECT)
1715 {
1716 rc = VERR_INVALID_PARAMETER;
1717 }
1718 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* cbChunkSize */
1719 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* enmCompression */
1720 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT) /* enmChecksumType */
1721 {
1722 rc = VERR_INVALID_PARAMETER;
1723 }
1724 else if (ShClSvcGetMode() == VBOX_SHCL_MODE_OFF)
1725 {
1726 rc = VERR_ACCESS_DENIED;
1727 }
1728 else
1729 {
1730 /* Report back supported chunk size to the guest. */
1731 HGCMSvcSetU32(&paParms[0], _64K); /* Chunk size */ /** @todo Make chunk size dynamic. */
1732
1733 rc = VINF_SUCCESS;
1734 }
1735
1736 break;
1737 }
1738
1739 case VBOX_SHCL_GUEST_FN_REPORT_FEATURES:
1740 {
1741 rc = shClSvcClientReportFeatures(pClient, callHandle, cParms, paParms);
1742 break;
1743 }
1744
1745 case VBOX_SHCL_GUEST_FN_QUERY_FEATURES:
1746 {
1747 rc = shClSvcClientQueryFeatures(callHandle, cParms, paParms);
1748 break;
1749 }
1750
1751 case VBOX_SHCL_GUEST_FN_MSG_PEEK_NOWAIT:
1752 {
1753 rc = shClSvcMsgPeek(pClient, callHandle, cParms, paParms, false /*fWait*/);
1754 break;
1755 }
1756
1757 case VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT:
1758 {
1759 rc = shClSvcMsgPeek(pClient, callHandle, cParms, paParms, true /*fWait*/);
1760 break;
1761 }
1762
1763 case VBOX_SHCL_GUEST_FN_MSG_GET:
1764 {
1765 rc = shClSvcMsgGet(pClient, callHandle, cParms, paParms);
1766 break;
1767 }
1768
1769 case VBOX_SHCL_GUEST_FN_FORMATS_REPORT:
1770 {
1771 uint32_t uFormats = 0;
1772
1773 if (!(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID)) /* Legacy, Guest Additions < 6.1. */
1774 {
1775 if (cParms != 1)
1776 {
1777 rc = VERR_INVALID_PARAMETER;
1778 }
1779 else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT) /* uFormats */
1780 {
1781 rc = VERR_INVALID_PARAMETER;
1782 }
1783 else
1784 {
1785 rc = HGCMSvcGetU32(&paParms[0], &uFormats);
1786 }
1787 }
1788 else
1789 {
1790 if (cParms != 3)
1791 {
1792 rc = VERR_INVALID_PARAMETER;
1793 }
1794 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_64BIT /* uContextID */
1795 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* uFormats */
1796 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT) /* fFlags */
1797 {
1798 rc = VERR_INVALID_PARAMETER;
1799 }
1800 else
1801 {
1802 rc = HGCMSvcGetU32(&paParms[1], &uFormats);
1803
1804 /** @todo Handle rest. */
1805 }
1806 }
1807
1808 if (RT_SUCCESS(rc))
1809 {
1810 if ( ShClSvcGetMode() != VBOX_SHCL_MODE_GUEST_TO_HOST
1811 && ShClSvcGetMode() != VBOX_SHCL_MODE_BIDIRECTIONAL)
1812 {
1813 rc = VERR_ACCESS_DENIED;
1814 }
1815 else if (uFormats != VBOX_SHCL_FMT_NONE) /* Only announce formats if we actually *have* formats to announce! */
1816 {
1817 rc = shClSvcSetSource(pClient, SHCLSOURCE_REMOTE);
1818 if (RT_SUCCESS(rc))
1819 {
1820 if (g_ExtState.pfnExtension)
1821 {
1822 SHCLEXTPARMS parms;
1823 RT_ZERO(parms);
1824
1825 parms.uFormat = uFormats;
1826
1827 g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE,
1828 &parms, sizeof(parms));
1829 }
1830
1831 SHCLCLIENTCMDCTX cmdCtx;
1832 RT_ZERO(cmdCtx);
1833
1834 SHCLFORMATDATA formatData;
1835 RT_ZERO(formatData);
1836
1837 formatData.Formats = uFormats;
1838 Assert(formatData.Formats != VBOX_SHCL_FMT_NONE); /* Sanity. */
1839
1840 rc = ShClSvcImplFormatAnnounce(pClient, &cmdCtx, &formatData);
1841 if (RT_SUCCESS(rc))
1842 {
1843 pClient->State.fFlags |= SHCLCLIENTSTATE_FLAGS_WRITE_ACTIVE;
1844 }
1845 }
1846 }
1847 }
1848
1849 break;
1850 }
1851
1852 case VBOX_SHCL_GUEST_FN_DATA_READ:
1853 {
1854 rc = shClSvcGetDataRead(pClient, cParms, paParms);
1855 break;
1856 }
1857
1858 case VBOX_SHCL_GUEST_FN_DATA_WRITE:
1859 {
1860 rc = shClSvcGetDataWrite(pClient, cParms, paParms);
1861 break;
1862 }
1863
1864 case VBOX_SHCL_GUEST_FN_CANCEL:
1865 {
1866 LogRel2(("Shared Clipboard: Operation canceled by guest side\n"));
1867
1868 /* Reset client state and start over. */
1869 shclSvcClientStateReset(&pClient->State);
1870#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1871 shClSvcClientTransfersReset(pClient);
1872#endif
1873 /** @todo Do we need to do anything else here? */
1874 break;
1875 }
1876
1877 case VBOX_SHCL_GUEST_FN_ERROR:
1878 {
1879 int rcGuest;
1880 rc = shClSvcGetError(cParms,paParms, &rcGuest);
1881 if (RT_SUCCESS(rc))
1882 {
1883 LogRel(("Shared Clipboard: Error from guest side: %Rrc\n", rcGuest));
1884
1885 /* Reset client state and start over. */
1886 shclSvcClientStateReset(&pClient->State);
1887#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1888 shClSvcClientTransfersReset(pClient);
1889#endif
1890 }
1891 break;
1892 }
1893
1894 default:
1895 {
1896#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1897 if (g_fTransferMode & VBOX_SHCL_TRANSFER_MODE_ENABLED)
1898 {
1899 rc = shClSvcTransferHandler(pClient, callHandle, u32Function, cParms, paParms, tsArrival);
1900 }
1901 else
1902 {
1903 LogRel2(("Shared Clipboard: File transfers are disabled for this VM\n"));
1904 rc = VERR_ACCESS_DENIED;
1905 }
1906#else
1907 rc = VERR_NOT_SUPPORTED;
1908#endif
1909 break;
1910 }
1911 }
1912
1913 LogFlowFunc(("[Client %RU32] rc=%Rrc\n", pClient->State.uClientID, rc));
1914
1915 if (rc != VINF_HGCM_ASYNC_EXECUTE)
1916 g_pHelpers->pfnCallComplete(callHandle, rc);
1917}
1918
1919/**
1920 * Initializes a Shared Clipboard service's client state.
1921 *
1922 * @returns VBox status code.
1923 * @param pClientState Client state to initialize.
1924 * @param uClientID Client ID (HGCM) to use for this client state.
1925 */
1926int shClSvcClientStateInit(PSHCLCLIENTSTATE pClientState, uint32_t uClientID)
1927{
1928 LogFlowFuncEnter();
1929
1930 shclSvcClientStateReset(pClientState);
1931
1932 /* Register the client. */
1933 pClientState->uClientID = uClientID;
1934
1935 return VINF_SUCCESS;
1936}
1937
1938/**
1939 * Destroys a Shared Clipboard service's client state.
1940 *
1941 * @returns VBox status code.
1942 * @param pClientState Client state to destroy.
1943 */
1944int shClSvcClientStateDestroy(PSHCLCLIENTSTATE pClientState)
1945{
1946 RT_NOREF(pClientState);
1947
1948 LogFlowFuncEnter();
1949
1950 return VINF_SUCCESS;
1951}
1952
1953/**
1954 * Resets a Shared Clipboard service's client state.
1955 *
1956 * @param pClientState Client state to reset.
1957 */
1958void shclSvcClientStateReset(PSHCLCLIENTSTATE pClientState)
1959{
1960 LogFlowFuncEnter();
1961
1962 pClientState->fGuestFeatures0 = VBOX_SHCL_GF_NONE;
1963 pClientState->fGuestFeatures1 = VBOX_SHCL_GF_NONE;
1964
1965 pClientState->cbChunkSize = _64K; /** Make this configurable. */
1966 pClientState->enmSource = SHCLSOURCE_INVALID;
1967 pClientState->fFlags = SHCLCLIENTSTATE_FLAGS_NONE;
1968
1969 pClientState->POD.enmDir = SHCLTRANSFERDIR_UNKNOWN;
1970 pClientState->POD.uFormat = VBOX_SHCL_FMT_NONE;
1971 pClientState->POD.cbToReadWriteTotal = 0;
1972 pClientState->POD.cbReadWritten = 0;
1973
1974#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1975 pClientState->Transfers.enmTransferDir = SHCLTRANSFERDIR_UNKNOWN;
1976#endif
1977
1978
1979}
1980
1981/*
1982 * We differentiate between a function handler for the guest and one for the host.
1983 */
1984static DECLCALLBACK(int) svcHostCall(void *,
1985 uint32_t u32Function,
1986 uint32_t cParms,
1987 VBOXHGCMSVCPARM paParms[])
1988{
1989 int rc = VINF_SUCCESS;
1990
1991 LogFlowFunc(("u32Function=%RU32 (%s), cParms=%RU32, paParms=%p\n",
1992 u32Function, ShClHostFunctionToStr(u32Function), cParms, paParms));
1993
1994 switch (u32Function)
1995 {
1996 case VBOX_SHCL_HOST_FN_SET_MODE:
1997 {
1998 if (cParms != 1)
1999 {
2000 rc = VERR_INVALID_PARAMETER;
2001 }
2002 else
2003 {
2004 uint32_t u32Mode = VBOX_SHCL_MODE_OFF;
2005
2006 rc = HGCMSvcGetU32(&paParms[0], &u32Mode);
2007 if (RT_SUCCESS(rc))
2008 rc = shClSvcModeSet(u32Mode);
2009 }
2010
2011 break;
2012 }
2013
2014#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2015 case VBOX_SHCL_HOST_FN_SET_TRANSFER_MODE:
2016 {
2017 if (cParms != 1)
2018 {
2019 rc = VERR_INVALID_PARAMETER;
2020 }
2021 else
2022 {
2023 uint32_t fTransferMode;
2024 rc = HGCMSvcGetU32(&paParms[0], &fTransferMode);
2025 if (RT_SUCCESS(rc))
2026 rc = shClSvcTransferModeSet(fTransferMode);
2027 }
2028 break;
2029 }
2030#endif
2031 case VBOX_SHCL_HOST_FN_SET_HEADLESS:
2032 {
2033 if (cParms != 1)
2034 {
2035 rc = VERR_INVALID_PARAMETER;
2036 }
2037 else
2038 {
2039 uint32_t uHeadless;
2040 rc = HGCMSvcGetU32(&paParms[0], &uHeadless);
2041 if (RT_SUCCESS(rc))
2042 {
2043 g_fHeadless = RT_BOOL(uHeadless);
2044 LogRel(("Shared Clipboard: Service running in %s mode\n", g_fHeadless ? "headless" : "normal"));
2045 }
2046 }
2047 break;
2048 }
2049
2050 default:
2051 {
2052#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2053 rc = shClSvcTransferHostHandler(u32Function, cParms, paParms);
2054#else
2055 rc = VERR_NOT_IMPLEMENTED;
2056#endif
2057 break;
2058 }
2059 }
2060
2061 LogFlowFuncLeaveRC(rc);
2062 return rc;
2063}
2064
2065#ifndef UNIT_TEST
2066/**
2067 * SSM descriptor table for the SHCLCLIENTSTATE structure.
2068 */
2069static SSMFIELD const s_aShClSSMClientState[] =
2070{
2071 /** Note: Saving the session ID not necessary, as they're not persistent across state save/restore. */
2072 SSMFIELD_ENTRY(SHCLCLIENTSTATE, fGuestFeatures0),
2073 SSMFIELD_ENTRY(SHCLCLIENTSTATE, fGuestFeatures1),
2074 SSMFIELD_ENTRY(SHCLCLIENTSTATE, cbChunkSize),
2075 SSMFIELD_ENTRY(SHCLCLIENTSTATE, enmSource),
2076 SSMFIELD_ENTRY(SHCLCLIENTSTATE, fFlags),
2077 SSMFIELD_ENTRY_TERM()
2078};
2079
2080/**
2081 * SSM descriptor table for the SHCLCLIENTPODSTATE structure.
2082 */
2083static SSMFIELD const s_aShClSSMClientPODState[] =
2084{
2085 SSMFIELD_ENTRY(SHCLCLIENTPODSTATE, enmDir),
2086 SSMFIELD_ENTRY(SHCLCLIENTPODSTATE, uFormat),
2087 SSMFIELD_ENTRY(SHCLCLIENTPODSTATE, cbToReadWriteTotal),
2088 SSMFIELD_ENTRY(SHCLCLIENTPODSTATE, cbReadWritten),
2089 SSMFIELD_ENTRY(SHCLCLIENTPODSTATE, tsLastReadWrittenMs),
2090 SSMFIELD_ENTRY_TERM()
2091};
2092
2093/**
2094 * SSM descriptor table for the SHCLCLIENTURISTATE structure.
2095 */
2096static SSMFIELD const s_aShClSSMClientTransferState[] =
2097{
2098 SSMFIELD_ENTRY(SHCLCLIENTTRANSFERSTATE, enmTransferDir),
2099 SSMFIELD_ENTRY_TERM()
2100};
2101
2102/**
2103 * SSM descriptor table for the header of the SHCLCLIENTMSG structure.
2104 * The actual message parameters will be serialized separately.
2105 */
2106static SSMFIELD const s_aShClSSMClientMsgHdr[] =
2107{
2108 SSMFIELD_ENTRY(SHCLCLIENTMSG, uMsg),
2109 SSMFIELD_ENTRY(SHCLCLIENTMSG, cParms),
2110 SSMFIELD_ENTRY_TERM()
2111};
2112
2113/**
2114 * SSM descriptor table for the VBOXSHCLMSGCTX structure.
2115 */
2116static SSMFIELD const s_aShClSSMClientMsgCtx[] =
2117{
2118 SSMFIELD_ENTRY(SHCLMSGCTX, uContextID),
2119 SSMFIELD_ENTRY_TERM()
2120};
2121#endif /* !UNIT_TEST */
2122
2123static DECLCALLBACK(int) svcSaveState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
2124{
2125 LogFlowFuncEnter();
2126
2127#ifndef UNIT_TEST
2128 /*
2129 * When the state will be restored, pending requests will be reissued
2130 * by VMMDev. The service therefore must save state as if there were no
2131 * pending request.
2132 * Pending requests, if any, will be completed in svcDisconnect.
2133 */
2134 RT_NOREF(u32ClientID);
2135 LogFunc(("u32ClientID=%RU32\n", u32ClientID));
2136
2137 PSHCLCLIENT pClient = (PSHCLCLIENT)pvClient;
2138 AssertPtr(pClient);
2139
2140 /* Write Shared Clipboard saved state version. */
2141 SSMR3PutU32(pSSM, VBOX_SHCL_SSM_VER_LATEST);
2142
2143 int rc = SSMR3PutStructEx(pSSM, &pClient->State, sizeof(pClient->State), 0 /*fFlags*/, &s_aShClSSMClientState[0], NULL);
2144 AssertRCReturn(rc, rc);
2145
2146 rc = SSMR3PutStructEx(pSSM, &pClient->State.POD, sizeof(pClient->State.POD), 0 /*fFlags*/, &s_aShClSSMClientPODState[0], NULL);
2147 AssertRCReturn(rc, rc);
2148
2149 rc = SSMR3PutStructEx(pSSM, &pClient->State.Transfers, sizeof(pClient->State.Transfers), 0 /*fFlags*/, &s_aShClSSMClientTransferState[0], NULL);
2150 AssertRCReturn(rc, rc);
2151
2152 /* Serialize the client's internal message queue. */
2153 rc = SSMR3PutU64(pSSM, (uint64_t)pClient->queueMsg.size());
2154 AssertRCReturn(rc, rc);
2155
2156 for (size_t i = 0; i < pClient->queueMsg.size(); i++)
2157 {
2158 PSHCLCLIENTMSG pMsg = pClient->queueMsg.at(i);
2159 AssertPtr(pMsg);
2160
2161 rc = SSMR3PutStructEx(pSSM, pMsg, sizeof(SHCLCLIENTMSG), 0 /*fFlags*/, &s_aShClSSMClientMsgHdr[0], NULL);
2162 AssertRCReturn(rc, rc);
2163
2164 rc = SSMR3PutStructEx(pSSM, &pMsg->Ctx, sizeof(SHCLMSGCTX), 0 /*fFlags*/, &s_aShClSSMClientMsgCtx[0], NULL);
2165 AssertRCReturn(rc, rc);
2166
2167 for (uint32_t p = 0; p < pMsg->cParms; p++)
2168 {
2169 rc = HGCMSvcSSMR3Put(&pMsg->paParms[p], pSSM);
2170 AssertRCReturn(rc, rc);
2171 }
2172 }
2173
2174#else /* UNIT_TEST */
2175 RT_NOREF3(u32ClientID, pvClient, pSSM);
2176#endif /* UNIT_TEST */
2177 return VINF_SUCCESS;
2178}
2179
2180#ifndef UNIT_TEST
2181static int svcLoadStateV0(uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion)
2182{
2183 RT_NOREF(u32ClientID, pvClient, pSSM, uVersion);
2184
2185 uint32_t uMarker;
2186 int rc = SSMR3GetU32(pSSM, &uMarker); /* Begin marker. */
2187 AssertRC(rc);
2188 Assert(uMarker == UINT32_C(0x19200102) /* SSMR3STRUCT_BEGIN */);
2189
2190 rc = SSMR3Skip(pSSM, sizeof(uint32_t)); /* Client ID */
2191 AssertRCReturn(rc, rc);
2192
2193 bool fValue;
2194 rc = SSMR3GetBool(pSSM, &fValue); /* fHostMsgQuit */
2195 AssertRCReturn(rc, rc);
2196
2197 rc = SSMR3GetBool(pSSM, &fValue); /* fHostMsgReadData */
2198 AssertRCReturn(rc, rc);
2199
2200 rc = SSMR3GetBool(pSSM, &fValue); /* fHostMsgFormats */
2201 AssertRCReturn(rc, rc);
2202
2203 uint32_t fFormats;
2204 rc = SSMR3GetU32(pSSM, &fFormats); /* u32RequestedFormat */
2205 AssertRCReturn(rc, rc);
2206
2207 rc = SSMR3GetU32(pSSM, &uMarker); /* End marker. */
2208 AssertRCReturn(rc, rc);
2209 Assert(uMarker == UINT32_C(0x19920406) /* SSMR3STRUCT_END */);
2210
2211 return VINF_SUCCESS;
2212}
2213#endif /* UNIT_TEST */
2214
2215static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion)
2216{
2217 LogFlowFuncEnter();
2218
2219#ifndef UNIT_TEST
2220
2221 RT_NOREF(u32ClientID, uVersion);
2222
2223 PSHCLCLIENT pClient = (PSHCLCLIENT)pvClient;
2224 AssertPtr(pClient);
2225
2226 /* Restore the client data. */
2227 uint32_t lenOrVer;
2228 int rc = SSMR3GetU32(pSSM, &lenOrVer);
2229 AssertRCReturn(rc, rc);
2230
2231 LogFunc(("u32ClientID=%RU32, lenOrVer=%#RX64\n", u32ClientID, lenOrVer));
2232
2233 if (lenOrVer == VBOX_SHCL_SSM_VER_0)
2234 {
2235 return svcLoadStateV0(u32ClientID, pvClient, pSSM, uVersion);
2236 }
2237 else if (lenOrVer >= VBOX_SHCL_SSM_VER_1)
2238 {
2239 if (lenOrVer >= VBOX_SHCL_SSM_VER_2)
2240 {
2241 rc = SSMR3GetStructEx(pSSM, &pClient->State, sizeof(pClient->State), 0 /* fFlags */,
2242 &s_aShClSSMClientState[0], NULL);
2243 AssertRCReturn(rc, rc);
2244
2245 rc = SSMR3GetStructEx(pSSM, &pClient->State.POD, sizeof(pClient->State.POD), 0 /* fFlags */,
2246 &s_aShClSSMClientPODState[0], NULL);
2247 AssertRCReturn(rc, rc);
2248 }
2249 else /** @todo Remove this block after 6.1 RC; don't annoy team members with broken saved states. */
2250 {
2251 rc = SSMR3Skip(pSSM, sizeof(uint32_t)); /* Begin marker */
2252 AssertRC(rc);
2253
2254 rc = SSMR3GetU64(pSSM, &pClient->State.fGuestFeatures0);
2255 AssertRC(rc);
2256
2257 rc = SSMR3GetU64(pSSM, &pClient->State.fGuestFeatures1);
2258 AssertRC(rc);
2259
2260 rc = SSMR3GetU32(pSSM, &pClient->State.cbChunkSize);
2261 AssertRC(rc);
2262
2263 rc = SSMR3GetU32(pSSM, (uint32_t *)&pClient->State.enmSource);
2264 AssertRC(rc);
2265
2266 rc = SSMR3Skip(pSSM, sizeof(uint32_t)); /* End marker */
2267 AssertRC(rc);
2268 }
2269
2270 rc = SSMR3GetStructEx(pSSM, &pClient->State.Transfers, sizeof(pClient->State.Transfers), 0 /* fFlags */,
2271 &s_aShClSSMClientTransferState[0], NULL);
2272 AssertRCReturn(rc, rc);
2273
2274 /* Load the client's internal message queue. */
2275 uint64_t cMsg;
2276 rc = SSMR3GetU64(pSSM, &cMsg);
2277 AssertRCReturn(rc, rc);
2278
2279 for (uint64_t i = 0; i < cMsg; i++)
2280 {
2281 PSHCLCLIENTMSG pMsg = (PSHCLCLIENTMSG)RTMemAlloc(sizeof(SHCLCLIENTMSG));
2282 AssertPtrReturn(pMsg, VERR_NO_MEMORY);
2283
2284 rc = SSMR3GetStructEx(pSSM, pMsg, sizeof(SHCLCLIENTMSG), 0 /*fFlags*/, &s_aShClSSMClientMsgHdr[0], NULL);
2285 AssertRCReturn(rc, rc);
2286
2287 rc = SSMR3GetStructEx(pSSM, &pMsg->Ctx, sizeof(SHCLMSGCTX), 0 /*fFlags*/, &s_aShClSSMClientMsgCtx[0], NULL);
2288 AssertRCReturn(rc, rc);
2289
2290 pMsg->paParms = (PVBOXHGCMSVCPARM)RTMemAllocZ(sizeof(VBOXHGCMSVCPARM) * pMsg->cParms);
2291 AssertPtrReturn(pMsg->paParms, VERR_NO_MEMORY);
2292
2293 for (uint32_t p = 0; p < pMsg->cParms; p++)
2294 {
2295 rc = HGCMSvcSSMR3Get(&pMsg->paParms[p], pSSM);
2296 AssertRCReturn(rc, rc);
2297 }
2298
2299 rc = shClSvcMsgAdd(pClient, pMsg, true /* fAppend */);
2300 AssertRCReturn(rc, rc);
2301 }
2302 }
2303 else
2304 {
2305 LogRel(("Shared Clipboard: Unknown saved state version (%#x)\n", lenOrVer));
2306 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2307 }
2308
2309 /* Actual host data are to be reported to guest (SYNC). */
2310 ShClSvcImplSync(pClient);
2311
2312#else /* UNIT_TEST */
2313 RT_NOREF(u32ClientID, pvClient, pSSM, uVersion);
2314#endif /* UNIT_TEST */
2315 return VINF_SUCCESS;
2316}
2317
2318static DECLCALLBACK(int) extCallback(uint32_t u32Function, uint32_t u32Format, void *pvData, uint32_t cbData)
2319{
2320 RT_NOREF(pvData, cbData);
2321
2322 LogFlowFunc(("u32Function=%RU32\n", u32Function));
2323
2324 int rc = VINF_SUCCESS;
2325
2326 /* Figure out if the client in charge for the service extension still is connected. */
2327 ClipboardClientMap::const_iterator itClient = g_mapClients.find(g_ExtState.uClientID);
2328 if (itClient != g_mapClients.end())
2329 {
2330 PSHCLCLIENT pClient = itClient->second;
2331 AssertPtr(pClient);
2332
2333 switch (u32Function)
2334 {
2335 /* The service extension announces formats to the guest. */
2336 case VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE:
2337 {
2338 LogFlowFunc(("VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE: g_ExtState.fReadingData=%RTbool\n", g_ExtState.fReadingData));
2339 if (g_ExtState.fReadingData)
2340 {
2341 g_ExtState.fDelayedAnnouncement = true;
2342 g_ExtState.uDelayedFormats = u32Format;
2343 }
2344 else if (u32Format != VBOX_SHCL_FMT_NONE)
2345 {
2346 SHCLFORMATDATA formatData;
2347 RT_ZERO(formatData);
2348
2349 formatData.Formats = u32Format;
2350
2351 rc = ShClSvcFormatsReport(pClient, &formatData);
2352 }
2353
2354 break;
2355 }
2356
2357 /* The service extension wants read data from the guest. */
2358 case VBOX_CLIPBOARD_EXT_FN_DATA_READ:
2359 {
2360 SHCLDATAREQ dataReq;
2361 RT_ZERO(dataReq);
2362
2363 dataReq.uFmt = u32Format;
2364 dataReq.cbSize = _64K; /** @todo Make this more dynamic. */
2365
2366 rc = ShClSvcDataReadRequest(pClient, &dataReq, NULL /* puEvent */);
2367 break;
2368 }
2369
2370 default:
2371 /* Just skip other messages. */
2372 break;
2373 }
2374 }
2375 else
2376 rc = VERR_NOT_FOUND;
2377
2378 LogFlowFuncLeaveRC(rc);
2379 return rc;
2380}
2381
2382static DECLCALLBACK(int) svcRegisterExtension(void *, PFNHGCMSVCEXT pfnExtension, void *pvExtension)
2383{
2384 LogFlowFunc(("pfnExtension=%p\n", pfnExtension));
2385
2386 SHCLEXTPARMS parms;
2387 RT_ZERO(parms);
2388
2389 if (pfnExtension)
2390 {
2391 /* Install extension. */
2392 g_ExtState.pfnExtension = pfnExtension;
2393 g_ExtState.pvExtension = pvExtension;
2394
2395 parms.u.pfnCallback = extCallback;
2396
2397 g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof(parms));
2398 }
2399 else
2400 {
2401 if (g_ExtState.pfnExtension)
2402 g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof(parms));
2403
2404 /* Uninstall extension. */
2405 g_ExtState.pfnExtension = NULL;
2406 g_ExtState.pvExtension = NULL;
2407 }
2408
2409 return VINF_SUCCESS;
2410}
2411
2412extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
2413{
2414 int rc = VINF_SUCCESS;
2415
2416 LogFlowFunc(("pTable=%p\n", pTable));
2417
2418 if (!VALID_PTR(pTable))
2419 {
2420 rc = VERR_INVALID_PARAMETER;
2421 }
2422 else
2423 {
2424 LogFunc(("pTable->cbSize = %d, ptable->u32Version = 0x%08X\n", pTable->cbSize, pTable->u32Version));
2425
2426 if ( pTable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
2427 || pTable->u32Version != VBOX_HGCM_SVC_VERSION)
2428 {
2429 rc = VERR_VERSION_MISMATCH;
2430 }
2431 else
2432 {
2433 g_pHelpers = pTable->pHelpers;
2434
2435 pTable->cbClient = sizeof(SHCLCLIENT);
2436
2437 pTable->pfnUnload = svcUnload;
2438 pTable->pfnConnect = svcConnect;
2439 pTable->pfnDisconnect = svcDisconnect;
2440 pTable->pfnCall = svcCall;
2441 pTable->pfnHostCall = svcHostCall;
2442 pTable->pfnSaveState = svcSaveState;
2443 pTable->pfnLoadState = svcLoadState;
2444 pTable->pfnRegisterExtension = svcRegisterExtension;
2445 pTable->pfnNotify = NULL;
2446 pTable->pvService = NULL;
2447
2448 /* Service specific initialization. */
2449 rc = svcInit();
2450 }
2451 }
2452
2453 LogFlowFunc(("Returning %Rrc\n", rc));
2454 return rc;
2455}
Note: See TracBrowser for help on using the repository browser.

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