VirtualBox

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

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

Shared Clipboard/URI: Build fix.

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