VirtualBox

source: vbox/trunk/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.cpp@ 97789

Last change on this file since 97789 was 97789, checked in by vboxsync, 2 years ago

DnD: Simplified / untangled reset handling:

  • Don't implicitly reset the DnD state with the GuestDnDSendCtx / GuestDnDRecvCtx classes; that can cause side effects.
  • Don't reset GuestDnDTargets state before sending data to the guest via GuestDnDTarget::sendData().
  • Don't reset the DnD state within GuestDnDState::setProgress() anymore. This needs to be done explicitly by the caller waiting for the progress.
  • Make sure to also clear the DnD state's callback map within GuestDnDState::reset().
  • Implemented a default guest callback, which gets invoked when no other (registered) guest callbacks are in place (anymore).
  • Return VERR_CANCELLED to VBoxTray / VBoxClient clients when there was a reset or the current operation was aborted.
  • Release (2) log what has been reset.
  • Implement reference counting for internal DnD messages within the host service; this should help wrt races between peeking for new client messages and actual retrieving those messages.

[build fix]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.4 KB
Line 
1/* $Id: VBoxDragAndDropSvc.cpp 97789 2022-12-12 18:43:46Z vboxsync $ */
2/** @file
3 * Drag and Drop Service.
4 */
5
6/*
7 * Copyright (C) 2011-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/** @page pg_svc_dnd Drag and drop HGCM Service
29 *
30 * @sa See src/VBox/Main/src-client/GuestDnDPrivate.cpp for more information.
31 */
32
33
34/*********************************************************************************************************************************
35* Header Files *
36*********************************************************************************************************************************/
37#define LOG_GROUP LOG_GROUP_GUEST_DND
38#include <VBox/GuestHost/DragAndDrop.h>
39#include <VBox/GuestHost/DragAndDropDefs.h>
40#include <VBox/HostServices/Service.h>
41#include <VBox/HostServices/DragAndDropSvc.h>
42#include <VBox/AssertGuest.h>
43
44#include <VBox/err.h>
45
46#include <algorithm>
47#include <list>
48#include <map>
49
50#include "dndmanager.h"
51
52using namespace DragAndDropSvc;
53
54
55/*********************************************************************************************************************************
56* Service class declaration *
57*********************************************************************************************************************************/
58
59class DragAndDropClient : public HGCM::Client
60{
61public:
62
63 DragAndDropClient(uint32_t idClient)
64 : HGCM::Client(idClient)
65 , uProtocolVerDeprecated(0)
66 , fGuestFeatures0(VBOX_DND_GF_NONE)
67 , fGuestFeatures1(VBOX_DND_GF_NONE)
68 {
69 RT_ZERO(m_SvcCtx);
70 }
71
72 virtual ~DragAndDropClient(void)
73 {
74 disconnect();
75 }
76
77public:
78
79 void disconnect(void) RT_NOEXCEPT;
80
81public:
82
83 /** Protocol version used by this client.
84 * Deprecated; only used for keeping backwards compatibility. */
85 uint32_t uProtocolVerDeprecated;
86 /** Guest feature flags, VBOX_DND_GF_0_XXX. */
87 uint64_t fGuestFeatures0;
88 /** Guest feature flags, VBOX_DND_GF_1_XXX. */
89 uint64_t fGuestFeatures1;
90};
91
92/** Map holding pointers to drag and drop clients. Key is the (unique) HGCM client ID. */
93typedef std::map<uint32_t, DragAndDropClient*> DnDClientMap;
94
95/** Simple queue (list) which holds deferred (waiting) clients. */
96typedef std::list<uint32_t> DnDClientQueue;
97
98/**
99 * Specialized drag & drop service class.
100 */
101class DragAndDropService : public HGCM::AbstractService<DragAndDropService>
102{
103public:
104 explicit DragAndDropService(PVBOXHGCMSVCHELPERS pHelpers)
105 : HGCM::AbstractService<DragAndDropService>(pHelpers)
106 , m_pManager(NULL)
107 , m_u32Mode(VBOX_DRAG_AND_DROP_MODE_OFF)
108 {}
109
110protected:
111 int init(VBOXHGCMSVCFNTABLE *pTable) RT_NOEXCEPT RT_OVERRIDE;
112 int uninit(void) RT_NOEXCEPT RT_OVERRIDE;
113 int clientConnect(uint32_t idClient, void *pvClient) RT_NOEXCEPT RT_OVERRIDE;
114 int clientDisconnect(uint32_t idClient, void *pvClient) RT_NOEXCEPT RT_OVERRIDE;
115 int clientQueryFeatures(uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT;
116 int clientReportFeatures(DragAndDropClient *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT;
117 void guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t idClient, void *pvClient, uint32_t u32Function,
118 uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT RT_OVERRIDE;
119 int hostCall(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT RT_OVERRIDE;
120
121private:
122 int modeSet(uint32_t u32Mode) RT_NOEXCEPT;
123 inline uint32_t modeGet(void) const RT_NOEXCEPT
124 { return m_u32Mode; };
125
126 static DECLCALLBACK(int) progressCallback(uint32_t uStatus, uint32_t uPercentage, int rc, void *pvUser);
127
128private:
129 /** Pointer to our DnD manager instance. */
130 DnDManager *m_pManager;
131 /** Map of all connected clients.
132 * The primary key is the (unique) client ID, the secondary value
133 * an allocated pointer to the DragAndDropClient class, managed
134 * by this service class. */
135 DnDClientMap m_clientMap;
136 /** List of all clients which are queued up (deferred return) and ready
137 * to process new commands. The key is the (unique) client ID. */
138 DnDClientQueue m_clientQueue;
139 /** Current drag and drop mode, VBOX_DRAG_AND_DROP_MODE_XXX. */
140 uint32_t m_u32Mode;
141 /** Host feature mask (VBOX_DND_HF_0_XXX) for DND_GUEST_REPORT_FEATURES
142 * and DND_GUEST_QUERY_FEATURES. */
143 uint64_t m_fHostFeatures0;
144};
145
146
147/*********************************************************************************************************************************
148* Client implementation *
149*********************************************************************************************************************************/
150
151/**
152 * Called when the HGCM client disconnected on the guest side.
153 *
154 * This function takes care of the client's data cleanup and also lets the host
155 * know that the client has been disconnected.
156 */
157void DragAndDropClient::disconnect(void) RT_NOEXCEPT
158{
159 LogFlowThisFunc(("uClient=%RU32, fDeferred=%RTbool\n", m_idClient, IsDeferred()));
160
161 /*
162 * If the client still is waiting for a message (i.e in deferred mode),
163 * complete the call with a VERR_CANCELED status so that the client (VBoxTray / VBoxClient) knows
164 * it should bail out.
165 */
166 if (IsDeferred())
167 CompleteDeferred(VERR_CANCELLED);
168
169 /*
170 * Let the host know.
171 */
172 VBOXDNDCBDISCONNECTMSGDATA data;
173 RT_ZERO(data);
174 /** @todo Magic needed? */
175 /** @todo Add context ID. */
176
177 if (m_SvcCtx.pfnHostCallback)
178 {
179 int rc2 = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, GUEST_DND_FN_DISCONNECT, &data, sizeof(data));
180 if (RT_FAILURE(rc2))
181 LogFlowFunc(("Warning: Unable to notify host about client %RU32 disconnect, rc=%Rrc\n", m_idClient, rc2));
182 /* Not fatal. */
183 }
184}
185
186
187/*********************************************************************************************************************************
188* Service class implementation *
189*********************************************************************************************************************************/
190
191int DragAndDropService::init(VBOXHGCMSVCFNTABLE *pTable) RT_NOEXCEPT
192{
193 /* Legacy clients map to the root category. */
194 pTable->idxLegacyClientCategory = HGCM_CLIENT_CATEGORY_ROOT;
195
196 /* Limit to 255 clients (see also DragAndDropService::clientConnect). */
197 for (uintptr_t i = 0; i < RT_ELEMENTS(pTable->acMaxClients); i++)
198 pTable->acMaxClients[i] = UINT8_MAX;
199
200 /* Limit the number of concurrent calls to 256 (playing safe). */
201 /** @todo Properly determin the max number of pending/concurrent calls for DnD. */
202 for (uintptr_t i = 0; i < RT_ELEMENTS(pTable->acMaxClients); i++)
203 pTable->acMaxCallsPerClient[i] = 256;
204
205 /* Register functions. */
206 pTable->pfnHostCall = svcHostCall;
207 pTable->pfnSaveState = NULL; /* The service is stateless, so the normal */
208 pTable->pfnLoadState = NULL; /* construction done before restoring suffices */
209 pTable->pfnRegisterExtension = svcRegisterExtension;
210 pTable->pfnNotify = NULL;
211
212 /* Drag'n drop mode is disabled by default. */
213 modeSet(VBOX_DRAG_AND_DROP_MODE_OFF);
214
215 /* Set host features. */
216 m_fHostFeatures0 = VBOX_DND_HF_NONE;
217
218 int rc = VINF_SUCCESS;
219
220 try
221 {
222 m_pManager = new DnDManager(&DragAndDropService::progressCallback, this);
223 }
224 catch (std::bad_alloc &)
225 {
226 rc = VERR_NO_MEMORY;
227 }
228
229 LogFlowFuncLeaveRC(rc);
230 return rc;
231}
232
233int DragAndDropService::uninit(void) RT_NOEXCEPT
234{
235 LogFlowFuncEnter();
236
237 if (m_pManager)
238 {
239 delete m_pManager;
240 m_pManager = NULL;
241 }
242
243 DnDClientMap::iterator itClient = m_clientMap.begin();
244 while (itClient != m_clientMap.end())
245 {
246 delete itClient->second;
247 m_clientMap.erase(itClient);
248 itClient = m_clientMap.begin();
249 }
250
251 LogFlowFuncLeave();
252 return VINF_SUCCESS;
253}
254
255int DragAndDropService::clientConnect(uint32_t idClient, void *pvClient) RT_NOEXCEPT
256{
257 RT_NOREF1(pvClient);
258 if (m_clientMap.size() >= UINT8_MAX) /* Don't allow too much clients at the same time. */
259 {
260 AssertMsgFailed(("Maximum number of clients reached\n"));
261 return VERR_MAX_PROCS_REACHED;
262 }
263
264
265 /*
266 * Add client to our client map.
267 */
268 if (m_clientMap.find(idClient) != m_clientMap.end())
269 {
270 LogFunc(("Client %RU32 is already connected!\n", idClient));
271 return VERR_ALREADY_EXISTS;
272 }
273
274 try
275 {
276 DragAndDropClient *pClient = new DragAndDropClient(idClient);
277 pClient->SetSvcContext(m_SvcCtx);
278 m_clientMap[idClient] = pClient;
279 }
280 catch (std::bad_alloc &)
281 {
282 LogFunc(("Client %RU32 - VERR_NO_MEMORY!\n", idClient));
283 return VERR_NO_MEMORY;
284 }
285
286 /*
287 * Reset the message queue as soon as a new client connects
288 * to ensure that every client has the same state.
289 */
290 if (m_pManager)
291 m_pManager->Reset(true /* fForce */);
292
293 LogFlowFunc(("Client %RU32 connected (VINF_SUCCESS)\n", idClient));
294 return VINF_SUCCESS;
295}
296
297int DragAndDropService::clientDisconnect(uint32_t idClient, void *pvClient) RT_NOEXCEPT
298{
299 RT_NOREF1(pvClient);
300
301 /* Client not found? Bail out early. */
302 DnDClientMap::iterator itClient = m_clientMap.find(idClient);
303 if (itClient == m_clientMap.end())
304 {
305 LogFunc(("Client %RU32 not found!\n", idClient));
306 return VERR_NOT_FOUND;
307 }
308
309 /*
310 * Remove from waiters queue.
311 */
312 m_clientQueue.remove(idClient);
313
314 /*
315 * Remove from client map and deallocate.
316 */
317 AssertPtr(itClient->second);
318 delete itClient->second;
319
320 m_clientMap.erase(itClient);
321
322 LogFlowFunc(("Client %RU32 disconnected\n", idClient));
323 return VINF_SUCCESS;
324}
325
326/**
327 * Implements GUEST_DND_FN_REPORT_FEATURES.
328 *
329 * @returns VBox status code.
330 * @retval VINF_HGCM_ASYNC_EXECUTE on success (we complete the message here).
331 * @retval VERR_ACCESS_DENIED if not master
332 * @retval VERR_INVALID_PARAMETER if bit 63 in the 2nd parameter isn't set.
333 * @retval VERR_WRONG_PARAMETER_COUNT
334 *
335 * @param pClient The client state.
336 * @param cParms Number of parameters.
337 * @param paParms Array of parameters.
338 */
339int DragAndDropService::clientReportFeatures(DragAndDropClient *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT
340{
341 RT_NOREF(pClient);
342
343 /*
344 * Validate the request.
345 */
346 ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
347 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
348 uint64_t const fFeatures0 = paParms[0].u.uint64;
349 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
350 uint64_t const fFeatures1 = paParms[1].u.uint64;
351 ASSERT_GUEST_RETURN(fFeatures1 & VBOX_DND_GF_1_MUST_BE_ONE, VERR_INVALID_PARAMETER);
352
353 /*
354 * Report back the host features.
355 */
356 paParms[0].u.uint64 = m_fHostFeatures0;
357 paParms[1].u.uint64 = 0;
358
359 pClient->fGuestFeatures0 = fFeatures0;
360 pClient->fGuestFeatures1 = fFeatures1;
361
362 Log(("[Client %RU32] features: %#RX64 %#RX64\n", pClient->GetClientID(), fFeatures0, fFeatures1));
363
364 return VINF_SUCCESS;
365}
366
367/**
368 * Implements GUEST_DND_FN_QUERY_FEATURES.
369 *
370 * @returns VBox status code.
371 * @retval VINF_HGCM_ASYNC_EXECUTE on success (we complete the message here).
372 * @retval VERR_WRONG_PARAMETER_COUNT
373 *
374 * @param cParms Number of parameters.
375 * @param paParms Array of parameters.
376 */
377int DragAndDropService::clientQueryFeatures(uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT
378{
379 /*
380 * Validate the request.
381 */
382 ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
383 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
384 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
385 ASSERT_GUEST(paParms[1].u.uint64 & RT_BIT_64(63));
386
387 /*
388 * Report back the host features.
389 */
390 paParms[0].u.uint64 = m_fHostFeatures0;
391 paParms[1].u.uint64 = 0;
392
393 return VINF_SUCCESS;
394}
395
396int DragAndDropService::modeSet(uint32_t u32Mode) RT_NOEXCEPT
397{
398#ifndef VBOX_WITH_DRAG_AND_DROP_GH
399 if ( u32Mode == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST
400 || u32Mode == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL)
401 {
402 m_u32Mode = VBOX_DRAG_AND_DROP_MODE_OFF;
403 return VERR_NOT_SUPPORTED;
404 }
405#endif
406
407 switch (u32Mode)
408 {
409 case VBOX_DRAG_AND_DROP_MODE_OFF:
410 case VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST:
411 case VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST:
412 case VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL:
413 m_u32Mode = u32Mode;
414 break;
415
416 default:
417 m_u32Mode = VBOX_DRAG_AND_DROP_MODE_OFF;
418 break;
419 }
420
421 return VINF_SUCCESS;
422}
423
424void DragAndDropService::guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t idClient,
425 void *pvClient, uint32_t u32Function,
426 uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT
427{
428 RT_NOREF1(pvClient);
429 LogFlowFunc(("idClient=%RU32, u32Function=%s (%#x), cParms=%RU32\n",
430 idClient, DnDGuestMsgToStr(u32Function), u32Function, cParms));
431
432 /* Check if we've the right mode set. */
433 int rc = VERR_ACCESS_DENIED; /* Play safe. */
434 switch (u32Function)
435 {
436 case GUEST_DND_FN_GET_NEXT_HOST_MSG:
437 {
438 if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF)
439 rc = VINF_SUCCESS;
440 else
441 {
442 LogRel(("DnD: Feature is disabled, ignoring request from guest\n"));
443 rc = VINF_HGCM_ASYNC_EXECUTE;
444 }
445 break;
446 }
447
448 /* New since protocol v2. */
449 case GUEST_DND_FN_CONNECT:
450 RT_FALL_THROUGH();
451 /* New since VBox 6.1.x. */
452 case GUEST_DND_FN_REPORT_FEATURES:
453 RT_FALL_THROUGH();
454 /* New since VBox 6.1.x. */
455 case GUEST_DND_FN_QUERY_FEATURES:
456 {
457 /*
458 * Never block these calls, as the clients issues those when
459 * initializing and might get stuck if drag and drop is set to "disabled" at
460 * that time.
461 */
462 rc = VINF_SUCCESS;
463 break;
464 }
465
466 /* New since VBOx 7.0.x. See define for details. */
467 case GUEST_DND_FN_EVT_ERROR:
468 {
469 rc = VINF_SUCCESS;
470 break;
471 }
472
473 case GUEST_DND_FN_HG_ACK_OP:
474 case GUEST_DND_FN_HG_REQ_DATA:
475 case GUEST_DND_FN_HG_EVT_PROGRESS:
476 {
477 if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
478 || modeGet() == VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST)
479 rc = VINF_SUCCESS;
480 else
481 LogRel(("DnD: Host -> Guest mode disabled, ignoring request from guest\n"));
482 break;
483 }
484
485 case GUEST_DND_FN_GH_ACK_PENDING:
486 case GUEST_DND_FN_GH_SND_DATA_HDR:
487 case GUEST_DND_FN_GH_SND_DATA:
488 case GUEST_DND_FN_GH_SND_DIR:
489 case GUEST_DND_FN_GH_SND_FILE_HDR:
490 case GUEST_DND_FN_GH_SND_FILE_DATA:
491 {
492#ifdef VBOX_WITH_DRAG_AND_DROP_GH
493 if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
494 || modeGet() == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST)
495 rc = VINF_SUCCESS;
496 else
497#endif
498 LogRel(("DnD: Guest -> Host mode disabled, ignoring request from guest\n"));
499 break;
500 }
501
502 default:
503 /* Reach through to DnD manager. */
504 rc = VINF_SUCCESS;
505 break;
506 }
507
508#define DO_HOST_CALLBACK(); \
509 if ( RT_SUCCESS(rc) \
510 && m_SvcCtx.pfnHostCallback) \
511 { \
512 rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data)); \
513 }
514
515 /*
516 * Lookup client.
517 */
518 DragAndDropClient *pClient = NULL;
519
520 DnDClientMap::iterator itClient = m_clientMap.find(idClient);
521 if (itClient != m_clientMap.end())
522 {
523 pClient = itClient->second;
524 AssertPtr(pClient);
525 }
526 else
527 {
528 LogFunc(("Client %RU32 was not found\n", idClient));
529 rc = VERR_NOT_FOUND;
530 }
531
532/* Verifies that an uint32 parameter has the expected buffer size set.
533 * Will set rc to VERR_INVALID_PARAMETER otherwise. See #9777. */
534#define VERIFY_BUFFER_SIZE_UINT32(a_ParmUInt32, a_SizeExpected) \
535do { \
536 uint32_t cbTemp = 0; \
537 rc = HGCMSvcGetU32(&a_ParmUInt32, &cbTemp); \
538 ASSERT_GUEST_BREAK(RT_SUCCESS(rc) && cbTemp == a_SizeExpected); \
539} while (0)
540
541/* Gets the context ID from the first parameter and store it into the data header.
542 * Then increments idxParm by one if more than one parameter is available. */
543#define GET_CONTEXT_ID_PARM0() \
544 if (fHasCtxID) \
545 { \
546 ASSERT_GUEST_BREAK(cParms >= 1); \
547 rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID); \
548 ASSERT_GUEST_BREAK(RT_SUCCESS(rc)); \
549 if (cParms > 1) \
550 idxParm++; \
551 }
552
553 if (rc == VINF_SUCCESS) /* Note: rc might be VINF_HGCM_ASYNC_EXECUTE! */
554 {
555 rc = VERR_INVALID_PARAMETER; /* Play safe by default. */
556
557 /* Whether the client's advertised protocol sends context IDs with commands. */
558 const bool fHasCtxID = pClient->uProtocolVerDeprecated >= 3;
559
560 /* Current parameter index to process. */
561 unsigned idxParm = 0;
562
563 switch (u32Function)
564 {
565 /*
566 * Note: Older VBox versions with enabled DnD guest->host support (< 5.0)
567 * used the same message ID (300) for GUEST_DND_FN_GET_NEXT_HOST_MSG and
568 * HOST_DND_FN_GH_REQ_PENDING, which led this service returning
569 * VERR_INVALID_PARAMETER when the guest wanted to actually
570 * handle HOST_DND_FN_GH_REQ_PENDING.
571 */
572 case GUEST_DND_FN_GET_NEXT_HOST_MSG:
573 {
574 if (cParms == 3)
575 {
576 /* Make sure to increase the reference count so that the next message doesn't get removed between
577 * the guest's GUEST_DND_FN_GET_NEXT_HOST_MSG call and the actual message retrieval call. */
578 rc = m_pManager->GetNextMsgInfo(true /* fAddRef */,
579 &paParms[0].u.uint32 /* uMsg */, &paParms[1].u.uint32 /* cParms */);
580 if (RT_FAILURE(rc)) /* No queued messages available? */
581 {
582 if (m_SvcCtx.pfnHostCallback) /* Try asking the host. */
583 {
584 VBOXDNDCBHGGETNEXTHOSTMSG data;
585 RT_ZERO(data);
586 data.hdr.uMagic = CB_MAGIC_DND_HG_GET_NEXT_HOST_MSG;
587 rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data));
588 if (RT_SUCCESS(rc))
589 {
590 paParms[0].u.uint32 = data.uMsg; /* uMsg */
591 paParms[1].u.uint32 = data.cParms; /* cParms */
592 /* Note: paParms[2] was set by the guest as blocking flag. */
593 }
594
595 LogFlowFunc(("Host callback returned %Rrc\n", rc));
596 }
597 else /* No host callback in place, so drag and drop is not supported by the host. */
598 rc = VERR_NOT_SUPPORTED;
599
600 if (RT_FAILURE(rc))
601 rc = m_pManager->GetNextMsg(u32Function, cParms, paParms);
602
603 /* Some error occurred or no (new) messages available? */
604 if (RT_FAILURE(rc))
605 {
606 uint32_t fFlags = 0;
607 int rc2 = HGCMSvcGetU32(&paParms[2], &fFlags);
608 if ( RT_SUCCESS(rc2)
609 && fFlags) /* Blocking flag set? */
610 {
611 /* Defer client returning. */
612 rc = VINF_HGCM_ASYNC_EXECUTE;
613 }
614 else
615 rc = VERR_INVALID_PARAMETER;
616
617 LogFlowFunc(("Message queue is empty, returning %Rrc to guest\n", rc));
618 }
619 }
620 }
621 break;
622 }
623 case GUEST_DND_FN_CONNECT:
624 {
625 ASSERT_GUEST_BREAK(cParms >= 2);
626
627 VBOXDNDCBCONNECTDATA data;
628 RT_ZERO(data);
629 data.hdr.uMagic = CB_MAGIC_DND_CONNECT;
630
631 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.hdr.uContextID); \
632 ASSERT_GUEST_RC_BREAK(rc);
633 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.uProtocolVersion);
634 ASSERT_GUEST_RC_BREAK(rc);
635 rc = HGCMSvcGetU32(&paParms[idxParm], &data.fFlags);
636 ASSERT_GUEST_RC_BREAK(rc);
637
638 unsigned uProtocolVer = 3; /* The protocol version we're going to use. */
639
640 /* Make sure we're only setting a protocl version we're supporting on the host. */
641 if (data.uProtocolVersion > uProtocolVer)
642 data.uProtocolVersion = uProtocolVer;
643
644 pClient->uProtocolVerDeprecated = data.uProtocolVersion;
645
646 /* Return the highest protocol version we're supporting. */
647 AssertBreak(idxParm);
648 ASSERT_GUEST_BREAK(idxParm);
649 paParms[idxParm - 1].u.uint32 = data.uProtocolVersion;
650
651 LogFlowFunc(("Client %RU32 is now using protocol v%RU32\n",
652 pClient->GetClientID(), pClient->uProtocolVerDeprecated));
653
654 DO_HOST_CALLBACK();
655 break;
656 }
657 case GUEST_DND_FN_REPORT_FEATURES:
658 {
659 rc = clientReportFeatures(pClient, cParms, paParms);
660 if (RT_SUCCESS(rc))
661 {
662 VBOXDNDCBREPORTFEATURESDATA data;
663 RT_ZERO(data);
664 data.hdr.uMagic = CB_MAGIC_DND_REPORT_FEATURES;
665
666 data.fGuestFeatures0 = pClient->fGuestFeatures0;
667 /* fGuestFeatures1 is not used yet. */
668
669 /* Don't touch initial rc. */
670 int rc2 = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data));
671 AssertRC(rc2);
672 }
673 break;
674 }
675 case GUEST_DND_FN_QUERY_FEATURES:
676 {
677 rc = clientQueryFeatures(cParms, paParms);
678 break;
679 }
680 case GUEST_DND_FN_HG_ACK_OP:
681 {
682 ASSERT_GUEST_BREAK(cParms >= 2);
683
684 VBOXDNDCBHGACKOPDATA data;
685 RT_ZERO(data);
686 data.hdr.uMagic = CB_MAGIC_DND_HG_ACK_OP;
687
688 GET_CONTEXT_ID_PARM0();
689 rc = HGCMSvcGetU32(&paParms[idxParm], &data.uAction); /* Get drop action. */
690 ASSERT_GUEST_RC_BREAK(rc);
691
692 DO_HOST_CALLBACK();
693 break;
694 }
695 case GUEST_DND_FN_HG_REQ_DATA:
696 {
697 VBOXDNDCBHGREQDATADATA data;
698 RT_ZERO(data);
699 data.hdr.uMagic = CB_MAGIC_DND_HG_REQ_DATA;
700
701 switch (pClient->uProtocolVerDeprecated)
702 {
703 case 3:
704 {
705 ASSERT_GUEST_BREAK(cParms == 3);
706 GET_CONTEXT_ID_PARM0();
707 rc = HGCMSvcGetPv(&paParms[idxParm++], (void **)&data.pszFormat, &data.cbFormat);
708 ASSERT_GUEST_RC_BREAK(rc);
709 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm], data.cbFormat);
710 break;
711 }
712
713 case 2:
714 RT_FALL_THROUGH();
715 default:
716 {
717 ASSERT_GUEST_BREAK(cParms == 1);
718 rc = HGCMSvcGetPv(&paParms[idxParm], (void**)&data.pszFormat, &data.cbFormat);
719 ASSERT_GUEST_RC_BREAK(rc);
720 break;
721 }
722 }
723
724 DO_HOST_CALLBACK();
725 break;
726 }
727 case GUEST_DND_FN_HG_EVT_PROGRESS:
728 {
729 ASSERT_GUEST_BREAK(cParms >= 3);
730
731 VBOXDNDCBHGEVTPROGRESSDATA data;
732 RT_ZERO(data);
733 data.hdr.uMagic = CB_MAGIC_DND_HG_EVT_PROGRESS;
734
735 GET_CONTEXT_ID_PARM0();
736 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.uStatus);
737 ASSERT_GUEST_RC_BREAK(rc);
738 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.uPercentage);
739 ASSERT_GUEST_RC_BREAK(rc);
740 rc = HGCMSvcGetU32(&paParms[idxParm], &data.rc);
741 ASSERT_GUEST_RC_BREAK(rc);
742
743 DO_HOST_CALLBACK();
744 break;
745 }
746#ifdef VBOX_WITH_DRAG_AND_DROP_GH
747 case GUEST_DND_FN_GH_ACK_PENDING:
748 {
749 VBOXDNDCBGHACKPENDINGDATA data;
750 RT_ZERO(data);
751 data.hdr.uMagic = CB_MAGIC_DND_GH_ACK_PENDING;
752
753 switch (pClient->uProtocolVerDeprecated)
754 {
755 case 3:
756 {
757 ASSERT_GUEST_BREAK(cParms == 5);
758 GET_CONTEXT_ID_PARM0();
759 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.uDefAction);
760 ASSERT_GUEST_RC_BREAK(rc);
761 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.uAllActions);
762 ASSERT_GUEST_RC_BREAK(rc);
763 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.pszFormat, &data.cbFormat);
764 ASSERT_GUEST_RC_BREAK(rc);
765 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm], data.cbFormat);
766 break;
767 }
768
769 case 2:
770 default:
771 {
772 ASSERT_GUEST_BREAK(cParms == 3);
773 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.uDefAction);
774 ASSERT_GUEST_RC_BREAK(rc);
775 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.uAllActions);
776 ASSERT_GUEST_RC_BREAK(rc);
777 rc = HGCMSvcGetPv(&paParms[idxParm], (void**)&data.pszFormat, &data.cbFormat);
778 ASSERT_GUEST_RC_BREAK(rc);
779 break;
780 }
781 }
782
783 DO_HOST_CALLBACK();
784 break;
785 }
786 /* New since protocol v3. */
787 case GUEST_DND_FN_GH_SND_DATA_HDR:
788 {
789 ASSERT_GUEST_BREAK(cParms == 12);
790
791 VBOXDNDCBSNDDATAHDRDATA data;
792 RT_ZERO(data);
793 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA_HDR;
794
795 GET_CONTEXT_ID_PARM0();
796 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.data.uFlags);
797 ASSERT_GUEST_RC_BREAK(rc);
798 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.data.uScreenId);
799 ASSERT_GUEST_RC_BREAK(rc);
800 rc = HGCMSvcGetU64(&paParms[idxParm++], &data.data.cbTotal);
801 ASSERT_GUEST_RC_BREAK(rc);
802 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.data.cbMeta);
803 ASSERT_GUEST_RC_BREAK(rc);
804 ASSERT_GUEST_BREAK(data.data.cbMeta <= data.data.cbTotal);
805 rc = HGCMSvcGetPv(&paParms[idxParm++], &data.data.pvMetaFmt, &data.data.cbMetaFmt);
806 ASSERT_GUEST_RC_BREAK(rc);
807 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm++], data.data.cbMetaFmt);
808 rc = HGCMSvcGetU64(&paParms[idxParm++], &data.data.cObjects);
809 ASSERT_GUEST_RC_BREAK(rc);
810 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.data.enmCompression);
811 ASSERT_GUEST_RC_BREAK(rc);
812 rc = HGCMSvcGetU32(&paParms[idxParm++], (uint32_t *)&data.data.enmChecksumType);
813 ASSERT_GUEST_RC_BREAK(rc);
814 rc = HGCMSvcGetPv(&paParms[idxParm++], &data.data.pvChecksum, &data.data.cbChecksum);
815 ASSERT_GUEST_RC_BREAK(rc);
816 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm], data.data.cbChecksum);
817
818 DO_HOST_CALLBACK();
819 break;
820 }
821 case GUEST_DND_FN_GH_SND_DATA:
822 {
823 switch (pClient->uProtocolVerDeprecated)
824 {
825 case 3:
826 {
827 ASSERT_GUEST_BREAK(cParms == 5);
828
829 VBOXDNDCBSNDDATADATA data;
830 RT_ZERO(data);
831 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA;
832
833 GET_CONTEXT_ID_PARM0();
834 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.data.u.v3.pvData, &data.data.u.v3.cbData);
835 ASSERT_GUEST_RC_BREAK(rc);
836 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm++], data.data.u.v3.cbData);
837 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.data.u.v3.pvChecksum, &data.data.u.v3.cbChecksum);
838 ASSERT_GUEST_RC_BREAK(rc);
839 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm], data.data.u.v3.cbChecksum);
840
841 DO_HOST_CALLBACK();
842 break;
843 }
844
845 case 2:
846 RT_FALL_THROUGH();
847 default:
848 {
849 ASSERT_GUEST_BREAK(cParms == 2);
850
851 VBOXDNDCBSNDDATADATA data;
852 RT_ZERO(data);
853 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA;
854
855 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.data.u.v1.pvData, &data.data.u.v1.cbData);
856 ASSERT_GUEST_RC_BREAK(rc);
857 rc = HGCMSvcGetU32(&paParms[idxParm], &data.data.u.v1.cbTotalSize);
858 ASSERT_GUEST_RC_BREAK(rc);
859
860 DO_HOST_CALLBACK();
861 break;
862 }
863 }
864 break;
865 }
866 case GUEST_DND_FN_GH_SND_DIR:
867 {
868 ASSERT_GUEST_BREAK(cParms >= 3);
869
870 VBOXDNDCBSNDDIRDATA data;
871 RT_ZERO(data);
872 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DIR;
873
874 GET_CONTEXT_ID_PARM0();
875 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.pszPath, &data.cbPath);
876 ASSERT_GUEST_RC_BREAK(rc);
877 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm++], data.cbPath);
878 rc = HGCMSvcGetU32(&paParms[idxParm], &data.fMode);
879 ASSERT_GUEST_RC_BREAK(rc);
880
881 DO_HOST_CALLBACK();
882 break;
883 }
884 /* New since protocol v2 (>= VBox 5.0). */
885 case GUEST_DND_FN_GH_SND_FILE_HDR:
886 {
887 ASSERT_GUEST_BREAK(cParms == 6);
888
889 VBOXDNDCBSNDFILEHDRDATA data;
890 RT_ZERO(data);
891 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_HDR;
892
893 GET_CONTEXT_ID_PARM0();
894 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.pszFilePath, &data.cbFilePath);
895 ASSERT_GUEST_RC_BREAK(rc);
896 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm++], data.cbFilePath);
897 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.fFlags);
898 ASSERT_GUEST_RC_BREAK(rc);
899 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.fMode);
900 ASSERT_GUEST_RC_BREAK(rc);
901 rc = HGCMSvcGetU64(&paParms[idxParm], &data.cbSize);
902 ASSERT_GUEST_RC_BREAK(rc);
903
904 DO_HOST_CALLBACK();
905 break;
906 }
907 case GUEST_DND_FN_GH_SND_FILE_DATA:
908 {
909 switch (pClient->uProtocolVerDeprecated)
910 {
911 /* Protocol v3 adds (optional) checksums. */
912 case 3:
913 {
914 ASSERT_GUEST_BREAK(cParms == 5);
915
916 VBOXDNDCBSNDFILEDATADATA data;
917 RT_ZERO(data);
918 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA;
919
920 GET_CONTEXT_ID_PARM0();
921 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.pvData, &data.cbData);
922 ASSERT_GUEST_RC_BREAK(rc);
923 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm++], data.cbData);
924 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.u.v3.pvChecksum, &data.u.v3.cbChecksum);
925 ASSERT_GUEST_RC_BREAK(rc);
926 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm], data.u.v3.cbChecksum);
927
928 DO_HOST_CALLBACK();
929 break;
930 }
931 /* Protocol v2 only sends the next data chunks to reduce traffic. */
932 case 2:
933 {
934 ASSERT_GUEST_BREAK(cParms == 3);
935
936 VBOXDNDCBSNDFILEDATADATA data;
937 RT_ZERO(data);
938 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA;
939
940 GET_CONTEXT_ID_PARM0();
941 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.pvData, &data.cbData);
942 ASSERT_GUEST_RC_BREAK(rc);
943 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm], data.cbData);
944
945 DO_HOST_CALLBACK();
946 break;
947 }
948 /* Protocol v1 sends the file path and attributes for every file chunk (!). */
949 default:
950 {
951 ASSERT_GUEST_BREAK(cParms == 5);
952
953 VBOXDNDCBSNDFILEDATADATA data;
954 RT_ZERO(data);
955 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA;
956
957 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.u.v1.pszFilePath, &data.u.v1.cbFilePath);
958 ASSERT_GUEST_RC_BREAK(rc);
959 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm++], data.u.v1.cbFilePath);
960 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.pvData, &data.cbData);
961 ASSERT_GUEST_RC_BREAK(rc);
962 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm++], data.cbData);
963 rc = HGCMSvcGetU32(&paParms[idxParm], &data.u.v1.fMode);
964 ASSERT_GUEST_RC_BREAK(rc);
965
966 DO_HOST_CALLBACK();
967 break;
968 }
969 }
970 break;
971 }
972 case GUEST_DND_FN_EVT_ERROR:
973 {
974 ASSERT_GUEST_BREAK(cParms >= 1);
975
976 VBOXDNDCBEVTERRORDATA data;
977 RT_ZERO(data);
978 data.hdr.uMagic = CB_MAGIC_DND_EVT_ERROR;
979
980 GET_CONTEXT_ID_PARM0();
981 rc = HGCMSvcGetU32(&paParms[idxParm], (uint32_t *)&data.rc);
982 ASSERT_GUEST_RC_BREAK(rc);
983
984 DO_HOST_CALLBACK();
985 break;
986 }
987#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
988
989 default:
990 {
991 LogFlowFunc(("u32Function=%s (%#x), cParms=%RU32\n", DnDHostMsgToStr(u32Function), u32Function, cParms));
992
993 /* All other messages are handled by the DnD manager. */
994 rc = m_pManager->GetNextMsg(u32Function, cParms, paParms);
995 if (rc == VERR_NO_DATA) /* Manager has no new messsages? Try asking the host. */
996 {
997 if (m_SvcCtx.pfnHostCallback)
998 {
999 VBOXDNDCBHGGETNEXTHOSTMSGDATA data;
1000 RT_ZERO(data);
1001
1002 data.hdr.uMagic = VBOX_DND_CB_MAGIC_MAKE(0 /* uFn */, 0 /* uVer */);
1003
1004 data.uMsg = u32Function;
1005 data.cParms = cParms;
1006 data.paParms = paParms;
1007
1008 rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data));
1009 if (RT_SUCCESS(rc))
1010 {
1011 cParms = data.cParms;
1012 paParms = data.paParms;
1013 }
1014 else
1015 {
1016 if (rc == VERR_CANCELLED)
1017 {
1018 /* Host indicated that the current operation was cancelled. Tell the guest. */
1019 LogFunc(("Host indicated that operation was cancelled\n"));
1020 }
1021 else
1022 {
1023 /*
1024 * In case the guest is too fast asking for the next message
1025 * and the host did not supply it yet, just defer the client's
1026 * return until a response from the host available.
1027 */
1028 LogFunc(("No new messages from the host (%Rrc), deferring request\n", rc));
1029 rc = VINF_HGCM_ASYNC_EXECUTE;
1030 }
1031 }
1032 }
1033 else /* No host callback in place, so drag and drop is not supported by the host. */
1034 rc = VERR_NOT_SUPPORTED;
1035 }
1036 break;
1037 }
1038 }
1039 }
1040
1041#undef VERIFY_BUFFER_SIZE_UINT32
1042
1043 /*
1044 * If async execution is requested, we didn't notify the guest yet about
1045 * completion. The client is queued into the waiters list and will be
1046 * notified as soon as a new event is available.
1047 */
1048 if (rc == VINF_HGCM_ASYNC_EXECUTE)
1049 {
1050 LogFunc(("Deferring client %RU32\n", idClient));
1051
1052 try
1053 {
1054 AssertPtr(pClient);
1055 pClient->SetDeferred(callHandle, u32Function, cParms, paParms);
1056 m_clientQueue.push_back(idClient);
1057 }
1058 catch (std::bad_alloc &)
1059 {
1060 rc = VERR_NO_MEMORY;
1061 /* Don't report to guest. */
1062 }
1063 }
1064 else if (pClient)
1065 {
1066 /* Complete the call on the guest side. */
1067 pClient->Complete(callHandle, rc);
1068 }
1069 else
1070 {
1071 AssertMsgFailed(("Guest call failed with %Rrc\n", rc));
1072 rc = VERR_NOT_IMPLEMENTED;
1073 }
1074
1075 LogFunc(("Returning %Rrc to guest\n", rc));
1076}
1077
1078int DragAndDropService::hostCall(uint32_t u32Function,
1079 uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT
1080{
1081 LogFlowFunc(("u32Function=%s (%#x), cParms=%RU32, cClients=%zu, cQueue=%zu\n",
1082 DnDHostMsgToStr(u32Function), u32Function, cParms, m_clientMap.size(), m_clientQueue.size()));
1083
1084 uint32_t const uMode = modeGet();
1085
1086 /* Check if we've the right mode set. */
1087 int rc = VERR_ACCESS_DENIED; /* Play safe. */
1088 switch (u32Function)
1089 {
1090 /*
1091 * Host -> Guest mode
1092 */
1093 case HOST_DND_FN_HG_EVT_ENTER:
1094 RT_FALL_THROUGH();
1095 case HOST_DND_FN_HG_EVT_MOVE:
1096 RT_FALL_THROUGH();
1097 case HOST_DND_FN_HG_EVT_LEAVE:
1098 RT_FALL_THROUGH();
1099 case HOST_DND_FN_HG_EVT_DROPPED:
1100 RT_FALL_THROUGH();
1101 case HOST_DND_FN_HG_SND_DATA_HDR:
1102 RT_FALL_THROUGH();
1103 case HOST_DND_FN_HG_SND_DATA:
1104 RT_FALL_THROUGH();
1105 case HOST_DND_FN_HG_SND_MORE_DATA:
1106 RT_FALL_THROUGH();
1107 case HOST_DND_FN_HG_SND_DIR:
1108 RT_FALL_THROUGH();
1109 case HOST_DND_FN_HG_SND_FILE_DATA:
1110 RT_FALL_THROUGH();
1111 case HOST_DND_FN_HG_SND_FILE_HDR:
1112 {
1113 if ( uMode == VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST
1114 || uMode == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL)
1115 rc = VINF_SUCCESS;
1116 else
1117 {
1118 LogRel2(("DnD: Host to guest mode is disabled, ignoring request from host\n"));
1119 }
1120 break;
1121 }
1122
1123 /*
1124 * Guest -> Host mode
1125 */
1126 case HOST_DND_FN_GH_REQ_PENDING:
1127 RT_FALL_THROUGH();
1128 case HOST_DND_FN_GH_EVT_DROPPED:
1129 {
1130 if ( uMode == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST
1131 || uMode == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL)
1132 rc = VINF_SUCCESS;
1133 else
1134 {
1135 LogRel2(("DnD: Guest to host mode is disabled, ignoring request from host\n"));
1136 }
1137 break;
1138 }
1139
1140 /*
1141 * Both modes
1142 */
1143 case HOST_DND_FN_CANCEL:
1144 if (uMode != VBOX_DRAG_AND_DROP_MODE_OFF)
1145 rc = VINF_SUCCESS;
1146 break;
1147
1148 /*
1149 * Functions that always work.
1150 */
1151 case HOST_DND_FN_SET_MODE:
1152 rc = VINF_SUCCESS;
1153 break;
1154
1155 /*
1156 * Forbid everything else not explicitly allowed.
1157 */
1158 default:
1159 break;
1160 }
1161
1162 if (RT_FAILURE(rc))
1163 return rc;
1164
1165 bool fSendToGuest = false; /* Whether to send the message down to the guest side or not. */
1166
1167 switch (u32Function)
1168 {
1169 case HOST_DND_FN_SET_MODE:
1170 {
1171 if (cParms != 1)
1172 rc = VERR_INVALID_PARAMETER;
1173 else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT)
1174 rc = VERR_INVALID_PARAMETER;
1175 else
1176 rc = modeSet(paParms[0].u.uint32);
1177 break;
1178 }
1179
1180 case HOST_DND_FN_CANCEL:
1181 {
1182 LogFlowFunc(("Cancelling all waiting clients ...\n"));
1183
1184 /* Forcefully reset the message queue, as the host has cancelled the current operation. */
1185 m_pManager->Reset(true /* fForce */);
1186
1187 /*
1188 * Wake up all deferred clients and tell them to process
1189 * the cancelling message next.
1190 */
1191 DnDClientQueue::iterator itQueue = m_clientQueue.begin();
1192 while (itQueue != m_clientQueue.end())
1193 {
1194 DnDClientMap::iterator itClient = m_clientMap.find(*itQueue);
1195 Assert(itClient != m_clientMap.end());
1196
1197 DragAndDropClient *pClient = itClient->second;
1198 AssertPtr(pClient);
1199
1200 int rc2 = pClient->SetDeferredMsgInfo(HOST_DND_FN_CANCEL,
1201 /* Protocol v3+ also contains the context ID. */
1202 pClient->uProtocolVerDeprecated >= 3 ? 1 : 0);
1203 AssertRC(rc2);
1204
1205 /* Return VERR_CANCELLED when waking up the guest side. */
1206 pClient->CompleteDeferred(VERR_CANCELLED);
1207
1208 m_clientQueue.erase(itQueue);
1209 itQueue = m_clientQueue.begin();
1210 }
1211
1212 Assert(m_clientQueue.empty());
1213
1214 /* Tell the host that everything went well. */
1215 rc = VINF_SUCCESS;
1216 break;
1217 }
1218
1219 case HOST_DND_FN_HG_EVT_ENTER:
1220 {
1221 /* Reset the message queue as a new DnD operation just began. */
1222 m_pManager->Reset(false /* fForce */);
1223
1224 fSendToGuest = true;
1225 rc = VINF_SUCCESS;
1226 break;
1227 }
1228
1229 default:
1230 {
1231 fSendToGuest = true;
1232 rc = VINF_SUCCESS;
1233 break;
1234 }
1235 }
1236
1237 do /* goto avoidance break-loop. */
1238 {
1239 if (fSendToGuest)
1240 {
1241 if (m_clientMap.empty()) /* At least one client on the guest connected? */
1242 {
1243 /*
1244 * Tell the host that the guest does not support drag'n drop.
1245 * This might happen due to not installed Guest Additions or
1246 * not running VBoxTray/VBoxClient.
1247 */
1248 rc = VERR_NOT_SUPPORTED;
1249 break;
1250 }
1251
1252 rc = m_pManager->AddMsg(u32Function, cParms, paParms, true /* fAppend */);
1253 if (RT_FAILURE(rc))
1254 {
1255 AssertMsgFailed(("Adding new message of type=%RU32 failed with rc=%Rrc\n", u32Function, rc));
1256 break;
1257 }
1258
1259 /* Any clients in our queue ready for processing the next command? */
1260 if (m_clientQueue.empty())
1261 {
1262 LogFlowFunc(("All clients (%zu) busy -- delaying execution\n", m_clientMap.size()));
1263 break;
1264 }
1265
1266 uint32_t uClientNext = m_clientQueue.front();
1267 DnDClientMap::iterator itClientNext = m_clientMap.find(uClientNext);
1268 Assert(itClientNext != m_clientMap.end());
1269
1270 DragAndDropClient *pClient = itClientNext->second;
1271 AssertPtr(pClient);
1272
1273 /*
1274 * Check if this was a request for getting the next host
1275 * message. If so, return the message ID and the parameter
1276 * count. The message itself has to be queued.
1277 */
1278 uint32_t uMsgClient = pClient->GetMsgType();
1279
1280 uint32_t uMsgNext = 0;
1281 uint32_t cParmsNext = 0;
1282 /* Note: We only want to peek for the next message, hence fAddRef is false. */
1283 int rcNext = m_pManager->GetNextMsgInfo(false /* fAddRef */, &uMsgNext, &cParmsNext);
1284
1285 LogFlowFunc(("uMsgClient=%s (%#x), uMsgNext=%s (%#x), cParmsNext=%RU32, rcNext=%Rrc\n",
1286 DnDGuestMsgToStr(uMsgClient), uMsgClient, DnDHostMsgToStr(uMsgNext), uMsgNext, cParmsNext, rcNext));
1287
1288 if (RT_SUCCESS(rcNext))
1289 {
1290 if (uMsgClient == GUEST_DND_FN_GET_NEXT_HOST_MSG)
1291 {
1292 rc = pClient->SetDeferredMsgInfo(uMsgNext, cParmsNext);
1293
1294 /* Note: Report the current rc back to the guest. */
1295 pClient->CompleteDeferred(rc);
1296 }
1297 /*
1298 * Does the message the client is waiting for match the message
1299 * next in the queue? Process it right away then.
1300 */
1301 else if (uMsgClient == uMsgNext)
1302 {
1303 rc = m_pManager->GetNextMsg(u32Function, cParms, paParms);
1304
1305 /* Note: Report the current rc back to the guest. */
1306 pClient->CompleteDeferred(rc);
1307 }
1308 else /* Should not happen; cancel the operation on the guest. */
1309 {
1310 LogFunc(("Client ID=%RU32 in wrong state with uMsg=%RU32 (next message in queue: %RU32), cancelling\n",
1311 pClient->GetClientID(), uMsgClient, uMsgNext));
1312
1313 pClient->CompleteDeferred(VERR_CANCELLED);
1314 }
1315
1316 m_clientQueue.pop_front();
1317 }
1318
1319 } /* fSendToGuest */
1320
1321 } while (0); /* To use breaks. */
1322
1323 LogFlowFuncLeaveRC(rc);
1324 return rc;
1325}
1326
1327DECLCALLBACK(int) DragAndDropService::progressCallback(uint32_t uStatus, uint32_t uPercentage, int rc, void *pvUser)
1328{
1329 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
1330
1331 DragAndDropService *pSelf = static_cast<DragAndDropService *>(pvUser);
1332 AssertPtr(pSelf);
1333
1334 if (pSelf->m_SvcCtx.pfnHostCallback)
1335 {
1336 LogFlowFunc(("GUEST_DND_FN_HG_EVT_PROGRESS: uStatus=%RU32, uPercentage=%RU32, rc=%Rrc\n",
1337 uStatus, uPercentage, rc));
1338
1339 VBOXDNDCBHGEVTPROGRESSDATA data;
1340 data.hdr.uMagic = CB_MAGIC_DND_HG_EVT_PROGRESS;
1341 data.uPercentage = RT_MIN(uPercentage, 100);
1342 data.uStatus = uStatus;
1343 data.rc = rc; /** @todo uin32_t vs. int. */
1344
1345 return pSelf->m_SvcCtx.pfnHostCallback(pSelf->m_SvcCtx.pvHostData,
1346 GUEST_DND_FN_HG_EVT_PROGRESS,
1347 &data, sizeof(data));
1348 }
1349
1350 return VINF_SUCCESS;
1351}
1352
1353/**
1354 * @copydoc FNVBOXHGCMSVCLOAD
1355 */
1356extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
1357{
1358 return DragAndDropService::svcLoad(pTable);
1359}
1360
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