VirtualBox

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

Last change on this file since 62517 was 62467, checked in by vboxsync, 8 years ago

HostServices: scm

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