VirtualBox

source: vbox/trunk/src/VBox/HostServices/DragAndDrop/service.cpp@ 74205

Last change on this file since 74205 was 74205, checked in by vboxsync, 6 years ago

DnD/HostService: Refactored out more code to the common client and message classes, added documentation (also about deferred client states).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 45.5 KB
Line 
1/* $Id: service.cpp 74205 2018-09-12 09:22:06Z vboxsync $ */
2/** @file
3 * Drag and Drop Service.
4 */
5
6/*
7 * Copyright (C) 2011-2018 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/** @page pg_svc_guest_control Drag and drop HGCM Service
19 *
20 * TODO
21 */
22
23
24/*********************************************************************************************************************************
25* Header Files *
26*********************************************************************************************************************************/
27#ifdef LOG_GROUP
28 #undef LOG_GROUP
29#endif
30#define LOG_GROUP LOG_GROUP_GUEST_DND
31
32#include <algorithm>
33#include <list>
34#include <map>
35
36#include <VBox/GuestHost/DragAndDrop.h>
37#include <VBox/HostServices/Service.h>
38#include <VBox/HostServices/DragAndDropSvc.h>
39
40#include "dndmanager.h"
41
42using namespace DragAndDropSvc;
43
44
45/*********************************************************************************************************************************
46* Service class declaration *
47*********************************************************************************************************************************/
48
49class DragAndDropClient : public HGCM::Client
50{
51public:
52
53 DragAndDropClient(uint32_t uClientID)
54 : HGCM::Client(uClientID)
55 {
56 RT_ZERO(m_SvcCtx);
57 }
58
59 virtual ~DragAndDropClient(void)
60 {
61 disconnect();
62 }
63
64public:
65
66 void disconnect(void);
67};
68
69/** Map holding pointers to drag and drop clients. Key is the (unique) HGCM client ID. */
70typedef std::map<uint32_t, DragAndDropClient*> DnDClientMap;
71
72/** Simple queue (list) which holds deferred (waiting) clients. */
73typedef std::list<uint32_t> DnDClientQueue;
74
75/**
76 * Specialized drag & drop service class.
77 */
78class DragAndDropService : public HGCM::AbstractService<DragAndDropService>
79{
80public:
81
82 explicit DragAndDropService(PVBOXHGCMSVCHELPERS pHelpers)
83 : HGCM::AbstractService<DragAndDropService>(pHelpers)
84 , m_pManager(NULL) {}
85
86protected:
87
88 int init(VBOXHGCMSVCFNTABLE *pTable);
89 int uninit(void);
90 int clientConnect(uint32_t u32ClientID, void *pvClient);
91 int clientDisconnect(uint32_t u32ClientID, void *pvClient);
92 void guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
93 int hostCall(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
94
95 int modeSet(uint32_t u32Mode);
96 inline uint32_t modeGet(void) const { return m_u32Mode; };
97
98protected:
99
100 static DECLCALLBACK(int) progressCallback(uint32_t uStatus, uint32_t uPercentage, int rc, void *pvUser);
101
102protected:
103
104 /** Pointer to our DnD manager instance. */
105 DnDManager *m_pManager;
106 /** Map of all connected clients.
107 * The primary key is the (unique) client ID, the secondary value
108 * an allocated pointer to the DragAndDropClient class, managed
109 * by this service class. */
110 DnDClientMap m_clientMap;
111 /** List of all clients which are queued up (deferred return) and ready
112 * to process new commands. The key is the (unique) client ID. */
113 DnDClientQueue m_clientQueue;
114 /** Current drag and drop mode. */
115 uint32_t m_u32Mode;
116};
117
118
119/*********************************************************************************************************************************
120* Client implementation *
121*********************************************************************************************************************************/
122
123/**
124 * Called when the HGCM client disconnected on the guest side.
125 * This function takes care of the client's data cleanup and also lets the host
126 * know that the client has been disconnected.
127 *
128 */
129void DragAndDropClient::disconnect(void)
130{
131 LogFlowThisFunc(("uClient=%RU32\n", m_uClientID));
132
133 if (IsDeferred())
134 CompleteDeferred(VERR_INTERRUPTED);
135
136 /*
137 * Let the host know.
138 */
139 VBOXDNDCBDISCONNECTMSGDATA data;
140 RT_ZERO(data);
141 /** @todo Magic needed? */
142 /** @todo Add context ID. */
143
144 if (m_SvcCtx.pfnHostCallback)
145 {
146 int rc2 = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, GUEST_DND_DISCONNECT, &data, sizeof(data));
147 if (RT_FAILURE(rc2))
148 LogFlowFunc(("Warning: Unable to notify host about client %RU32 disconnect, rc=%Rrc\n", m_uClientID, rc2));
149 /* Not fatal. */
150 }
151}
152
153/*********************************************************************************************************************************
154* Service class implementation *
155*********************************************************************************************************************************/
156
157int DragAndDropService::init(VBOXHGCMSVCFNTABLE *pTable)
158{
159 /* Register functions. */
160 pTable->pfnHostCall = svcHostCall;
161 pTable->pfnSaveState = NULL; /* The service is stateless, so the normal */
162 pTable->pfnLoadState = NULL; /* construction done before restoring suffices */
163 pTable->pfnRegisterExtension = svcRegisterExtension;
164
165 /* Drag'n drop mode is disabled by default. */
166 modeSet(VBOX_DRAG_AND_DROP_MODE_OFF);
167
168 int rc = VINF_SUCCESS;
169
170 try
171 {
172 m_pManager = new DnDManager(&DragAndDropService::progressCallback, this);
173 }
174 catch(std::bad_alloc &)
175 {
176 rc = VERR_NO_MEMORY;
177 }
178
179 LogFlowFuncLeaveRC(rc);
180 return rc;
181}
182
183int DragAndDropService::uninit(void)
184{
185 LogFlowFuncEnter();
186
187 if (m_pManager)
188 {
189 delete m_pManager;
190 m_pManager = NULL;
191 }
192
193 DnDClientMap::iterator itClient = m_clientMap.begin();
194 while (itClient != m_clientMap.end())
195 {
196 delete itClient->second;
197 m_clientMap.erase(itClient);
198 itClient = m_clientMap.begin();
199 }
200
201 LogFlowFuncLeave();
202 return VINF_SUCCESS;
203}
204
205int DragAndDropService::clientConnect(uint32_t u32ClientID, void *pvClient)
206{
207 RT_NOREF1(pvClient);
208 if (m_clientMap.size() >= UINT8_MAX) /* Don't allow too much clients at the same time. */
209 {
210 AssertMsgFailed(("Maximum number of clients reached\n"));
211 return VERR_MAX_PROCS_REACHED;
212 }
213
214 int rc = VINF_SUCCESS;
215
216 /*
217 * Add client to our client map.
218 */
219 if (m_clientMap.find(u32ClientID) != m_clientMap.end())
220 rc = VERR_ALREADY_EXISTS;
221
222 if (RT_SUCCESS(rc))
223 {
224 try
225 {
226 DragAndDropClient *pClient = new DragAndDropClient(u32ClientID);
227 pClient->SetSvcContext(m_SvcCtx);
228 m_clientMap[u32ClientID] = pClient;
229 }
230 catch(std::bad_alloc &)
231 {
232 rc = VERR_NO_MEMORY;
233 }
234
235 if (RT_SUCCESS(rc))
236 {
237 /*
238 * Clear the message queue as soon as a new clients connect
239 * to ensure that every client has the same state.
240 */
241 if (m_pManager)
242 m_pManager->clear();
243 }
244 }
245
246 LogFlowFunc(("Client %RU32 connected, rc=%Rrc\n", u32ClientID, rc));
247 return rc;
248}
249
250int DragAndDropService::clientDisconnect(uint32_t u32ClientID, void *pvClient)
251{
252 RT_NOREF1(pvClient);
253
254 /* Client not found? Bail out early. */
255 DnDClientMap::iterator itClient = m_clientMap.find(u32ClientID);
256 if (itClient == m_clientMap.end())
257 return VERR_NOT_FOUND;
258
259 /*
260 * Remove from waiters queue.
261 */
262 m_clientQueue.remove(u32ClientID);
263
264 /*
265 * Remove from client map and deallocate.
266 */
267 AssertPtr(itClient->second);
268 delete itClient->second;
269
270 m_clientMap.erase(itClient);
271
272 LogFlowFunc(("Client %RU32 disconnected\n", u32ClientID));
273 return VINF_SUCCESS;
274}
275
276int DragAndDropService::modeSet(uint32_t u32Mode)
277{
278#ifndef VBOX_WITH_DRAG_AND_DROP_GH
279 if ( u32Mode == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST
280 || u32Mode == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL)
281 {
282 m_u32Mode = VBOX_DRAG_AND_DROP_MODE_OFF;
283 return VERR_NOT_SUPPORTED;
284 }
285#endif
286
287 switch (u32Mode)
288 {
289 case VBOX_DRAG_AND_DROP_MODE_OFF:
290 case VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST:
291 case VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST:
292 case VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL:
293 m_u32Mode = u32Mode;
294 break;
295
296 default:
297 m_u32Mode = VBOX_DRAG_AND_DROP_MODE_OFF;
298 break;
299 }
300
301 return VINF_SUCCESS;
302}
303
304void DragAndDropService::guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
305 void *pvClient, uint32_t u32Function,
306 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
307{
308 RT_NOREF1(pvClient);
309 LogFlowFunc(("u32ClientID=%RU32, u32Function=%RU32, cParms=%RU32\n",
310 u32ClientID, u32Function, cParms));
311
312 /* Check if we've the right mode set. */
313 int rc = VERR_ACCESS_DENIED; /* Play safe. */
314 switch (u32Function)
315 {
316 case GUEST_DND_GET_NEXT_HOST_MSG:
317 {
318 if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF)
319 {
320 rc = VINF_SUCCESS;
321 }
322 else
323 {
324 LogFlowFunc(("DnD disabled, deferring request\n"));
325 rc = VINF_HGCM_ASYNC_EXECUTE;
326 }
327 break;
328 }
329
330 /* New since protocol v2. */
331 case GUEST_DND_CONNECT:
332 {
333 /*
334 * Never block the initial connect call, as the clients do this when
335 * initializing and might get stuck if drag and drop is set to "disabled" at
336 * that time.
337 */
338 rc = VINF_SUCCESS;
339 break;
340 }
341 case GUEST_DND_HG_ACK_OP:
342 /* Fall through is intentional. */
343 case GUEST_DND_HG_REQ_DATA:
344 /* Fall through is intentional. */
345 case GUEST_DND_HG_EVT_PROGRESS:
346 {
347 if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
348 || modeGet() == VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST)
349 {
350 rc = VINF_SUCCESS;
351 }
352 else
353 LogFlowFunc(("Host -> Guest DnD mode disabled, ignoring request\n"));
354 break;
355 }
356
357 case GUEST_DND_GH_ACK_PENDING:
358 case GUEST_DND_GH_SND_DATA_HDR:
359 case GUEST_DND_GH_SND_DATA:
360 case GUEST_DND_GH_SND_DIR:
361 case GUEST_DND_GH_SND_FILE_HDR:
362 case GUEST_DND_GH_SND_FILE_DATA:
363 case GUEST_DND_GH_EVT_ERROR:
364 {
365#ifdef VBOX_WITH_DRAG_AND_DROP_GH
366 if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
367 || modeGet() == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST)
368 {
369 rc = VINF_SUCCESS;
370 }
371 else
372#endif
373 LogFlowFunc(("Guest -> Host DnD mode disabled, ignoring request\n"));
374 break;
375 }
376
377 default:
378 /* Reach through to DnD manager. */
379 rc = VINF_SUCCESS;
380 break;
381 }
382
383#ifdef DEBUG_andy
384 LogFlowFunc(("Mode (%RU32) check rc=%Rrc\n", modeGet(), rc));
385#endif
386
387#define DO_HOST_CALLBACK(); \
388 if ( RT_SUCCESS(rc) \
389 && m_SvcCtx.pfnHostCallback) \
390 { \
391 rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data)); \
392 }
393
394 /*
395 * Lookup client.
396 */
397 DragAndDropClient *pClient = NULL;
398
399 DnDClientMap::iterator itClient = m_clientMap.find(u32ClientID);
400 if (itClient != m_clientMap.end())
401 {
402 pClient = itClient->second;
403 AssertPtr(pClient);
404 }
405 else
406 {
407 LogFunc(("Client %RU32 was not found\n", u32ClientID));
408 rc = VERR_NOT_FOUND;
409 }
410
411 if (rc == VINF_SUCCESS) /* Note: rc might be VINF_HGCM_ASYNC_EXECUTE! */
412 {
413 LogFlowFunc(("Client %RU32: Protocol v%RU32\n", pClient->GetClientID(), pClient->GetProtocolVer()));
414
415 rc = VERR_INVALID_PARAMETER; /* Play safe. */
416
417 switch (u32Function)
418 {
419 /*
420 * Note: Older VBox versions with enabled DnD guest->host support (< 5.0)
421 * used the same message ID (300) for GUEST_DND_GET_NEXT_HOST_MSG and
422 * HOST_DND_GH_REQ_PENDING, which led this service returning
423 * VERR_INVALID_PARAMETER when the guest wanted to actually
424 * handle HOST_DND_GH_REQ_PENDING.
425 */
426 case GUEST_DND_GET_NEXT_HOST_MSG:
427 {
428 LogFlowFunc(("GUEST_DND_GET_NEXT_HOST_MSG\n"));
429 if (cParms == 3)
430 {
431 rc = m_pManager->nextMessageInfo(&paParms[0].u.uint32 /* uMsg */, &paParms[1].u.uint32 /* cParms */);
432 if (RT_FAILURE(rc)) /* No queued messages available? */
433 {
434 if (m_SvcCtx.pfnHostCallback) /* Try asking the host. */
435 {
436 VBOXDNDCBHGGETNEXTHOSTMSG data;
437 RT_ZERO(data);
438 data.hdr.uMagic = CB_MAGIC_DND_HG_GET_NEXT_HOST_MSG;
439 rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data));
440 if (RT_SUCCESS(rc))
441 {
442 paParms[0].u.uint32 = data.uMsg; /* uMsg */
443 paParms[1].u.uint32 = data.cParms; /* cParms */
444 /* Note: paParms[2] was set by the guest as blocking flag. */
445 }
446 }
447 else /* No host callback in place, so drag and drop is not supported by the host. */
448 rc = VERR_NOT_SUPPORTED;
449
450 if (RT_FAILURE(rc))
451 rc = m_pManager->nextMessage(u32Function, cParms, paParms);
452
453 /* Some error occurred or no (new) messages available? */
454 if (RT_FAILURE(rc))
455 {
456 uint32_t fFlags = 0;
457 int rc2 = paParms[2].getUInt32(&fFlags);
458 if ( RT_SUCCESS(rc2)
459 && fFlags) /* Blocking flag set? */
460 {
461 /* Defer client returning. */
462 rc = VINF_HGCM_ASYNC_EXECUTE;
463 }
464 else
465 rc = VERR_INVALID_PARAMETER;
466
467 LogFlowFunc(("Message queue is empty, returning %Rrc to guest\n", rc));
468 }
469 }
470 }
471 break;
472 }
473 case GUEST_DND_CONNECT:
474 {
475 LogFlowFunc(("GUEST_DND_CONNECT\n"));
476 if (cParms >= 2)
477 {
478 const uint8_t idxProto = cParms >= 3 ? 1 : 0;
479
480 VBOXDNDCBCONNECTMSGDATA data;
481 RT_ZERO(data);
482 data.hdr.uMagic = CB_MAGIC_DND_CONNECT;
483 if (cParms >= 3)
484 rc = paParms[0].getUInt32(&data.hdr.uContextID);
485 else /* Older protocols don't have a context ID. */
486 rc = VINF_SUCCESS;
487 if (RT_SUCCESS(rc))
488 rc = paParms[idxProto].getUInt32(&data.uProtocol);
489 if (RT_SUCCESS(rc))
490 rc = paParms[idxProto + 1].getUInt32(&data.uFlags);
491 if (RT_SUCCESS(rc))
492 pClient->SetProtocolVer(data.uProtocol);
493 if (RT_SUCCESS(rc))
494 {
495 LogFlowFunc(("Client %RU32 is now using protocol v%RU32\n", pClient->GetClientID(), pClient->GetProtocolVer()));
496 DO_HOST_CALLBACK();
497 }
498 }
499 break;
500 }
501 case GUEST_DND_HG_ACK_OP:
502 {
503 LogFlowFunc(("GUEST_DND_HG_ACK_OP\n"));
504
505 VBOXDNDCBHGACKOPDATA data;
506 RT_ZERO(data);
507 data.hdr.uMagic = CB_MAGIC_DND_HG_ACK_OP;
508
509 switch (pClient->GetProtocolVer())
510 {
511 case 3:
512 {
513 if (cParms == 2)
514 {
515 rc = paParms[0].getUInt32(&data.hdr.uContextID);
516 if (RT_SUCCESS(rc))
517 rc = paParms[1].getUInt32(&data.uAction); /* Get drop action. */
518 }
519 break;
520 }
521
522 case 2:
523 default:
524 {
525 if (cParms == 1)
526 rc = paParms[0].getUInt32(&data.uAction); /* Get drop action. */
527 break;
528 }
529 }
530
531 DO_HOST_CALLBACK();
532 break;
533 }
534 case GUEST_DND_HG_REQ_DATA:
535 {
536 LogFlowFunc(("GUEST_DND_HG_REQ_DATA\n"));
537
538 VBOXDNDCBHGREQDATADATA data;
539 RT_ZERO(data);
540 data.hdr.uMagic = CB_MAGIC_DND_HG_REQ_DATA;
541
542 switch (pClient->GetProtocolVer())
543 {
544 case 3:
545 {
546 if (cParms == 3)
547 {
548 rc = paParms[0].getUInt32(&data.hdr.uContextID);
549 if (RT_SUCCESS(rc))
550 rc = paParms[1].getPointer((void **)&data.pszFormat, &data.cbFormat);
551 if (RT_SUCCESS(rc))
552 rc = paParms[2].getUInt32(&data.cbFormat);
553 }
554 break;
555 }
556
557 case 2:
558 default:
559 {
560 if (cParms == 1)
561 rc = paParms[0].getPointer((void**)&data.pszFormat, &data.cbFormat);
562 break;
563 }
564 }
565
566 DO_HOST_CALLBACK();
567 break;
568 }
569 case GUEST_DND_HG_EVT_PROGRESS:
570 {
571 LogFlowFunc(("GUEST_DND_HG_EVT_PROGRESS\n"));
572
573 VBOXDNDCBHGEVTPROGRESSDATA data;
574 RT_ZERO(data);
575 data.hdr.uMagic = CB_MAGIC_DND_HG_EVT_PROGRESS;
576
577 switch (pClient->GetProtocolVer())
578 {
579 case 3:
580 {
581 if (cParms == 4)
582 {
583 rc = paParms[0].getUInt32(&data.uStatus);
584 if (RT_SUCCESS(rc))
585 rc = paParms[1].getUInt32(&data.uStatus);
586 if (RT_SUCCESS(rc))
587 rc = paParms[2].getUInt32(&data.uPercentage);
588 if (RT_SUCCESS(rc))
589 rc = paParms[3].getUInt32(&data.rc);
590 }
591 break;
592 }
593
594 case 2:
595 default:
596 {
597 if (cParms == 3)
598 {
599 rc = paParms[0].getUInt32(&data.uStatus);
600 if (RT_SUCCESS(rc))
601 rc = paParms[1].getUInt32(&data.uPercentage);
602 if (RT_SUCCESS(rc))
603 rc = paParms[2].getUInt32(&data.rc);
604 }
605 break;
606 }
607 }
608
609 DO_HOST_CALLBACK();
610 break;
611 }
612#ifdef VBOX_WITH_DRAG_AND_DROP_GH
613 case GUEST_DND_GH_ACK_PENDING:
614 {
615 LogFlowFunc(("GUEST_DND_GH_ACK_PENDING\n"));
616
617 VBOXDNDCBGHACKPENDINGDATA data;
618 RT_ZERO(data);
619 data.hdr.uMagic = CB_MAGIC_DND_GH_ACK_PENDING;
620
621 switch (pClient->GetProtocolVer())
622 {
623 case 3:
624 {
625 if (cParms == 5)
626 {
627 rc = paParms[0].getUInt32(&data.hdr.uContextID);
628 if (RT_SUCCESS(rc))
629 rc = paParms[1].getUInt32(&data.uDefAction);
630 if (RT_SUCCESS(rc))
631 rc = paParms[2].getUInt32(&data.uAllActions);
632 if (RT_SUCCESS(rc))
633 rc = paParms[3].getPointer((void**)&data.pszFormat, &data.cbFormat);
634 if (RT_SUCCESS(rc))
635 rc = paParms[4].getUInt32(&data.cbFormat);
636 }
637 break;
638 }
639
640 case 2:
641 default:
642 {
643 if (cParms == 3)
644 {
645 rc = paParms[0].getUInt32(&data.uDefAction);
646 if (RT_SUCCESS(rc))
647 rc = paParms[1].getUInt32(&data.uAllActions);
648 if (RT_SUCCESS(rc))
649 rc = paParms[2].getPointer((void**)&data.pszFormat, &data.cbFormat);
650 }
651 break;
652 }
653 }
654
655 DO_HOST_CALLBACK();
656 break;
657 }
658 /* New since protocol v3. */
659 case GUEST_DND_GH_SND_DATA_HDR:
660 {
661 LogFlowFunc(("GUEST_DND_GH_SND_DATA_HDR\n"));
662 if (cParms == 12)
663 {
664 VBOXDNDCBSNDDATAHDRDATA data;
665 RT_ZERO(data);
666 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA_HDR;
667 rc = paParms[0].getUInt32(&data.hdr.uContextID);
668 if (RT_SUCCESS(rc))
669 rc = paParms[1].getUInt32(&data.data.uFlags);
670 if (RT_SUCCESS(rc))
671 rc = paParms[2].getUInt32(&data.data.uScreenId);
672 if (RT_SUCCESS(rc))
673 rc = paParms[3].getUInt64(&data.data.cbTotal);
674 if (RT_SUCCESS(rc))
675 rc = paParms[4].getUInt32(&data.data.cbMeta);
676 if (RT_SUCCESS(rc))
677 rc = paParms[5].getPointer(&data.data.pvMetaFmt, &data.data.cbMetaFmt);
678 if (RT_SUCCESS(rc))
679 rc = paParms[6].getUInt32(&data.data.cbMetaFmt);
680 if (RT_SUCCESS(rc))
681 rc = paParms[7].getUInt64(&data.data.cObjects);
682 if (RT_SUCCESS(rc))
683 rc = paParms[8].getUInt32(&data.data.enmCompression);
684 if (RT_SUCCESS(rc))
685 rc = paParms[9].getUInt32((uint32_t *)&data.data.enmChecksumType);
686 if (RT_SUCCESS(rc))
687 rc = paParms[10].getPointer(&data.data.pvChecksum, &data.data.cbChecksum);
688 if (RT_SUCCESS(rc))
689 rc = paParms[11].getUInt32(&data.data.cbChecksum);
690
691 LogFlowFunc(("fFlags=0x%x, cbTotalSize=%RU64, cObj=%RU64\n",
692 data.data.uFlags, data.data.cbTotal, data.data.cObjects));
693 DO_HOST_CALLBACK();
694 }
695 break;
696 }
697 case GUEST_DND_GH_SND_DATA:
698 {
699 LogFlowFunc(("GUEST_DND_GH_SND_DATA\n"));
700 switch (pClient->GetProtocolVer())
701 {
702 case 3:
703 {
704 if (cParms == 5)
705 {
706 VBOXDNDCBSNDDATADATA data;
707 RT_ZERO(data);
708 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA;
709 rc = paParms[0].getUInt32(&data.hdr.uContextID);
710 if (RT_SUCCESS(rc))
711 rc = paParms[1].getPointer((void**)&data.data.u.v3.pvData, &data.data.u.v3.cbData);
712 if (RT_SUCCESS(rc))
713 rc = paParms[2].getUInt32(&data.data.u.v3.cbData);
714 if (RT_SUCCESS(rc))
715 rc = paParms[3].getPointer((void**)&data.data.u.v3.pvChecksum, &data.data.u.v3.cbChecksum);
716 if (RT_SUCCESS(rc))
717 rc = paParms[4].getUInt32(&data.data.u.v3.cbChecksum);
718 DO_HOST_CALLBACK();
719 }
720 break;
721 }
722
723 case 2:
724 default:
725 {
726 if (cParms == 2)
727 {
728 VBOXDNDCBSNDDATADATA data;
729 RT_ZERO(data);
730 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA;
731 rc = paParms[0].getPointer((void**)&data.data.u.v1.pvData, &data.data.u.v1.cbData);
732 if (RT_SUCCESS(rc))
733 rc = paParms[1].getUInt32(&data.data.u.v1.cbTotalSize);
734 DO_HOST_CALLBACK();
735 }
736 break;
737 }
738 }
739 break;
740 }
741 case GUEST_DND_GH_SND_DIR:
742 {
743 LogFlowFunc(("GUEST_DND_GH_SND_DIR\n"));
744
745 VBOXDNDCBSNDDIRDATA data;
746 RT_ZERO(data);
747 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DIR;
748
749 switch (pClient->GetProtocolVer())
750 {
751 case 3:
752 {
753 if (cParms == 4)
754 {
755 rc = paParms[0].getUInt32(&data.hdr.uContextID);
756 if (RT_SUCCESS(rc))
757 rc = paParms[1].getPointer((void**)&data.pszPath, &data.cbPath);
758 if (RT_SUCCESS(rc))
759 rc = paParms[2].getUInt32(&data.cbPath);
760 if (RT_SUCCESS(rc))
761 rc = paParms[3].getUInt32(&data.fMode);
762 }
763 break;
764 }
765
766 case 2:
767 default:
768 {
769 if (cParms == 3)
770 {
771 rc = paParms[0].getPointer((void**)&data.pszPath, &data.cbPath);
772 if (RT_SUCCESS(rc))
773 rc = paParms[1].getUInt32(&data.cbPath);
774 if (RT_SUCCESS(rc))
775 rc = paParms[2].getUInt32(&data.fMode);
776 }
777 break;
778 }
779 }
780
781 DO_HOST_CALLBACK();
782 break;
783 }
784 /* New since protocol v2 (>= VBox 5.0). */
785 case GUEST_DND_GH_SND_FILE_HDR:
786 {
787 LogFlowFunc(("GUEST_DND_GH_SND_FILE_HDR\n"));
788 if (cParms == 6)
789 {
790 VBOXDNDCBSNDFILEHDRDATA data;
791 RT_ZERO(data);
792 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_HDR;
793
794 rc = paParms[0].getUInt32(&data.hdr.uContextID);
795 if (RT_SUCCESS(rc))
796 rc = paParms[1].getPointer((void**)&data.pszFilePath, &data.cbFilePath);
797 if (RT_SUCCESS(rc))
798 rc = paParms[2].getUInt32(&data.cbFilePath);
799 if (RT_SUCCESS(rc))
800 rc = paParms[3].getUInt32(&data.fFlags);
801 if (RT_SUCCESS(rc))
802 rc = paParms[4].getUInt32(&data.fMode);
803 if (RT_SUCCESS(rc))
804 rc = paParms[5].getUInt64(&data.cbSize);
805
806 LogFlowFunc(("pszPath=%s, cbPath=%RU32, fMode=0x%x, cbSize=%RU64\n",
807 data.pszFilePath, data.cbFilePath, data.fMode, data.cbSize));
808 DO_HOST_CALLBACK();
809 }
810 break;
811 }
812 case GUEST_DND_GH_SND_FILE_DATA:
813 {
814 LogFlowFunc(("GUEST_DND_GH_SND_FILE_DATA\n"));
815
816 switch (pClient->GetProtocolVer())
817 {
818 /* Protocol v3 adds (optional) checksums. */
819 case 3:
820 {
821 if (cParms == 5)
822 {
823 VBOXDNDCBSNDFILEDATADATA data;
824 RT_ZERO(data);
825 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA;
826
827 rc = paParms[0].getUInt32(&data.hdr.uContextID);
828 if (RT_SUCCESS(rc))
829 rc = paParms[1].getPointer((void**)&data.pvData, &data.cbData);
830 if (RT_SUCCESS(rc))
831 rc = paParms[2].getUInt32(&data.cbData);
832 if (RT_SUCCESS(rc))
833 rc = paParms[3].getPointer((void**)&data.u.v3.pvChecksum, &data.u.v3.cbChecksum);
834 if (RT_SUCCESS(rc))
835 rc = paParms[4].getUInt32(&data.u.v3.cbChecksum);
836
837 LogFlowFunc(("pvData=0x%p, cbData=%RU32\n", data.pvData, data.cbData));
838 DO_HOST_CALLBACK();
839 }
840 break;
841 }
842 /* Protocol v2 only sends the next data chunks to reduce traffic. */
843 case 2:
844 {
845 if (cParms == 3)
846 {
847 VBOXDNDCBSNDFILEDATADATA data;
848 RT_ZERO(data);
849 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA;
850 rc = paParms[0].getUInt32(&data.hdr.uContextID);
851 if (RT_SUCCESS(rc))
852 rc = paParms[1].getPointer((void**)&data.pvData, &data.cbData);
853 if (RT_SUCCESS(rc))
854 rc = paParms[2].getUInt32(&data.cbData);
855
856 LogFlowFunc(("cbData=%RU32, pvData=0x%p\n", data.cbData, data.pvData));
857 DO_HOST_CALLBACK();
858 }
859 break;
860 }
861 /* Protocol v1 sends the file path and attributes for every file chunk (!). */
862 default:
863 {
864 if (cParms == 5)
865 {
866 VBOXDNDCBSNDFILEDATADATA data;
867 RT_ZERO(data);
868 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA;
869 uint32_t cTmp;
870 rc = paParms[0].getPointer((void**)&data.u.v1.pszFilePath, &cTmp);
871 if (RT_SUCCESS(rc))
872 rc = paParms[1].getUInt32(&data.u.v1.cbFilePath);
873 if (RT_SUCCESS(rc))
874 rc = paParms[2].getPointer((void**)&data.pvData, &cTmp);
875 if (RT_SUCCESS(rc))
876 rc = paParms[3].getUInt32(&data.cbData);
877 if (RT_SUCCESS(rc))
878 rc = paParms[4].getUInt32(&data.u.v1.fMode);
879
880 LogFlowFunc(("pszFilePath=%s, cbData=%RU32, pvData=0x%p, fMode=0x%x\n",
881 data.u.v1.pszFilePath, data.cbData, data.pvData, data.u.v1.fMode));
882 DO_HOST_CALLBACK();
883 }
884 break;
885 }
886 }
887 break;
888 }
889 case GUEST_DND_GH_EVT_ERROR:
890 {
891 LogFlowFunc(("GUEST_DND_GH_EVT_ERROR\n"));
892
893 VBOXDNDCBEVTERRORDATA data;
894 RT_ZERO(data);
895 data.hdr.uMagic = CB_MAGIC_DND_GH_EVT_ERROR;
896
897 switch (pClient->GetProtocolVer())
898 {
899 case 3:
900 {
901 if (cParms == 2)
902 {
903 rc = paParms[0].getUInt32(&data.hdr.uContextID);
904 if (RT_SUCCESS(rc))
905 {
906 uint32_t rcOp;
907 rc = paParms[1].getUInt32(&rcOp);
908 if (RT_SUCCESS(rc))
909 data.rc = rcOp;
910 }
911 }
912 break;
913 }
914
915 case 2:
916 default:
917 {
918 if (cParms == 1)
919 {
920 uint32_t rcOp;
921 rc = paParms[0].getUInt32(&rcOp);
922 if (RT_SUCCESS(rc))
923 data.rc = (int32_t)rcOp;
924 }
925 break;
926 }
927 }
928
929 DO_HOST_CALLBACK();
930 break;
931 }
932#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
933
934 /*
935 * Note: This is a fire-and-forget message, as the host should
936 * not rely on an answer from the guest side in order to
937 * properly cancel the operation.
938 */
939 case HOST_DND_HG_EVT_CANCEL:
940 {
941 LogFlowFunc(("HOST_DND_HG_EVT_CANCEL\n"));
942
943 VBOXDNDCBEVTERRORDATA data;
944 RT_ZERO(data);
945 data.hdr.uMagic = CB_MAGIC_DND_GH_EVT_ERROR;
946
947 switch (pClient->GetProtocolVer())
948 {
949 case 3:
950 {
951 /* Protocol v3+ at least requires the context ID. */
952 if (cParms == 1)
953 rc = paParms[0].getUInt32(&data.hdr.uContextID);
954
955 break;
956 }
957
958 default:
959 break;
960 }
961
962 /* Tell the host that the guest has cancelled the operation. */
963 data.rc = VERR_CANCELLED;
964
965 DO_HOST_CALLBACK();
966
967 /* Note: If the host is not prepared for handling the cancelling reply
968 * from the guest, don't report this back to the guest. */
969 if (RT_FAILURE(rc))
970 rc = VINF_SUCCESS;
971 break;
972 }
973
974 default:
975 {
976 /* All other messages are handled by the DnD manager. */
977 rc = m_pManager->nextMessage(u32Function, cParms, paParms);
978 if (rc == VERR_NO_DATA) /* Manager has no new messsages? Try asking the host. */
979 {
980 if (m_SvcCtx.pfnHostCallback)
981 {
982 VBOXDNDCBHGGETNEXTHOSTMSGDATA data;
983 RT_ZERO(data);
984
985 data.hdr.uMagic = VBOX_DND_CB_MAGIC_MAKE(0 /* uFn */, 0 /* uVer */);
986
987 data.uMsg = u32Function;
988 data.cParms = cParms;
989 data.paParms = paParms;
990
991 rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function,
992 &data, sizeof(data));
993 if (RT_SUCCESS(rc))
994 {
995 cParms = data.cParms;
996 paParms = data.paParms;
997 }
998 else
999 {
1000 /*
1001 * In case the guest is too fast asking for the next message
1002 * and the host did not supply it yet, just defer the client's
1003 * return until a response from the host available.
1004 */
1005 LogFlowFunc(("No new messages from the host (yet), deferring request: %Rrc\n", rc));
1006 rc = VINF_HGCM_ASYNC_EXECUTE;
1007 }
1008 }
1009 else /* No host callback in place, so drag and drop is not supported by the host. */
1010 rc = VERR_NOT_SUPPORTED;
1011 }
1012 break;
1013 }
1014 }
1015 }
1016
1017 /*
1018 * If async execution is requested, we didn't notify the guest yet about
1019 * completion. The client is queued into the waiters list and will be
1020 * notified as soon as a new event is available.
1021 */
1022 if (rc == VINF_HGCM_ASYNC_EXECUTE)
1023 {
1024 try
1025 {
1026 AssertPtr(pClient);
1027 pClient->SetDeferred(callHandle, u32Function, cParms, paParms);
1028 m_clientQueue.push_back(u32ClientID);
1029 }
1030 catch (std::bad_alloc &)
1031 {
1032 rc = VERR_NO_MEMORY;
1033 /* Don't report to guest. */
1034 }
1035 }
1036 else if (pClient)
1037 pClient->Complete(callHandle, rc);
1038 else
1039 {
1040 AssertMsgFailed(("Guest call failed with %Rrc\n", rc));
1041 rc = VERR_NOT_IMPLEMENTED;
1042 }
1043
1044 LogFlowFunc(("Returning rc=%Rrc\n", rc));
1045}
1046
1047int DragAndDropService::hostCall(uint32_t u32Function,
1048 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1049{
1050 LogFlowFunc(("u32Function=%RU32, cParms=%RU32, cClients=%zu, cQueue=%zu\n",
1051 u32Function, cParms, m_clientMap.size(), m_clientQueue.size()));
1052
1053 int rc;
1054
1055 do
1056 {
1057 bool fSendToGuest = false; /* Whether to send the message down to the guest side or not. */
1058
1059 switch (u32Function)
1060 {
1061 case HOST_DND_SET_MODE:
1062 {
1063 if (cParms != 1)
1064 rc = VERR_INVALID_PARAMETER;
1065 else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT)
1066 rc = VERR_INVALID_PARAMETER;
1067 else
1068 rc = modeSet(paParms[0].u.uint32);
1069 break;
1070 }
1071
1072 case HOST_DND_HG_EVT_ENTER:
1073 {
1074 /* Clear the message queue as a new DnD operation just began. */
1075 m_pManager->clear();
1076
1077 fSendToGuest = true;
1078 rc = VINF_SUCCESS;
1079 break;
1080 }
1081
1082 case HOST_DND_HG_EVT_CANCEL:
1083 {
1084 LogFlowFunc(("Cancelling all waiting clients ...\n"));
1085
1086 /* Clear the message queue as the host cancelled the whole operation. */
1087 m_pManager->clear();
1088
1089 /*
1090 * Wake up all deferred clients and tell them to process
1091 * the cancelling message next.
1092 */
1093 DnDClientQueue::iterator itQueue = m_clientQueue.begin();
1094 while (itQueue != m_clientQueue.end())
1095 {
1096 DnDClientMap::iterator itClient = m_clientMap.find(*itQueue);
1097 Assert(itClient != m_clientMap.end());
1098
1099 DragAndDropClient *pClient = itClient->second;
1100 AssertPtr(pClient);
1101
1102 int rc2 = pClient->SetDeferredMsgInfo(HOST_DND_HG_EVT_CANCEL,
1103 /* Protocol v3+ also contains the context ID. */
1104 pClient->GetProtocolVer() >= 3 ? 1 : 0);
1105 pClient->CompleteDeferred(rc2);
1106
1107 m_clientQueue.erase(itQueue);
1108 itQueue = m_clientQueue.begin();
1109 }
1110
1111 Assert(m_clientQueue.empty());
1112
1113 /* Tell the host that everything went well. */
1114 rc = VINF_SUCCESS;
1115 break;
1116 }
1117
1118 default:
1119 {
1120 fSendToGuest = true;
1121 rc = VINF_SUCCESS;
1122 break;
1123 }
1124 }
1125
1126 if (fSendToGuest)
1127 {
1128 if (modeGet() == VBOX_DRAG_AND_DROP_MODE_OFF)
1129 {
1130 /* Tell the host that a wrong drag'n drop mode is set. */
1131 rc = VERR_ACCESS_DENIED;
1132 break;
1133 }
1134
1135 if (m_clientMap.empty()) /* At least one client on the guest connected? */
1136 {
1137 /*
1138 * Tell the host that the guest does not support drag'n drop.
1139 * This might happen due to not installed Guest Additions or
1140 * not running VBoxTray/VBoxClient.
1141 */
1142 rc = VERR_NOT_SUPPORTED;
1143 break;
1144 }
1145
1146 rc = m_pManager->addMessage(u32Function, cParms, paParms, true /* fAppend */);
1147 if (RT_FAILURE(rc))
1148 {
1149 AssertMsgFailed(("Adding new message of type=%RU32 failed with rc=%Rrc\n", u32Function, rc));
1150 break;
1151 }
1152
1153 /* Any clients in our queue ready for processing the next command? */
1154 if (m_clientQueue.empty())
1155 {
1156 LogFlowFunc(("All clients (%zu) busy -- delaying execution\n", m_clientMap.size()));
1157 break;
1158 }
1159
1160 uint32_t uClientNext = m_clientQueue.front();
1161 DnDClientMap::iterator itClientNext = m_clientMap.find(uClientNext);
1162 Assert(itClientNext != m_clientMap.end());
1163
1164 DragAndDropClient *pClient = itClientNext->second;
1165 AssertPtr(pClient);
1166
1167 /*
1168 * Check if this was a request for getting the next host
1169 * message. If so, return the message ID and the parameter
1170 * count. The message itself has to be queued.
1171 */
1172 uint32_t uMsgClient = pClient->GetMsgType();
1173
1174 uint32_t uMsgNext = 0;
1175 uint32_t cParmsNext = 0;
1176 int rcNext = m_pManager->nextMessageInfo(&uMsgNext, &cParmsNext);
1177
1178 LogFlowFunc(("uMsgClient=%RU32, uMsgNext=%RU32, cParmsNext=%RU32, rcNext=%Rrc\n",
1179 uMsgClient, uMsgNext, cParmsNext, rcNext));
1180
1181 if (RT_SUCCESS(rcNext))
1182 {
1183 if (uMsgClient == GUEST_DND_GET_NEXT_HOST_MSG)
1184 {
1185 rc = pClient->SetDeferredMsgInfo(uMsgNext, cParmsNext);
1186
1187 /* Note: Report the current rc back to the guest. */
1188 pClient->CompleteDeferred(rc);
1189 }
1190 /*
1191 * Does the message the client is waiting for match the message
1192 * next in the queue? Process it right away then.
1193 */
1194 else if (uMsgClient == uMsgNext)
1195 {
1196 rc = m_pManager->nextMessage(u32Function, cParms, paParms);
1197
1198 /* Note: Report the current rc back to the guest. */
1199 pClient->CompleteDeferred(rc);
1200 }
1201 else /* Should not happen; cancel the operation on the guest. */
1202 {
1203 LogFunc(("Client ID=%RU32 in wrong state with uMsg=%RU32 (next message in queue: %RU32), cancelling\n",
1204 pClient->GetClientID(), uMsgClient, uMsgNext));
1205
1206 pClient->CompleteDeferred(VERR_CANCELLED);
1207 }
1208
1209 m_clientQueue.pop_front();
1210 }
1211
1212 } /* fSendToGuest */
1213
1214 } while (0); /* To use breaks. */
1215
1216 LogFlowFuncLeaveRC(rc);
1217 return rc;
1218}
1219
1220DECLCALLBACK(int) DragAndDropService::progressCallback(uint32_t uStatus, uint32_t uPercentage, int rc, void *pvUser)
1221{
1222 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
1223
1224 DragAndDropService *pSelf = static_cast<DragAndDropService *>(pvUser);
1225 AssertPtr(pSelf);
1226
1227 if (pSelf->m_SvcCtx.pfnHostCallback)
1228 {
1229 LogFlowFunc(("GUEST_DND_HG_EVT_PROGRESS: uStatus=%RU32, uPercentage=%RU32, rc=%Rrc\n",
1230 uStatus, uPercentage, rc));
1231
1232 VBOXDNDCBHGEVTPROGRESSDATA data;
1233 data.hdr.uMagic = CB_MAGIC_DND_HG_EVT_PROGRESS;
1234 data.uPercentage = RT_MIN(uPercentage, 100);
1235 data.uStatus = uStatus;
1236 data.rc = rc; /** @todo uin32_t vs. int. */
1237
1238 return pSelf->m_SvcCtx.pfnHostCallback(pSelf->m_SvcCtx.pvHostData,
1239 GUEST_DND_HG_EVT_PROGRESS,
1240 &data, sizeof(data));
1241 }
1242
1243 return VINF_SUCCESS;
1244}
1245
1246/**
1247 * @copydoc VBOXHGCMSVCLOAD
1248 */
1249extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
1250{
1251 return DragAndDropService::svcLoad(pTable);
1252}
1253
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