VirtualBox

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

Last change on this file since 105969 was 104628, checked in by vboxsync, 7 months ago

HostServices/Drag'n Drop: Fixed missing field initializer. ​bugref:3409

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