VirtualBox

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

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

DnD: Added typedefs for DNDACTION and DNDACTIONLIST to emphasize usage, did some renaming to clean things up.

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