VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestDnDImpl.cpp@ 51342

Last change on this file since 51342 was 51339, checked in by vboxsync, 11 years ago

Main,Frontends: use GetScreenResolution to get the guest screen origin.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 48.2 KB
Line 
1/* $Id: GuestDnDImpl.cpp 51339 2014-05-22 09:12:47Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation: Guest Drag and Drop parts
4 */
5
6/*
7 * Copyright (C) 2011-2014 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#include "GuestImpl.h"
19#include "AutoCaller.h"
20
21#ifdef VBOX_WITH_DRAG_AND_DROP
22# include "ConsoleImpl.h"
23# include "ProgressImpl.h"
24# include "GuestDnDImpl.h"
25
26# include <iprt/dir.h>
27# include <iprt/path.h>
28# include <iprt/stream.h>
29# include <iprt/semaphore.h>
30# include <iprt/cpp/utils.h>
31
32# include <VMMDev.h>
33
34# include <VBox/com/list.h>
35# include <VBox/GuestHost/DragAndDrop.h>
36# include <VBox/HostServices/DragAndDropSvc.h>
37
38# ifdef LOG_GROUP
39 # undef LOG_GROUP
40# endif
41# define LOG_GROUP LOG_GROUP_GUEST_DND
42# include <VBox/log.h>
43
44/* How does this work:
45 *
46 * Drag and Drop is handled over the internal HGCM service for the host <->
47 * guest communication. Beside that we need to map the Drag and Drop protocols
48 * of the various OS's we support to our internal channels, this is also highly
49 * communicative in both directions. Unfortunately HGCM isn't really designed
50 * for that. Next we have to foul some of the components. This includes to
51 * trick X11 on the guest side, but also Qt needs to be tricked on the host
52 * side a little bit.
53 *
54 * The following components are involved:
55 *
56 * 1. GUI: Uses the Qt classes for Drag and Drop and mainly forward the content
57 * of it to the Main IGuest interface (see UIDnDHandler.cpp).
58 * 2. Main: Public interface for doing Drag and Drop. Also manage the IProgress
59 * interfaces for blocking the caller by showing a progress dialog (see
60 * this file).
61 * 3. HGCM service: Handle all messages from the host to the guest at once and
62 * encapsulate the internal communication details (see dndmanager.cpp and
63 * friends).
64 * 4. Guest additions: Split into the platform neutral part (see
65 * VBoxGuestR3LibDragAndDrop.cpp) and the guest OS specific parts.
66 * Receive/send message from/to the HGCM service and does all guest specific
67 * operations. Currently only X11 is supported (see draganddrop.cpp within
68 * VBoxClient).
69 *
70 * Host -> Guest:
71 * 1. There are DnD Enter, Move, Leave events which are send exactly like this
72 * to the guest. The info includes the pos, mimetypes and allowed actions.
73 * The guest has to respond with an action it would accept, so the GUI could
74 * change the cursor.
75 * 2. On drop, first a drop event is send. If this is accepted a drop data
76 * event follows. This blocks the GUI and shows some progress indicator.
77 *
78 * Guest -> Host:
79 * 1. The GUI is asking the guest if a DnD event is pending when the user moves
80 * the cursor out of the view window. If so, this returns the mimetypes and
81 * allowed actions.
82 * (2. On every mouse move this is asked again, to make sure the DnD event is
83 * still valid.)
84 * 3. On drop the host request the data from the guest. This blocks the GUI and
85 * shows some progress indicator.
86 *
87 * Some hints:
88 * m_sstrAllowedMimeTypes here in this file defines the allowed mime-types.
89 * This is necessary because we need special handling for some of the
90 * mime-types. E.g. for URI lists we need to transfer the actual dirs and
91 * files. Text EOL may to be changed. Also unknown mime-types may need special
92 * handling as well, which may lead to undefined behavior in the host/guest, if
93 * not done.
94 *
95 * Dropping of a directory, means recursively transferring _all_ the content.
96 *
97 * Directories and files are placed into the user's temporary directory on the
98 * guest (e.g. /tmp/VirtualBox Dropped Files). We can't delete them after the
99 * DnD operation, because we didn't know what the DnD target does with it. E.g.
100 * it could just be opened in place. This could lead ofc to filling up the disk
101 * within the guest. To inform the user about this, a small app could be
102 * developed which scans this directory regularly and inform the user with a
103 * tray icon hint (and maybe the possibility to clean this up instantly). The
104 * same has to be done in the G->H direction when it is implemented.
105 *
106 * Of course only regularly files are supported. Symlinks are resolved and
107 * transfered as regularly files. First we don't know if the other side support
108 * symlinks at all and second they could point to somewhere in a directory tree
109 * which not exists on the other side.
110 *
111 * The code tries to preserve the file modes of the transfered dirs/files. This
112 * is useful (and maybe necessary) for two things:
113 * 1. If a file is executable, it should be also after the transfer, so the
114 * user can just execute it, without manually tweaking the modes first.
115 * 2. If a dir/file is not accessible by group/others in the host, it shouldn't
116 * be in the guest.
117 * In any case, the user mode is always set to rwx (so that we can access it
118 * ourself, in e.g. for a cleanup case after cancel).
119 *
120 * Cancel is supported in both directions and cleans up all previous steps
121 * (thats is: deleting already transfered dirs/files).
122 *
123 * In general I propose the following changes in the VBox HGCM infrastructure
124 * for the future:
125 * - Currently it isn't really possible to send messages to the guest from the
126 * host. The host informs the guest just that there is something, the guest
127 * than has to ask which message and depending on that send the appropriate
128 * message to the host, which is filled with the right data.
129 * - There is no generic interface for sending bigger memory blocks to/from the
130 * guest. This is now done here, but I guess was also necessary for e.g.
131 * guest execution. So something generic which brake this up into smaller
132 * blocks and send it would be nice (with all the error handling and such
133 * ofc).
134 * - I developed a "protocol" for the DnD communication here. So the host and
135 * the guest have always to match in the revision. This is ofc bad, because
136 * the additions could be outdated easily. So some generic protocol number
137 * support in HGCM for asking the host and the guest of the support version,
138 * would be nice. Ofc at least the host should be able to talk to the guest,
139 * even when the version is below the host one.
140 * All this stuff would be useful for the current services, but also for future
141 * onces.
142 *
143 * Todo:
144 * - Dragging out of the guest (partly done)
145 * - ESC doesn't really work (on Windows guests it's already implemented)
146 * - transfer of URIs (that is the files and patching of the data)
147 * - testing in a multi monitor setup
148 * ... in any case it seems a little bit difficult to handle from the Qt
149 * side. Maybe also a host specific implementation becomes necessary ...
150 * this would be really worst ofc.
151 * - Win guest support (maybe there have to be done a mapping between the
152 * official mime-types and Win Clipboard formats (see QWindowsMime, for an
153 * idea), for VBox internally only mime-types should be used)
154 * - EOL handling on text (should be shared with the clipboard code)
155 * - add configuration (GH, HG, Bidirectional, None), like for the clipboard
156 * - HG->GH and GH->HG-switch: Handle the case the user drags something out of
157 * the guest and than return to the source view (or another window in the
158 * multiple guest screen scenario).
159 * - add support for more mime-types (especially images, csv)
160 * - test unusual behavior:
161 * - DnD service crash in the guest during a DnD op (e.g. crash of VBoxClient or X11)
162 * - not expected order of the events between HGCM and the guest
163 * - Security considerations: We transfer a lot of memory between the guest and
164 * the host and even allow the creation of dirs/files. Maybe there should be
165 * limits introduced to preventing DOS attacks or filling up all the memory
166 * (both in the host and the guest).
167 * - test, test, test ...
168 */
169
170class DnDGuestResponse
171{
172
173public:
174
175 DnDGuestResponse(const ComObjPtr<Guest>& pGuest);
176
177 virtual ~DnDGuestResponse(void);
178
179public:
180
181 int notifyAboutGuestResponse(void);
182 int waitForGuestResponse(RTMSINTERVAL msTimeout = 500);
183
184 void setDefAction(uint32_t a) { m_defAction = a; }
185 uint32_t defAction(void) const { return m_defAction; }
186
187 void setAllActions(uint32_t a) { m_allActions = a; }
188 uint32_t allActions() const { return m_allActions; }
189
190 void setFormat(const Utf8Str &strFormat) { m_strFormat = strFormat; }
191 Utf8Str format(void) const { return m_strFormat; }
192
193 void setDropDir(const Utf8Str &strDropDir) { m_strDropDir = strDropDir; }
194 Utf8Str dropDir(void) const { return m_strDropDir; }
195
196 int dataAdd(const void *pvData, uint32_t cbData, uint32_t *pcbCurSize);
197 int dataSetStatus(size_t cbDataAdd, size_t cbDataTotal = 0);
198 void reset(void);
199 const void *data(void) { return m_pvData; }
200 size_t size(void) const { return m_cbData; }
201
202 int setProgress(unsigned uPercentage, uint32_t uState, int rcOp = VINF_SUCCESS);
203 HRESULT resetProgress(const ComObjPtr<Guest>& pParent);
204 HRESULT queryProgressTo(IProgress **ppProgress);
205
206 int writeToFile(const char *pszPath, size_t cbPath, void *pvData, size_t cbData, uint32_t fMode);
207
208public:
209
210 Utf8Str errorToString(const ComObjPtr<Guest>& pGuest, int guestRc);
211
212private:
213 RTSEMEVENT m_EventSem;
214 uint32_t m_defAction;
215 uint32_t m_allActions;
216 Utf8Str m_strFormat;
217
218 /** The actual MIME data.*/
219 void *m_pvData;
220 /** Size (in bytes) of MIME data. */
221 uint32_t m_cbData;
222
223 size_t m_cbDataCurrent;
224 size_t m_cbDataTotal;
225 /** Dropped files directory on the host. */
226 Utf8Str m_strDropDir;
227 /** The handle of the currently opened file being written to
228 * or read from. */
229 RTFILE m_hFile;
230 Utf8Str m_strFile;
231
232 ComObjPtr<Guest> m_parent;
233 ComObjPtr<Progress> m_progress;
234};
235
236/** @todo This class needs a major cleanup. Later. */
237class GuestDnDPrivate
238{
239private:
240
241 /** @todo Currently we only support one response. Maybe this needs to be extended at some time. */
242 GuestDnDPrivate(GuestDnD *q, const ComObjPtr<Guest>& pGuest)
243 : q_ptr(q)
244 , p(pGuest)
245 , m_pDnDResponse(new DnDGuestResponse(pGuest))
246 {}
247 virtual ~GuestDnDPrivate(void) { delete m_pDnDResponse; }
248
249 DnDGuestResponse *response(void) const { return m_pDnDResponse; }
250
251 HRESULT adjustCoords(ULONG uScreenId, ULONG *puX, ULONG *puY) const;
252 int hostCall(uint32_t u32Function, uint32_t cParms, PVBOXHGCMSVCPARM paParms) const;
253
254 /* Static helpers. */
255 static RTCString toFormatString(ComSafeArrayIn(IN_BSTR, formats));
256 static void toFormatSafeArray(const RTCString &strFormats, ComSafeArrayOut(BSTR, formats));
257
258 static DragAndDropAction_T toMainAction(uint32_t uAction);
259 static void toMainActions(uint32_t uActions, ComSafeArrayOut(DragAndDropAction_T, actions));
260 static uint32_t toHGCMAction(DragAndDropAction_T action);
261 static void toHGCMActions(DragAndDropAction_T inDefAction, uint32_t *pOutDefAction,
262 ComSafeArrayIn(DragAndDropAction_T, inAllowedActions),
263 uint32_t *pOutAllowedActions);
264
265 /* Private q and parent pointer */
266 GuestDnD *q_ptr;
267 ComObjPtr<Guest> p;
268
269 /* Private helper members. */
270 static const RTCList<RTCString> m_sstrAllowedMimeTypes;
271 DnDGuestResponse *m_pDnDResponse;
272
273 friend class GuestDnD;
274};
275
276/* What mime-types are supported by VirtualBox.
277 * Note: If you add something here, make sure you test it with all guest OS's!
278 ** @todo Make this MIME list configurable / extendable (by extra data?). Currently
279 * this is done hardcoded on every guest platform (POSIX/Windows).
280 */
281/* static */
282const RTCList<RTCString> GuestDnDPrivate::m_sstrAllowedMimeTypes = RTCList<RTCString>()
283 /* Uri's */
284 << "text/uri-list"
285 /* Text */
286 << "text/plain;charset=utf-8"
287 << "UTF8_STRING"
288 << "text/plain"
289 << "COMPOUND_TEXT"
290 << "TEXT"
291 << "STRING"
292 /* OpenOffice formates */
293 << "application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\""
294 << "application/x-openoffice-drawing;windows_formatname=\"Drawing Format\"";
295
296DnDGuestResponse::DnDGuestResponse(const ComObjPtr<Guest>& pGuest)
297 : m_EventSem(NIL_RTSEMEVENT)
298 , m_defAction(0)
299 , m_allActions(0)
300 , m_pvData(0)
301 , m_cbData(0)
302 , m_cbDataCurrent(0)
303 , m_cbDataTotal(0)
304 , m_hFile(NIL_RTFILE)
305 , m_parent(pGuest)
306{
307 int rc = RTSemEventCreate(&m_EventSem);
308 AssertRC(rc);
309}
310
311DnDGuestResponse::~DnDGuestResponse(void)
312{
313 reset();
314
315 int rc = RTSemEventDestroy(m_EventSem);
316 AssertRC(rc);
317}
318
319int DnDGuestResponse::dataAdd(const void *pvData, uint32_t cbData,
320 uint32_t *pcbCurSize)
321{
322 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
323 AssertReturn(cbData, VERR_INVALID_PARAMETER);
324 /* pcbCurSize is optional. */
325
326 int rc = VINF_SUCCESS;
327
328 /** @todo Make reallocation scheme a bit smarter here. */
329 m_pvData = RTMemRealloc(m_pvData, m_cbData + cbData);
330 if (m_pvData)
331 {
332 memcpy(&static_cast<uint8_t*>(m_pvData)[m_cbData],
333 pvData, cbData);
334 m_cbData += cbData;
335
336 if (pcbCurSize)
337 *pcbCurSize = m_cbData;
338 }
339 else
340 rc = VERR_NO_MEMORY;
341
342 return rc;
343}
344
345/* static */
346Utf8Str DnDGuestResponse::errorToString(const ComObjPtr<Guest>& pGuest, int guestRc)
347{
348 Utf8Str strError;
349
350 switch (guestRc)
351 {
352 case VERR_ACCESS_DENIED:
353 strError += Utf8StrFmt(pGuest->tr("For one or more guest files or directories selected for transferring to the host your guest "
354 "user does not have the appropriate access rights for. Please make sure that all selected "
355 "elements can be accessed and that your guest user has the appropriate rights."));
356 break;
357
358 case VERR_NOT_FOUND:
359 /* Should not happen due to file locking on the guest, but anyway ... */
360 strError += Utf8StrFmt(pGuest->tr("One or more guest files or directories selected for transferring to the host were not"
361 "found on the guest anymore. This can be the case if the guest files were moved and/or"
362 "altered while the drag'n drop operation was in progress."));
363 break;
364
365 case VERR_SHARING_VIOLATION:
366 strError += Utf8StrFmt(pGuest->tr("One or more guest files or directories selected for transferring to the host were locked. "
367 "Please make sure that all selected elements can be accessed and that your guest user has "
368 "the appropriate rights."));
369 break;
370
371 default:
372 strError += Utf8StrFmt("Drag'n drop guest error (%Rrc)", guestRc);
373 break;
374 }
375
376 return strError;
377}
378
379int DnDGuestResponse::notifyAboutGuestResponse(void)
380{
381 return RTSemEventSignal(m_EventSem);
382}
383
384void DnDGuestResponse::reset(void)
385{
386 LogFlowThisFuncEnter();
387
388 m_defAction = 0;
389 m_allActions = 0;
390
391 m_strDropDir = "";
392 m_strFormat = "";
393
394 if (m_pvData)
395 {
396 RTMemFree(m_pvData);
397 m_pvData = NULL;
398 }
399 m_cbData = 0;
400 m_cbDataCurrent = 0;
401 m_cbDataTotal = 0;
402
403 if (m_hFile != NIL_RTFILE)
404 {
405 RTFileClose(m_hFile);
406 m_hFile = NIL_RTFILE;
407 }
408 m_strFile = "";
409}
410
411HRESULT DnDGuestResponse::resetProgress(const ComObjPtr<Guest>& pParent)
412{
413 m_progress.setNull();
414 HRESULT rc = m_progress.createObject();
415 if (SUCCEEDED(rc))
416 {
417 rc = m_progress->init(static_cast<IGuest*>(pParent),
418 Bstr(pParent->tr("Dropping data")).raw(),
419 TRUE);
420 }
421 return rc;
422}
423
424int DnDGuestResponse::setProgress(unsigned uPercentage,
425 uint32_t uState, int rcOp /* = VINF_SUCCESS */)
426{
427 LogFlowFunc(("uPercentage=%RU32, uState=%RU32, rcOp=%Rrc\n",
428 uPercentage, uState, rcOp));
429
430 int vrc = VINF_SUCCESS;
431 if (!m_progress.isNull())
432 {
433 BOOL fCompleted;
434 HRESULT hr = m_progress->COMGETTER(Completed)(&fCompleted);
435 if (!fCompleted)
436 {
437 if (uState == DragAndDropSvc::DND_PROGRESS_ERROR)
438 {
439 hr = m_progress->i_notifyComplete(VBOX_E_IPRT_ERROR,
440 COM_IIDOF(IGuest),
441 m_parent->getComponentName(),
442 DnDGuestResponse::errorToString(m_parent, rcOp).c_str());
443 reset();
444 }
445 else if (uState == DragAndDropSvc::DND_PROGRESS_CANCELLED)
446 {
447 hr = m_progress->Cancel();
448 if (SUCCEEDED(hr))
449 vrc = VERR_CANCELLED;
450
451 reset();
452 }
453 else /* uState == DragAndDropSvc::DND_PROGRESS_RUNNING */
454 {
455 hr = m_progress->SetCurrentOperationProgress(uPercentage);
456 AssertComRC(hr);
457 if ( uState == DragAndDropSvc::DND_PROGRESS_COMPLETE
458 || uPercentage >= 100)
459 hr = m_progress->i_notifyComplete(S_OK);
460 }
461 }
462 }
463
464 return vrc;
465}
466
467int DnDGuestResponse::dataSetStatus(size_t cbDataAdd, size_t cbDataTotal /* = 0 */)
468{
469 if (cbDataTotal)
470 {
471#ifndef DEBUG_andy
472 AssertMsg(m_cbDataTotal <= cbDataTotal, ("New data size must not be smaller (%zu) than old value (%zu)\n",
473 cbDataTotal, m_cbDataTotal));
474#endif
475 LogFlowFunc(("Updating total data size from %zu to %zu\n", m_cbDataTotal, cbDataTotal));
476 m_cbDataTotal = cbDataTotal;
477 }
478 AssertMsg(m_cbDataTotal, ("m_cbDataTotal must not be <= 0\n"));
479
480 m_cbDataCurrent += cbDataAdd;
481 unsigned int cPercentage = RT_MIN(m_cbDataCurrent * 100.0 / m_cbDataTotal, 100);
482
483 /** @todo Don't use anonymous enums (uint32_t). */
484 uint32_t uStatus = DragAndDropSvc::DND_PROGRESS_RUNNING;
485 if (m_cbDataCurrent >= m_cbDataTotal)
486 uStatus = DragAndDropSvc::DND_PROGRESS_COMPLETE;
487
488#ifdef DEBUG_andy
489 LogFlowFunc(("Updating transfer status (%zu/%zu), status=%ld\n",
490 m_cbDataCurrent, m_cbDataTotal, uStatus));
491#else
492 AssertMsg(m_cbDataCurrent <= m_cbDataTotal,
493 ("More data transferred (%zu) than initially announced (%zu), cbDataAdd=%zu\n",
494 m_cbDataCurrent, m_cbDataTotal, cbDataAdd));
495#endif
496 int rc = setProgress(cPercentage, uStatus);
497
498 /** @todo For now we instantly confirm the cancel. Check if the
499 * guest should first clean up stuff itself and than really confirm
500 * the cancel request by an extra message. */
501 if (rc == VERR_CANCELLED)
502 rc = setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED);
503
504 LogFlowFuncLeaveRC(rc);
505 return rc;
506}
507
508HRESULT DnDGuestResponse::queryProgressTo(IProgress **ppProgress)
509{
510 return m_progress.queryInterfaceTo(ppProgress);
511}
512
513int DnDGuestResponse::waitForGuestResponse(RTMSINTERVAL msTimeout /*= 500 */)
514{
515 int rc = RTSemEventWait(m_EventSem, msTimeout);
516#ifdef DEBUG_andy
517 LogFlowFunc(("msTimeout=%RU32, rc=%Rrc\n", msTimeout, rc));
518#endif
519 return rc;
520}
521
522int DnDGuestResponse::writeToFile(const char *pszPath, size_t cbPath,
523 void *pvData, size_t cbData, uint32_t fMode)
524{
525 /** @todo Support locking more than one file at a time! We
526 * might want to have a table in DnDGuestImpl which
527 * keeps those file pointers around, or extend the
528 * actual protocol for explicit open calls.
529 *
530 * For now we only keep one file open at a time, so if
531 * a client does alternating writes to different files
532 * this function will close the old and re-open the new
533 * file on every call. */
534 int rc;
535 if ( m_hFile == NIL_RTFILE
536 || m_strFile != pszPath)
537 {
538 char *pszFile = RTPathJoinA(m_strDropDir.c_str(), pszPath);
539 if (pszFile)
540 {
541 RTFILE hFile;
542 /** @todo Respect fMode! */
543 rc = RTFileOpen(&hFile, pszFile,
544 RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE
545 | RTFILE_O_WRITE | RTFILE_O_APPEND);
546 if (RT_SUCCESS(rc))
547 {
548 LogFlowFunc(("Opening \"%s\" (fMode=0x%x) for writing ...\n",
549 pszFile, fMode));
550
551 m_hFile = hFile;
552 m_strFile = pszPath;
553 }
554
555 RTStrFree(pszFile);
556 }
557 else
558 rc = VERR_NO_MEMORY;
559 }
560 else
561 rc = VINF_SUCCESS;
562
563 if (RT_SUCCESS(rc))
564 {
565 rc = RTFileWrite(m_hFile, pvData, cbData,
566 NULL /* No partial writes */);
567
568 if (RT_SUCCESS(rc))
569 rc = dataSetStatus(cbData);
570 }
571
572 return rc;
573}
574
575///////////////////////////////////////////////////////////////////////////////
576
577HRESULT GuestDnDPrivate::adjustCoords(ULONG uScreenId, ULONG *puX, ULONG *puY) const
578{
579 /* For multi-monitor support we need to add shift values to the coordinates
580 * (depending on the screen number). */
581 ComPtr<IDisplay> pDisplay;
582 HRESULT hr = p->mParent->COMGETTER(Display)(pDisplay.asOutParam());
583 if (FAILED(hr))
584 return hr;
585
586 LONG xShift, yShift;
587 hr = pDisplay->GetScreenResolution(uScreenId, NULL, NULL, NULL,
588 &xShift, &yShift);
589 if (FAILED(hr))
590 return hr;
591
592 *puX += xShift;
593 *puY += yShift;
594
595 return hr;
596}
597
598int GuestDnDPrivate::hostCall(uint32_t u32Function, uint32_t cParms,
599 PVBOXHGCMSVCPARM paParms) const
600{
601 VMMDev *pVMMDev = NULL;
602 {
603 /* Make sure mParent is valid, so set the read lock while using.
604 * Do not keep this lock while doing the actual call, because in the meanwhile
605 * another thread could request a write lock which would be a bad idea ... */
606 AutoReadLock alock(p COMMA_LOCKVAL_SRC_POS);
607
608 /* Forward the information to the VMM device. */
609 AssertPtr(p->mParent);
610 pVMMDev = p->mParent->getVMMDev();
611 }
612
613 if (!pVMMDev)
614 throw p->setError(VBOX_E_VM_ERROR,
615 p->tr("VMM device is not available (is the VM running?)"));
616
617 LogFlowFunc(("hgcmHostCall uMsg=%RU32, cParms=%RU32\n", u32Function, cParms));
618 int rc = pVMMDev->hgcmHostCall("VBoxDragAndDropSvc",
619 u32Function,
620 cParms, paParms);
621 if (RT_FAILURE(rc))
622 {
623 LogFlowFunc(("hgcmHostCall failed with rc=%Rrc\n", rc));
624 throw p->setError(VBOX_E_IPRT_ERROR,
625 p->tr("hgcmHostCall failed (%Rrc)"), rc);
626 }
627
628 return rc;
629}
630
631/* static */
632RTCString GuestDnDPrivate::toFormatString(ComSafeArrayIn(IN_BSTR, formats))
633{
634 const RTCList<Utf8Str> formatList(ComSafeArrayInArg(formats));
635 RTCString strFormat;
636 for (size_t i = 0; i < formatList.size(); ++i)
637 {
638 const RTCString &f = formatList.at(i);
639 /* Only keep allowed format types. */
640 if (m_sstrAllowedMimeTypes.contains(f))
641 strFormat += f + "\r\n";
642 }
643 return strFormat;
644}
645
646/* static */
647void GuestDnDPrivate::toFormatSafeArray(const RTCString &strFormats, ComSafeArrayOut(BSTR, formats))
648{
649 RTCList<RTCString> list = strFormats.split("\r\n");
650 size_t i = 0;
651 while (i < list.size())
652 {
653 /* Only keep allowed format types. */
654 if (!m_sstrAllowedMimeTypes.contains(list.at(i)))
655 list.removeAt(i);
656 else
657 ++i;
658 }
659 /* Create a safe array out of the cleaned list. */
660 com::SafeArray<BSTR> sfaFormats(list.size());
661 for (i = 0; i < list.size(); ++i)
662 {
663 const RTCString &f = list.at(i);
664 if (m_sstrAllowedMimeTypes.contains(f))
665 {
666 Bstr bstr(f);
667 bstr.detachTo(&sfaFormats[i]);
668 }
669 }
670 sfaFormats.detachTo(ComSafeArrayOutArg(formats));
671}
672
673/* static */
674uint32_t GuestDnDPrivate::toHGCMAction(DragAndDropAction_T action)
675{
676 uint32_t a = DND_IGNORE_ACTION;
677 switch (action)
678 {
679 case DragAndDropAction_Copy: a = DND_COPY_ACTION; break;
680 case DragAndDropAction_Move: a = DND_MOVE_ACTION; break;
681 case DragAndDropAction_Link: /* For now it doesn't seems useful to allow a link
682 action between host & guest. Maybe later! */
683 case DragAndDropAction_Ignore: /* Ignored */ break;
684 default: AssertMsgFailed(("Action %u not recognized!\n", action)); break;
685 }
686 return a;
687}
688
689/* static */
690void GuestDnDPrivate::toHGCMActions(DragAndDropAction_T inDefAction,
691 uint32_t *pOutDefAction,
692 ComSafeArrayIn(DragAndDropAction_T, inAllowedActions),
693 uint32_t *pOutAllowedActions)
694{
695 const com::SafeArray<DragAndDropAction_T> sfaInActions(ComSafeArrayInArg(inAllowedActions));
696
697 /* Defaults */
698 *pOutDefAction = toHGCMAction(inDefAction);;
699 *pOutAllowedActions = DND_IGNORE_ACTION;
700
701 /* First convert the allowed actions to a bit array. */
702 for (size_t i = 0; i < sfaInActions.size(); ++i)
703 *pOutAllowedActions |= toHGCMAction(sfaInActions[i]);
704
705 /* Second check if the default action is a valid action and if not so try
706 * to find an allowed action. */
707 if (isDnDIgnoreAction(*pOutDefAction))
708 {
709 if (hasDnDCopyAction(*pOutAllowedActions))
710 *pOutDefAction = DND_COPY_ACTION;
711 else if (hasDnDMoveAction(*pOutAllowedActions))
712 *pOutDefAction = DND_MOVE_ACTION;
713 }
714}
715
716/* static */
717DragAndDropAction_T GuestDnDPrivate::toMainAction(uint32_t uAction)
718{
719 /* For now it doesn't seems useful to allow a
720 * link action between host & guest. Maybe later! */
721 return (isDnDCopyAction(uAction) ? (DragAndDropAction_T)DragAndDropAction_Copy :
722 isDnDMoveAction(uAction) ? (DragAndDropAction_T)DragAndDropAction_Move :
723 (DragAndDropAction_T)DragAndDropAction_Ignore);
724}
725
726/* static */
727void GuestDnDPrivate::toMainActions(uint32_t uActions,
728 ComSafeArrayOut(DragAndDropAction_T, actions))
729{
730 /* For now it doesn't seems useful to allow a
731 * link action between host & guest. Maybe later! */
732 RTCList<DragAndDropAction_T> list;
733 if (hasDnDCopyAction(uActions))
734 list.append(DragAndDropAction_Copy);
735 if (hasDnDMoveAction(uActions))
736 list.append(DragAndDropAction_Move);
737
738 com::SafeArray<DragAndDropAction_T> sfaActions(list.size());
739 for (size_t i = 0; i < list.size(); ++i)
740 sfaActions[i] = list.at(i);
741 sfaActions.detachTo(ComSafeArrayOutArg(actions));
742}
743
744GuestDnD::GuestDnD(const ComObjPtr<Guest>& pGuest)
745 : d_ptr(new GuestDnDPrivate(this, pGuest))
746{
747}
748
749GuestDnD::~GuestDnD(void)
750{
751 delete d_ptr;
752}
753
754HRESULT GuestDnD::dragHGEnter(ULONG uScreenId, ULONG uX, ULONG uY,
755 DragAndDropAction_T defaultAction,
756 ComSafeArrayIn(DragAndDropAction_T, allowedActions),
757 ComSafeArrayIn(IN_BSTR, formats),
758 DragAndDropAction_T *pResultAction)
759{
760 DPTR(GuestDnD);
761 const ComObjPtr<Guest> &p = d->p;
762
763 /* Default is ignoring */
764 *pResultAction = DragAndDropAction_Ignore;
765
766 /* Check & convert the drag & drop actions */
767 uint32_t uDefAction = 0;
768 uint32_t uAllowedActions = 0;
769 d->toHGCMActions(defaultAction, &uDefAction, ComSafeArrayInArg(allowedActions), &uAllowedActions);
770 /* If there is no usable action, ignore this request. */
771 if (isDnDIgnoreAction(uDefAction))
772 return S_OK;
773
774 /* Make a flat data string out of the mime-type list. */
775 RTCString strFormats = d->toFormatString(ComSafeArrayInArg(formats));
776 /* If there is no valid mime-type, ignore this request. */
777 if (strFormats.isEmpty())
778 return S_OK;
779
780 HRESULT hr = S_OK;
781
782 try
783 {
784 /* Adjust the coordinates in a multi-monitor setup. */
785 d->adjustCoords(uScreenId, &uX, &uY);
786
787 VBOXHGCMSVCPARM paParms[7];
788 int i = 0;
789 paParms[i++].setUInt32(uScreenId);
790 paParms[i++].setUInt32(uX);
791 paParms[i++].setUInt32(uY);
792 paParms[i++].setUInt32(uDefAction);
793 paParms[i++].setUInt32(uAllowedActions);
794 paParms[i++].setPointer((void*)strFormats.c_str(), strFormats.length() + 1);
795 paParms[i++].setUInt32(strFormats.length() + 1);
796
797 d->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_ENTER,
798 i,
799 paParms);
800
801 DnDGuestResponse *pResp = d->response();
802 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
803 return S_OK;
804
805 /* Copy the response info */
806 *pResultAction = d->toMainAction(pResp->defAction());
807 LogFlowFunc(("*pResultAction=%ld\n", *pResultAction));
808 }
809 catch (HRESULT hr2)
810 {
811 hr = hr2;
812 }
813
814 return hr;
815}
816
817HRESULT GuestDnD::dragHGMove(ULONG uScreenId, ULONG uX, ULONG uY,
818 DragAndDropAction_T defaultAction,
819 ComSafeArrayIn(DragAndDropAction_T, allowedActions),
820 ComSafeArrayIn(IN_BSTR, formats),
821 DragAndDropAction_T *pResultAction)
822{
823 DPTR(GuestDnD);
824 const ComObjPtr<Guest> &p = d->p;
825
826 /* Default is ignoring */
827 *pResultAction = DragAndDropAction_Ignore;
828
829 /* Check & convert the drag & drop actions */
830 uint32_t uDefAction = 0;
831 uint32_t uAllowedActions = 0;
832 d->toHGCMActions(defaultAction, &uDefAction, ComSafeArrayInArg(allowedActions), &uAllowedActions);
833 /* If there is no usable action, ignore this request. */
834 if (isDnDIgnoreAction(uDefAction))
835 return S_OK;
836
837 /* Make a flat data string out of the mime-type list. */
838 RTCString strFormats = d->toFormatString(ComSafeArrayInArg(formats));
839 /* If there is no valid mime-type, ignore this request. */
840 if (strFormats.isEmpty())
841 return S_OK;
842
843 HRESULT hr = S_OK;
844
845 try
846 {
847 /* Adjust the coordinates in a multi-monitor setup. */
848 d->adjustCoords(uScreenId, &uX, &uY);
849
850 VBOXHGCMSVCPARM paParms[7];
851 int i = 0;
852 paParms[i++].setUInt32(uScreenId);
853 paParms[i++].setUInt32(uX);
854 paParms[i++].setUInt32(uY);
855 paParms[i++].setUInt32(uDefAction);
856 paParms[i++].setUInt32(uAllowedActions);
857 paParms[i++].setPointer((void*)strFormats.c_str(), strFormats.length() + 1);
858 paParms[i++].setUInt32(strFormats.length() + 1);
859
860 d->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_MOVE,
861 i,
862 paParms);
863
864 DnDGuestResponse *pResp = d->response();
865 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
866 return S_OK;
867
868 /* Copy the response info */
869 *pResultAction = d->toMainAction(pResp->defAction());
870 LogFlowFunc(("*pResultAction=%ld\n", *pResultAction));
871 }
872 catch (HRESULT hr2)
873 {
874 hr = hr2;
875 }
876
877 return hr;
878}
879
880HRESULT GuestDnD::dragHGLeave(ULONG uScreenId)
881{
882 DPTR(GuestDnD);
883 const ComObjPtr<Guest> &p = d->p;
884
885 HRESULT hr = S_OK;
886
887 try
888 {
889 d->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_LEAVE,
890 0,
891 NULL);
892
893 DnDGuestResponse *pResp = d->response();
894 pResp->waitForGuestResponse();
895 }
896 catch (HRESULT hr2)
897 {
898 hr = hr2;
899 }
900
901 return hr;
902}
903
904HRESULT GuestDnD::dragHGDrop(ULONG uScreenId, ULONG uX, ULONG uY,
905 DragAndDropAction_T defaultAction,
906 ComSafeArrayIn(DragAndDropAction_T, allowedActions),
907 ComSafeArrayIn(IN_BSTR, formats),
908 BSTR *pstrFormat,
909 DragAndDropAction_T *pResultAction)
910{
911 DPTR(GuestDnD);
912 const ComObjPtr<Guest> &p = d->p;
913
914 /* Default is ignoring */
915 *pResultAction = DragAndDropAction_Ignore;
916
917 /* Check & convert the drag & drop actions */
918 uint32_t uDefAction = 0;
919 uint32_t uAllowedActions = 0;
920 d->toHGCMActions(defaultAction, &uDefAction, ComSafeArrayInArg(allowedActions), &uAllowedActions);
921 /* If there is no usable action, ignore this request. */
922 if (isDnDIgnoreAction(uDefAction))
923 return S_OK;
924
925 /* Make a flat data string out of the mime-type list. */
926 RTCString strFormats = d->toFormatString(ComSafeArrayInArg(formats));
927 /* If there is no valid mime-type, ignore this request. */
928 if (strFormats.isEmpty())
929 return S_OK;
930
931 HRESULT hr = S_OK;
932
933 try
934 {
935 /* Adjust the coordinates in a multi-monitor setup. */
936 d->adjustCoords(uScreenId, &uX, &uY);
937
938 VBOXHGCMSVCPARM paParms[7];
939 int i = 0;
940 paParms[i++].setUInt32(uScreenId);
941 paParms[i++].setUInt32(uX);
942 paParms[i++].setUInt32(uY);
943 paParms[i++].setUInt32(uDefAction);
944 paParms[i++].setUInt32(uAllowedActions);
945 paParms[i++].setPointer((void*)strFormats.c_str(), strFormats.length() + 1);
946 paParms[i++].setUInt32(strFormats.length() + 1);
947
948 d->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_DROPPED,
949 i,
950 paParms);
951
952 DnDGuestResponse *pResp = d->response();
953 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
954 return S_OK;
955
956 /* Get the resulting action from the guest. */
957 *pResultAction = d->toMainAction(pResp->defAction());
958
959 LogFlowFunc(("resFormat=%s, resAction=%RU32\n",
960 pResp->format().c_str(), pResp->defAction()));
961
962 Bstr(pResp->format()).cloneTo(pstrFormat);
963 }
964 catch (HRESULT hr2)
965 {
966 hr = hr2;
967 }
968
969 return hr;
970}
971
972HRESULT GuestDnD::dragHGPutData(ULONG uScreenId, IN_BSTR bstrFormat,
973 ComSafeArrayIn(BYTE, data), IProgress **ppProgress)
974{
975 DPTR(GuestDnD);
976 const ComObjPtr<Guest> &p = d->p;
977
978 HRESULT hr = S_OK;
979
980 try
981 {
982 Utf8Str strFormat(bstrFormat);
983 com::SafeArray<BYTE> sfaData(ComSafeArrayInArg(data));
984
985 VBOXHGCMSVCPARM paParms[5];
986 int i = 0;
987 paParms[i++].setUInt32(uScreenId);
988 paParms[i++].setPointer((void*)strFormat.c_str(), (uint32_t)strFormat.length() + 1);
989 paParms[i++].setUInt32((uint32_t)strFormat.length() + 1);
990 paParms[i++].setPointer((void*)sfaData.raw(), (uint32_t)sfaData.size());
991 paParms[i++].setUInt32((uint32_t)sfaData.size());
992
993 DnDGuestResponse *pResp = d->response();
994 /* Reset any old progress status. */
995 pResp->resetProgress(p);
996
997 /* Note: The actual data transfer of files/directoies is performed by the
998 * DnD host service. */
999 d->hostCall(DragAndDropSvc::HOST_DND_HG_SND_DATA,
1000 i,
1001 paParms);
1002
1003 /* Query the progress object to the caller. */
1004 pResp->queryProgressTo(ppProgress);
1005 }
1006 catch (HRESULT hr2)
1007 {
1008 hr = hr2;
1009 }
1010
1011 return hr;
1012}
1013
1014#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1015HRESULT GuestDnD::dragGHPending(ULONG uScreenId,
1016 ComSafeArrayOut(BSTR, formats),
1017 ComSafeArrayOut(DragAndDropAction_T, allowedActions),
1018 DragAndDropAction_T *pDefaultAction)
1019{
1020 DPTR(GuestDnD);
1021 const ComObjPtr<Guest> &p = d->p;
1022
1023 /* Default is ignoring */
1024 *pDefaultAction = DragAndDropAction_Ignore;
1025
1026 HRESULT hr = S_OK;
1027
1028 try
1029 {
1030 VBOXHGCMSVCPARM paParms[1];
1031 int i = 0;
1032 paParms[i++].setUInt32(uScreenId);
1033
1034 d->hostCall(DragAndDropSvc::HOST_DND_GH_REQ_PENDING,
1035 i,
1036 paParms);
1037
1038 DnDGuestResponse *pResp = d->response();
1039 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
1040 return S_OK;
1041
1042 if (isDnDIgnoreAction(pResp->defAction()))
1043 return S_OK;
1044
1045 /* Fetch the default action to use. */
1046 *pDefaultAction = d->toMainAction(pResp->defAction());
1047 d->toFormatSafeArray(pResp->format(), ComSafeArrayOutArg(formats));
1048 d->toMainActions(pResp->allActions(), ComSafeArrayOutArg(allowedActions));
1049
1050 LogFlowFunc(("*pDefaultAction=0x%x\n", *pDefaultAction));
1051 }
1052 catch (HRESULT hr2)
1053 {
1054 hr = hr2;
1055 }
1056
1057 return hr;
1058}
1059
1060HRESULT GuestDnD::dragGHDropped(IN_BSTR bstrFormat, DragAndDropAction_T action,
1061 IProgress **ppProgress)
1062{
1063 DPTR(GuestDnD);
1064 const ComObjPtr<Guest> &p = d->p;
1065
1066 Utf8Str strFormat(bstrFormat);
1067 HRESULT hr = S_OK;
1068
1069 uint32_t uAction = d->toHGCMAction(action);
1070 /* If there is no usable action, ignore this request. */
1071 if (isDnDIgnoreAction(uAction))
1072 return S_OK;
1073
1074 const char *pcszFormat = strFormat.c_str();
1075 bool fNeedsDropDir = DnDMIMENeedsDropDir(pcszFormat, strlen(pcszFormat));
1076 LogFlowFunc(("strFormat=%s, uAction=0x%x, fNeedsDropDir=%RTbool\n",
1077 pcszFormat, uAction, fNeedsDropDir));
1078
1079 DnDGuestResponse *pResp = d->response();
1080 AssertPtr(pResp);
1081
1082 /* Reset any old data. */
1083 pResp->reset();
1084 pResp->resetProgress(p);
1085
1086 /* Set the format we are going to retrieve to have it around
1087 * when retrieving the data later. */
1088 pResp->setFormat(strFormat);
1089
1090 if (fNeedsDropDir)
1091 {
1092 char szDropDir[RTPATH_MAX];
1093 int rc = DnDDirCreateDroppedFiles(szDropDir, sizeof(szDropDir));
1094 if (RT_FAILURE(rc))
1095 return p->setError(VBOX_E_IPRT_ERROR,
1096 p->tr("Unable to create the temporary drag'n drop directory \"%s\" (%Rrc)\n"),
1097 szDropDir, rc);
1098 LogFlowFunc(("Dropped files directory on the host is: %s\n", szDropDir));
1099
1100 pResp->setDropDir(szDropDir);
1101 }
1102
1103 try
1104 {
1105 VBOXHGCMSVCPARM paParms[3];
1106 int i = 0;
1107 paParms[i++].setPointer((void*)strFormat.c_str(), (uint32_t)strFormat.length() + 1);
1108 paParms[i++].setUInt32((uint32_t)strFormat.length() + 1);
1109 paParms[i++].setUInt32(uAction);
1110
1111 d->hostCall(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED,
1112 i,
1113 paParms);
1114
1115 /* Query the progress object to the caller. */
1116 pResp->queryProgressTo(ppProgress);
1117 }
1118 catch (HRESULT hr2)
1119 {
1120 hr = hr2;
1121 }
1122
1123 return hr;
1124}
1125
1126HRESULT GuestDnD::dragGHGetData(ComSafeArrayOut(BYTE, data))
1127{
1128 DPTR(GuestDnD);
1129 const ComObjPtr<Guest> &p = d->p;
1130
1131 HRESULT hr = S_OK;
1132
1133 DnDGuestResponse *pResp = d->response();
1134 if (pResp)
1135 {
1136 com::SafeArray<BYTE> sfaData;
1137
1138 size_t cbData = pResp->size();
1139 if (cbData)
1140 {
1141 const void *pvData = pResp->data();
1142 AssertPtr(pvData);
1143
1144 Utf8Str strFormat = pResp->format();
1145 LogFlowFunc(("strFormat=%s, cbData=%zu, pvData=0x%p\n",
1146 strFormat.c_str(), cbData, pvData));
1147
1148 if (DnDMIMEHasFileURLs(strFormat.c_str(), strFormat.length()))
1149 {
1150 LogFlowFunc(("strDropDir=%s\n", pResp->dropDir().c_str()));
1151
1152 DnDURIList lstURI;
1153 int rc2 = lstURI.RootFromURIData(pvData, cbData, 0 /* fFlags */);
1154 if (RT_SUCCESS(rc2))
1155 {
1156 Utf8Str strURIs = lstURI.RootToString(pResp->dropDir());
1157 size_t cbURIs = strURIs.length();
1158 if (sfaData.resize(cbURIs + 1 /* Include termination */))
1159 memcpy(sfaData.raw(), strURIs.c_str(), cbURIs);
1160 else
1161 hr = E_OUTOFMEMORY;
1162 }
1163 else
1164 hr = VBOX_E_IPRT_ERROR;
1165
1166 LogFlowFunc(("Found %zu root URIs, rc=%Rrc\n", lstURI.RootCount(), rc2));
1167 }
1168 else
1169 {
1170 /* Copy the data into a safe array of bytes. */
1171 if (sfaData.resize(cbData))
1172 memcpy(sfaData.raw(), pvData, cbData);
1173 else
1174 hr = E_OUTOFMEMORY;
1175 }
1176 }
1177
1178 /* Detach in any case, regardless of data size. */
1179 sfaData.detachTo(ComSafeArrayOutArg(data));
1180
1181 /* Delete the data. */
1182 pResp->reset();
1183 }
1184 else
1185 hr = VBOX_E_INVALID_OBJECT_STATE;
1186
1187 LogFlowFunc(("Returning hr=%Rhrc\n", hr));
1188 return hr;
1189}
1190
1191int GuestDnD::onGHSendData(DnDGuestResponse *pResp,
1192 const void *pvData, size_t cbData,
1193 size_t cbTotalSize)
1194{
1195 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
1196 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1197 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1198 AssertReturn(cbTotalSize, VERR_INVALID_PARAMETER);
1199
1200 int rc = pResp->dataAdd(pvData, cbData, NULL /* Current size */);
1201 if (RT_SUCCESS(rc))
1202 rc = pResp->dataSetStatus(cbData, cbTotalSize);
1203
1204 LogFlowFuncLeaveRC(rc);
1205 return rc;
1206}
1207
1208int GuestDnD::onGHSendDir(DnDGuestResponse *pResp,
1209 const char *pszPath, size_t cbPath,
1210 uint32_t fMode)
1211{
1212 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
1213 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1214 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
1215
1216 LogFlowFunc(("pszPath=%s, cbPath=%zu, fMode=0x%x\n",
1217 pszPath, cbPath, fMode));
1218
1219 int rc;
1220 char *pszDir = RTPathJoinA(pResp->dropDir().c_str(), pszPath);
1221 if (pszDir)
1222 {
1223 rc = RTDirCreateFullPath(pszDir, fMode);
1224 RTStrFree(pszDir);
1225 }
1226 else
1227 rc = VERR_NO_MEMORY;
1228
1229 if (RT_SUCCESS(rc))
1230 rc = pResp->dataSetStatus(cbPath);
1231
1232 LogFlowFuncLeaveRC(rc);
1233 return rc;
1234}
1235
1236int GuestDnD::onGHSendFile(DnDGuestResponse *pResp,
1237 const char *pszPath, size_t cbPath,
1238 void *pvData, size_t cbData, uint32_t fMode)
1239{
1240 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
1241 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1242 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
1243
1244 LogFlowFunc(("pszPath=%s, cbPath=%zu, fMode=0x%x\n",
1245 pszPath, cbPath, fMode));
1246
1247 int rc = pResp->writeToFile(pszPath, cbPath, pvData, cbData, fMode);
1248
1249 LogFlowFuncLeaveRC(rc);
1250 return rc;
1251}
1252#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1253
1254/* static */
1255DECLCALLBACK(int) GuestDnD::notifyGuestDragAndDropEvent(void *pvExtension, uint32_t u32Function,
1256 void *pvParms, uint32_t cbParms)
1257{
1258 LogFlowFunc(("pvExtension=%p, u32Function=%RU32, pvParms=%p, cbParms=%RU32\n",
1259 pvExtension, u32Function, pvParms, cbParms));
1260
1261 ComObjPtr<Guest> pGuest = reinterpret_cast<Guest*>(pvExtension);
1262 if (!pGuest->m_pGuestDnD)
1263 return VINF_SUCCESS;
1264
1265 GuestDnD *pGuestDnD = pGuest->m_pGuestDnD;
1266 AssertPtr(pGuestDnD);
1267
1268 GuestDnDPrivate *d = static_cast<GuestDnDPrivate*>(pGuest->m_pGuestDnD->d_ptr);
1269 const ComObjPtr<Guest> &p = d->p;
1270
1271 DnDGuestResponse *pResp = d->response();
1272 if (pResp == NULL)
1273 return VERR_INVALID_PARAMETER;
1274
1275 int rc = VINF_SUCCESS;
1276 switch (u32Function)
1277 {
1278 case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
1279 {
1280 DragAndDropSvc::PVBOXDNDCBHGACKOPDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGACKOPDATA>(pvParms);
1281 AssertPtr(pCBData);
1282 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGACKOPDATA) == cbParms, VERR_INVALID_PARAMETER);
1283 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1284
1285 pResp->setDefAction(pCBData->uAction);
1286
1287 rc = pResp->notifyAboutGuestResponse();
1288 break;
1289 }
1290
1291 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
1292 {
1293 DragAndDropSvc::PVBOXDNDCBHGREQDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGREQDATADATA>(pvParms);
1294 AssertPtr(pCBData);
1295 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGREQDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1296 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1297
1298 pResp->setFormat(pCBData->pszFormat);
1299
1300 rc = pResp->notifyAboutGuestResponse();
1301 break;
1302 }
1303
1304 case DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS:
1305 {
1306 DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA pCBData =
1307 reinterpret_cast <DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA>(pvParms);
1308 AssertPtr(pCBData);
1309 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGEVTPROGRESSDATA) == cbParms, VERR_INVALID_PARAMETER);
1310 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_EVT_PROGRESS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1311
1312 rc = pResp->setProgress(pCBData->uPercentage, pCBData->uState, pCBData->rc);
1313 break;
1314 }
1315
1316#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1317 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
1318 {
1319 DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA pCBData =
1320 reinterpret_cast <DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA>(pvParms);
1321 AssertPtr(pCBData);
1322 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA) == cbParms, VERR_INVALID_PARAMETER);
1323 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1324
1325 pResp->setFormat(pCBData->pszFormat);
1326 pResp->setDefAction(pCBData->uDefAction);
1327 pResp->setAllActions(pCBData->uAllActions);
1328
1329 rc = pResp->notifyAboutGuestResponse();
1330 break;
1331 }
1332
1333 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
1334 {
1335 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
1336 AssertPtr(pCBData);
1337 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1338 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1339
1340 rc = pGuestDnD->onGHSendData(pResp, pCBData->pvData, pCBData->cbData,
1341 pCBData->cbTotalSize);
1342 break;
1343 }
1344
1345 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
1346 {
1347 DragAndDropSvc::PVBOXDNDCBSNDDIRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDIRDATA>(pvParms);
1348 AssertPtr(pCBData);
1349 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDIRDATA) == cbParms, VERR_INVALID_PARAMETER);
1350 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1351
1352 rc = pGuestDnD->onGHSendDir(pResp, pCBData->pszPath, pCBData->cbPath, pCBData->fMode);
1353 break;
1354 }
1355
1356 case DragAndDropSvc::GUEST_DND_GH_SND_FILE:
1357 {
1358 DragAndDropSvc::PVBOXDNDCBSNDFILEDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEDATA>(pvParms);
1359 AssertPtr(pCBData);
1360 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEDATA) == cbParms, VERR_INVALID_PARAMETER);
1361 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1362
1363 rc = pGuestDnD->onGHSendFile(pResp, pCBData->pszFilePath, pCBData->cbFilePath,
1364 pCBData->pvData, pCBData->cbData, pCBData->fMode);
1365 break;
1366 }
1367
1368 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
1369 {
1370 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
1371 AssertPtr(pCBData);
1372 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
1373 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1374
1375 /* Cleanup. */
1376 pResp->reset();
1377 rc = pResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc);
1378 break;
1379 }
1380#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1381 default:
1382 rc = VERR_NOT_SUPPORTED; /* Tell the guest. */
1383 break;
1384 }
1385
1386 LogFlowFunc(("Returning rc=%Rrc\n", rc));
1387 return rc;
1388}
1389#endif /* VBOX_WITH_DRAG_AND_DROP */
1390
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