VirtualBox

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

Last change on this file since 72118 was 69500, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

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