VirtualBox

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

Last change on this file since 50782 was 50734, checked in by vboxsync, 11 years ago

DnD: Bugfixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 48.1 KB
Line 
1/* $Id: GuestDnDImpl.cpp 50734 2014-03-10 13:54:03Z 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, ComSafeArrayIn(DragAndDropAction_T, inAllowedActions), uint32_t *pOutAllowedActions);
262
263 /* Private q and parent pointer */
264 GuestDnD *q_ptr;
265 ComObjPtr<Guest> p;
266
267 /* Private helper members. */
268 static const RTCList<RTCString> m_sstrAllowedMimeTypes;
269 DnDGuestResponse *m_pDnDResponse;
270
271 friend class GuestDnD;
272};
273
274/* What mime-types are supported by VirtualBox.
275 * Note: If you add something here, make sure you test it with all guest OS's!
276 ** @todo Make this MIME list configurable / extendable (by extra data?). Currently
277 * this is done hardcoded on every guest platform (POSIX/Windows).
278 */
279/* static */
280const RTCList<RTCString> GuestDnDPrivate::m_sstrAllowedMimeTypes = RTCList<RTCString>()
281 /* Uri's */
282 << "text/uri-list"
283 /* Text */
284 << "text/plain;charset=utf-8"
285 << "UTF8_STRING"
286 << "text/plain"
287 << "COMPOUND_TEXT"
288 << "TEXT"
289 << "STRING"
290 /* OpenOffice formates */
291 << "application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\""
292 << "application/x-openoffice-drawing;windows_formatname=\"Drawing Format\"";
293
294DnDGuestResponse::DnDGuestResponse(const ComObjPtr<Guest>& pGuest)
295 : m_EventSem(NIL_RTSEMEVENT)
296 , m_defAction(0)
297 , m_allActions(0)
298 , m_pvData(0)
299 , m_cbData(0)
300 , m_cbDataCurrent(0)
301 , m_cbDataTotal(0)
302 , m_hFile(NIL_RTFILE)
303 , m_parent(pGuest)
304{
305 int rc = RTSemEventCreate(&m_EventSem);
306 AssertRC(rc);
307}
308
309DnDGuestResponse::~DnDGuestResponse(void)
310{
311 reset();
312
313 int rc = RTSemEventDestroy(m_EventSem);
314 AssertRC(rc);
315}
316
317int DnDGuestResponse::dataAdd(const void *pvData, uint32_t cbData,
318 uint32_t *pcbCurSize)
319{
320 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
321 AssertReturn(cbData, VERR_INVALID_PARAMETER);
322 /* pcbCurSize is optional. */
323
324 int rc = VINF_SUCCESS;
325
326 /** @todo Make reallocation scheme a bit smarter here. */
327 m_pvData = RTMemRealloc(m_pvData, m_cbData + cbData);
328 if (m_pvData)
329 {
330 memcpy(&static_cast<uint8_t*>(m_pvData)[m_cbData],
331 pvData, cbData);
332 m_cbData += cbData;
333
334 if (pcbCurSize)
335 *pcbCurSize = m_cbData;
336 }
337 else
338 rc = VERR_NO_MEMORY;
339
340 return rc;
341}
342
343/* static */
344Utf8Str DnDGuestResponse::errorToString(const ComObjPtr<Guest>& pGuest, int guestRc)
345{
346 Utf8Str strError;
347
348 switch (guestRc)
349 {
350 case VERR_ACCESS_DENIED:
351 strError += Utf8StrFmt(pGuest->tr("For one or more guest files or directories selected for transferring to the host your guest "
352 "user does not have the appropriate access rights for. Please make sure that all selected "
353 "elements can be accessed and that your guest user has the appropriate rights."));
354 break;
355
356 case VERR_NOT_FOUND:
357 /* Should not happen due to file locking on the guest, but anyway ... */
358 strError += Utf8StrFmt(pGuest->tr("One or more guest files or directories selected for transferring to the host were not"
359 "found on the guest anymore. This can be the case if the guest files were moved and/or"
360 "altered while the drag'n drop operation was in progress."));
361 break;
362
363 case VERR_SHARING_VIOLATION:
364 strError += Utf8StrFmt(pGuest->tr("One or more guest files or directories selected for transferring to the host were locked. "
365 "Please make sure that all selected elements can be accessed and that your guest user has "
366 "the appropriate rights."));
367 break;
368
369 default:
370 strError += Utf8StrFmt("Drag'n drop guest error (%Rrc)", guestRc);
371 break;
372 }
373
374 return strError;
375}
376
377int DnDGuestResponse::notifyAboutGuestResponse(void)
378{
379 return RTSemEventSignal(m_EventSem);
380}
381
382void DnDGuestResponse::reset(void)
383{
384 LogFlowThisFuncEnter();
385
386 m_defAction = 0;
387 m_allActions = 0;
388
389 m_strDropDir = "";
390 m_strFormat = "";
391
392 if (m_pvData)
393 {
394 RTMemFree(m_pvData);
395 m_pvData = NULL;
396 }
397 m_cbData = 0;
398 m_cbDataCurrent = 0;
399 m_cbDataTotal = 0;
400
401 if (m_hFile != NIL_RTFILE)
402 {
403 RTFileClose(m_hFile);
404 m_hFile = NIL_RTFILE;
405 }
406 m_strFile = "";
407}
408
409HRESULT DnDGuestResponse::resetProgress(const ComObjPtr<Guest>& pParent)
410{
411 m_progress.setNull();
412 HRESULT rc = m_progress.createObject();
413 if (SUCCEEDED(rc))
414 {
415 rc = m_progress->init(static_cast<IGuest*>(pParent),
416 Bstr(pParent->tr("Dropping data")).raw(),
417 TRUE);
418 }
419 return rc;
420}
421
422int DnDGuestResponse::setProgress(unsigned uPercentage,
423 uint32_t uState, int rcOp /* = VINF_SUCCESS */)
424{
425 LogFlowFunc(("uPercentage=%RU32, uState=%RU32, rcOp=%Rrc\n",
426 uPercentage, uState, rcOp));
427
428 int vrc = VINF_SUCCESS;
429 if (!m_progress.isNull())
430 {
431 BOOL fCompleted;
432 HRESULT hr = m_progress->COMGETTER(Completed)(&fCompleted);
433 if (!fCompleted)
434 {
435 if (uState == DragAndDropSvc::DND_PROGRESS_ERROR)
436 {
437 hr = m_progress->notifyComplete(VBOX_E_IPRT_ERROR,
438 COM_IIDOF(IGuest),
439 m_parent->getComponentName(),
440 DnDGuestResponse::errorToString(m_parent, rcOp).c_str());
441 reset();
442 }
443 else if (uState == DragAndDropSvc::DND_PROGRESS_CANCELLED)
444 {
445 hr = m_progress->Cancel();
446 if (SUCCEEDED(hr))
447 vrc = VERR_CANCELLED;
448
449 reset();
450 }
451 else /* uState == DragAndDropSvc::DND_PROGRESS_RUNNING */
452 {
453 hr = m_progress->SetCurrentOperationProgress(uPercentage);
454 AssertComRC(hr);
455 if ( uState == DragAndDropSvc::DND_PROGRESS_COMPLETE
456 || uPercentage >= 100)
457 hr = m_progress->notifyComplete(S_OK);
458 }
459 }
460 }
461
462 return vrc;
463}
464
465int DnDGuestResponse::dataSetStatus(size_t cbDataAdd, size_t cbDataTotal /* = 0 */)
466{
467 if (cbDataTotal)
468 {
469#ifndef DEBUG_andy
470 AssertMsg(m_cbDataTotal <= cbDataTotal, ("New data size must not be smaller (%zu) than old value (%zu)\n",
471 cbDataTotal, m_cbDataTotal));
472#endif
473 LogFlowFunc(("Updating total data size from %zu to %zu\n", m_cbDataTotal, cbDataTotal));
474 m_cbDataTotal = cbDataTotal;
475 }
476 AssertMsg(m_cbDataTotal, ("m_cbDataTotal must not be <= 0\n"));
477
478 m_cbDataCurrent += cbDataAdd;
479 unsigned int cPercentage = RT_MIN(m_cbDataCurrent * 100.0 / m_cbDataTotal, 100);
480
481 /** @todo Don't use anonymous enums (uint32_t). */
482 uint32_t uStatus = DragAndDropSvc::DND_PROGRESS_RUNNING;
483 if (m_cbDataCurrent >= m_cbDataTotal)
484 uStatus = DragAndDropSvc::DND_PROGRESS_COMPLETE;
485
486#ifdef DEBUG_andy
487 LogFlowFunc(("Updating transfer status (%zu/%zu), status=%ld\n",
488 m_cbDataCurrent, m_cbDataTotal, uStatus));
489#else
490 AssertMsg(m_cbDataCurrent <= m_cbDataTotal,
491 ("More data transferred (%zu) than initially announced (%zu), cbDataAdd=%zu\n",
492 m_cbDataCurrent, m_cbDataTotal, cbDataAdd));
493#endif
494 int rc = setProgress(cPercentage, uStatus);
495
496 /** @todo For now we instantly confirm the cancel. Check if the
497 * guest should first clean up stuff itself and than really confirm
498 * the cancel request by an extra message. */
499 if (rc == VERR_CANCELLED)
500 rc = setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED);
501
502 LogFlowFuncLeaveRC(rc);
503 return rc;
504}
505
506HRESULT DnDGuestResponse::queryProgressTo(IProgress **ppProgress)
507{
508 return m_progress.queryInterfaceTo(ppProgress);
509}
510
511int DnDGuestResponse::waitForGuestResponse(RTMSINTERVAL msTimeout /*= 500 */)
512{
513 int rc = RTSemEventWait(m_EventSem, msTimeout);
514#ifdef DEBUG_andy
515 LogFlowFunc(("msTimeout=%RU32, rc=%Rrc\n", msTimeout, rc));
516#endif
517 return rc;
518}
519
520int DnDGuestResponse::writeToFile(const char *pszPath, size_t cbPath,
521 void *pvData, size_t cbData, uint32_t fMode)
522{
523 /** @todo Support locking more than one file at a time! We
524 * might want to have a table in DnDGuestImpl which
525 * keeps those file pointers around, or extend the
526 * actual protocol for explicit open calls.
527 *
528 * For now we only keep one file open at a time, so if
529 * a client does alternating writes to different files
530 * this function will close the old and re-open the new
531 * file on every call. */
532 int rc;
533 if ( m_hFile == NIL_RTFILE
534 || m_strFile != pszPath)
535 {
536 char *pszFile = RTPathJoinA(m_strDropDir.c_str(), pszPath);
537 if (pszFile)
538 {
539 RTFILE hFile;
540 /** @todo Respect fMode! */
541 rc = RTFileOpen(&hFile, pszFile,
542 RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE
543 | RTFILE_O_WRITE | RTFILE_O_APPEND);
544 if (RT_SUCCESS(rc))
545 {
546 LogFlowFunc(("Opening \"%s\" (fMode=0x%x) for writing ...\n",
547 pszFile, fMode));
548
549 m_hFile = hFile;
550 m_strFile = pszPath;
551 }
552
553 RTStrFree(pszFile);
554 }
555 else
556 rc = VERR_NO_MEMORY;
557 }
558 else
559 rc = VINF_SUCCESS;
560
561 if (RT_SUCCESS(rc))
562 {
563 rc = RTFileWrite(m_hFile, pvData, cbData,
564 NULL /* No partial writes */);
565
566 if (RT_SUCCESS(rc))
567 rc = dataSetStatus(cbData);
568 }
569
570 return rc;
571}
572
573///////////////////////////////////////////////////////////////////////////////
574
575HRESULT GuestDnDPrivate::adjustCoords(ULONG uScreenId, ULONG *puX, ULONG *puY) const
576{
577 /* For multi-monitor support we need to add shift values to the coordinates
578 * (depending on the screen number). */
579 ComPtr<IDisplay> pDisplay;
580 HRESULT hr = p->mParent->COMGETTER(Display)(pDisplay.asOutParam());
581 if (FAILED(hr))
582 return hr;
583
584 ComPtr<IFramebuffer> pFramebuffer;
585 LONG xShift, yShift;
586 hr = pDisplay->GetFramebuffer(uScreenId, pFramebuffer.asOutParam(),
587 &xShift, &yShift);
588 if (FAILED(hr))
589 return hr;
590
591 *puX += xShift;
592 *puY += yShift;
593
594 return hr;
595}
596
597int GuestDnDPrivate::hostCall(uint32_t u32Function, uint32_t cParms,
598 PVBOXHGCMSVCPARM paParms) const
599{
600 VMMDev *pVMMDev = NULL;
601 {
602 /* Make sure mParent is valid, so set the read lock while using.
603 * Do not keep this lock while doing the actual call, because in the meanwhile
604 * another thread could request a write lock which would be a bad idea ... */
605 AutoReadLock alock(p COMMA_LOCKVAL_SRC_POS);
606
607 /* Forward the information to the VMM device. */
608 AssertPtr(p->mParent);
609 pVMMDev = p->mParent->getVMMDev();
610 }
611
612 if (!pVMMDev)
613 throw p->setError(VBOX_E_VM_ERROR,
614 p->tr("VMM device is not available (is the VM running?)"));
615
616 LogFlowFunc(("hgcmHostCall uMsg=%RU32, cParms=%RU32\n", u32Function, cParms));
617 int rc = pVMMDev->hgcmHostCall("VBoxDragAndDropSvc",
618 u32Function,
619 cParms, paParms);
620 if (RT_FAILURE(rc))
621 {
622 LogFlowFunc(("hgcmHostCall failed with rc=%Rrc\n", rc));
623 throw p->setError(VBOX_E_IPRT_ERROR,
624 p->tr("hgcmHostCall failed (%Rrc)"), rc);
625 }
626
627 return rc;
628}
629
630/* static */
631RTCString GuestDnDPrivate::toFormatString(ComSafeArrayIn(IN_BSTR, formats))
632{
633 const RTCList<Utf8Str> formatList(ComSafeArrayInArg(formats));
634 RTCString strFormat;
635 for (size_t i = 0; i < formatList.size(); ++i)
636 {
637 const RTCString &f = formatList.at(i);
638 /* Only keep allowed format types. */
639 if (m_sstrAllowedMimeTypes.contains(f))
640 strFormat += f + "\r\n";
641 }
642 return strFormat;
643}
644
645/* static */
646void GuestDnDPrivate::toFormatSafeArray(const RTCString &strFormats, ComSafeArrayOut(BSTR, formats))
647{
648 RTCList<RTCString> list = strFormats.split("\r\n");
649 size_t i = 0;
650 while (i < list.size())
651 {
652 /* Only keep allowed format types. */
653 if (!m_sstrAllowedMimeTypes.contains(list.at(i)))
654 list.removeAt(i);
655 else
656 ++i;
657 }
658 /* Create a safe array out of the cleaned list. */
659 com::SafeArray<BSTR> sfaFormats(list.size());
660 for (i = 0; i < list.size(); ++i)
661 {
662 const RTCString &f = list.at(i);
663 if (m_sstrAllowedMimeTypes.contains(f))
664 {
665 Bstr bstr(f);
666 bstr.detachTo(&sfaFormats[i]);
667 }
668 }
669 sfaFormats.detachTo(ComSafeArrayOutArg(formats));
670}
671
672/* static */
673uint32_t GuestDnDPrivate::toHGCMAction(DragAndDropAction_T action)
674{
675 uint32_t a = DND_IGNORE_ACTION;
676 switch (action)
677 {
678 case DragAndDropAction_Copy: a = DND_COPY_ACTION; break;
679 case DragAndDropAction_Move: a = DND_MOVE_ACTION; break;
680 case DragAndDropAction_Link: /* For now it doesn't seems useful to allow a link action between host & guest. Maybe later! */
681 case DragAndDropAction_Ignore: /* Ignored */ break;
682 default: AssertMsgFailed(("Action %u not recognized!\n", action)); break;
683 }
684 return a;
685}
686
687/* static */
688void GuestDnDPrivate::toHGCMActions(DragAndDropAction_T inDefAction,
689 uint32_t *pOutDefAction,
690 ComSafeArrayIn(DragAndDropAction_T, inAllowedActions),
691 uint32_t *pOutAllowedActions)
692{
693 const com::SafeArray<DragAndDropAction_T> sfaInActions(ComSafeArrayInArg(inAllowedActions));
694
695 /* Defaults */
696 *pOutDefAction = toHGCMAction(inDefAction);;
697 *pOutAllowedActions = DND_IGNORE_ACTION;
698
699 /* First convert the allowed actions to a bit array. */
700 for (size_t i = 0; i < sfaInActions.size(); ++i)
701 *pOutAllowedActions |= toHGCMAction(sfaInActions[i]);
702
703 /* Second check if the default action is a valid action and if not so try
704 * to find an allowed action. */
705 if (isDnDIgnoreAction(*pOutDefAction))
706 {
707 if (hasDnDCopyAction(*pOutAllowedActions))
708 *pOutDefAction = DND_COPY_ACTION;
709 else if (hasDnDMoveAction(*pOutAllowedActions))
710 *pOutDefAction = DND_MOVE_ACTION;
711 }
712}
713
714/* static */
715DragAndDropAction_T GuestDnDPrivate::toMainAction(uint32_t uAction)
716{
717 /* For now it doesn't seems useful to allow a
718 * link action between host & guest. Maybe later! */
719 return (isDnDCopyAction(uAction) ? (DragAndDropAction_T)DragAndDropAction_Copy :
720 isDnDMoveAction(uAction) ? (DragAndDropAction_T)DragAndDropAction_Move :
721 (DragAndDropAction_T)DragAndDropAction_Ignore);
722}
723
724/* static */
725void GuestDnDPrivate::toMainActions(uint32_t uActions,
726 ComSafeArrayOut(DragAndDropAction_T, actions))
727{
728 /* For now it doesn't seems useful to allow a
729 * link action between host & guest. Maybe later! */
730 RTCList<DragAndDropAction_T> list;
731 if (hasDnDCopyAction(uActions))
732 list.append(DragAndDropAction_Copy);
733 if (hasDnDMoveAction(uActions))
734 list.append(DragAndDropAction_Move);
735
736 com::SafeArray<DragAndDropAction_T> sfaActions(list.size());
737 for (size_t i = 0; i < list.size(); ++i)
738 sfaActions[i] = list.at(i);
739 sfaActions.detachTo(ComSafeArrayOutArg(actions));
740}
741
742GuestDnD::GuestDnD(const ComObjPtr<Guest>& pGuest)
743 : d_ptr(new GuestDnDPrivate(this, pGuest))
744{
745}
746
747GuestDnD::~GuestDnD(void)
748{
749 delete d_ptr;
750}
751
752HRESULT GuestDnD::dragHGEnter(ULONG uScreenId, ULONG uX, ULONG uY,
753 DragAndDropAction_T defaultAction,
754 ComSafeArrayIn(DragAndDropAction_T, allowedActions),
755 ComSafeArrayIn(IN_BSTR, formats),
756 DragAndDropAction_T *pResultAction)
757{
758 DPTR(GuestDnD);
759 const ComObjPtr<Guest> &p = d->p;
760
761 /* Default is ignoring */
762 *pResultAction = DragAndDropAction_Ignore;
763
764 /* Check & convert the drag & drop actions */
765 uint32_t uDefAction = 0;
766 uint32_t uAllowedActions = 0;
767 d->toHGCMActions(defaultAction, &uDefAction, ComSafeArrayInArg(allowedActions), &uAllowedActions);
768 /* If there is no usable action, ignore this request. */
769 if (isDnDIgnoreAction(uDefAction))
770 return S_OK;
771
772 /* Make a flat data string out of the mime-type list. */
773 RTCString strFormats = d->toFormatString(ComSafeArrayInArg(formats));
774 /* If there is no valid mime-type, ignore this request. */
775 if (strFormats.isEmpty())
776 return S_OK;
777
778 HRESULT hr = S_OK;
779
780 try
781 {
782 /* Adjust the coordinates in a multi-monitor setup. */
783 d->adjustCoords(uScreenId, &uX, &uY);
784
785 VBOXHGCMSVCPARM paParms[7];
786 int i = 0;
787 paParms[i++].setUInt32(uScreenId);
788 paParms[i++].setUInt32(uX);
789 paParms[i++].setUInt32(uY);
790 paParms[i++].setUInt32(uDefAction);
791 paParms[i++].setUInt32(uAllowedActions);
792 paParms[i++].setPointer((void*)strFormats.c_str(), strFormats.length() + 1);
793 paParms[i++].setUInt32(strFormats.length() + 1);
794
795 d->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_ENTER,
796 i,
797 paParms);
798
799 DnDGuestResponse *pResp = d->response();
800 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
801 return S_OK;
802
803 /* Copy the response info */
804 *pResultAction = d->toMainAction(pResp->defAction());
805 LogFlowFunc(("*pResultAction=%ld\n", *pResultAction));
806 }
807 catch (HRESULT hr2)
808 {
809 hr = hr2;
810 }
811
812 return hr;
813}
814
815HRESULT GuestDnD::dragHGMove(ULONG uScreenId, ULONG uX, ULONG uY,
816 DragAndDropAction_T defaultAction,
817 ComSafeArrayIn(DragAndDropAction_T, allowedActions),
818 ComSafeArrayIn(IN_BSTR, formats),
819 DragAndDropAction_T *pResultAction)
820{
821 DPTR(GuestDnD);
822 const ComObjPtr<Guest> &p = d->p;
823
824 /* Default is ignoring */
825 *pResultAction = DragAndDropAction_Ignore;
826
827 /* Check & convert the drag & drop actions */
828 uint32_t uDefAction = 0;
829 uint32_t uAllowedActions = 0;
830 d->toHGCMActions(defaultAction, &uDefAction, ComSafeArrayInArg(allowedActions), &uAllowedActions);
831 /* If there is no usable action, ignore this request. */
832 if (isDnDIgnoreAction(uDefAction))
833 return S_OK;
834
835 /* Make a flat data string out of the mime-type list. */
836 RTCString strFormats = d->toFormatString(ComSafeArrayInArg(formats));
837 /* If there is no valid mime-type, ignore this request. */
838 if (strFormats.isEmpty())
839 return S_OK;
840
841 HRESULT hr = S_OK;
842
843 try
844 {
845 /* Adjust the coordinates in a multi-monitor setup. */
846 d->adjustCoords(uScreenId, &uX, &uY);
847
848 VBOXHGCMSVCPARM paParms[7];
849 int i = 0;
850 paParms[i++].setUInt32(uScreenId);
851 paParms[i++].setUInt32(uX);
852 paParms[i++].setUInt32(uY);
853 paParms[i++].setUInt32(uDefAction);
854 paParms[i++].setUInt32(uAllowedActions);
855 paParms[i++].setPointer((void*)strFormats.c_str(), strFormats.length() + 1);
856 paParms[i++].setUInt32(strFormats.length() + 1);
857
858 d->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_MOVE,
859 i,
860 paParms);
861
862 DnDGuestResponse *pResp = d->response();
863 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
864 return S_OK;
865
866 /* Copy the response info */
867 *pResultAction = d->toMainAction(pResp->defAction());
868 LogFlowFunc(("*pResultAction=%ld\n", *pResultAction));
869 }
870 catch (HRESULT hr2)
871 {
872 hr = hr2;
873 }
874
875 return hr;
876}
877
878HRESULT GuestDnD::dragHGLeave(ULONG uScreenId)
879{
880 DPTR(GuestDnD);
881 const ComObjPtr<Guest> &p = d->p;
882
883 HRESULT hr = S_OK;
884
885 try
886 {
887 d->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_LEAVE,
888 0,
889 NULL);
890
891 DnDGuestResponse *pResp = d->response();
892 pResp->waitForGuestResponse();
893 }
894 catch (HRESULT hr2)
895 {
896 hr = hr2;
897 }
898
899 return hr;
900}
901
902HRESULT GuestDnD::dragHGDrop(ULONG uScreenId, ULONG uX, ULONG uY,
903 DragAndDropAction_T defaultAction,
904 ComSafeArrayIn(DragAndDropAction_T, allowedActions),
905 ComSafeArrayIn(IN_BSTR, formats),
906 BSTR *pstrFormat,
907 DragAndDropAction_T *pResultAction)
908{
909 DPTR(GuestDnD);
910 const ComObjPtr<Guest> &p = d->p;
911
912 /* Default is ignoring */
913 *pResultAction = DragAndDropAction_Ignore;
914
915 /* Check & convert the drag & drop actions */
916 uint32_t uDefAction = 0;
917 uint32_t uAllowedActions = 0;
918 d->toHGCMActions(defaultAction, &uDefAction, ComSafeArrayInArg(allowedActions), &uAllowedActions);
919 /* If there is no usable action, ignore this request. */
920 if (isDnDIgnoreAction(uDefAction))
921 return S_OK;
922
923 /* Make a flat data string out of the mime-type list. */
924 RTCString strFormats = d->toFormatString(ComSafeArrayInArg(formats));
925 /* If there is no valid mime-type, ignore this request. */
926 if (strFormats.isEmpty())
927 return S_OK;
928
929 HRESULT hr = S_OK;
930
931 try
932 {
933 /* Adjust the coordinates in a multi-monitor setup. */
934 d->adjustCoords(uScreenId, &uX, &uY);
935
936 VBOXHGCMSVCPARM paParms[7];
937 int i = 0;
938 paParms[i++].setUInt32(uScreenId);
939 paParms[i++].setUInt32(uX);
940 paParms[i++].setUInt32(uY);
941 paParms[i++].setUInt32(uDefAction);
942 paParms[i++].setUInt32(uAllowedActions);
943 paParms[i++].setPointer((void*)strFormats.c_str(), strFormats.length() + 1);
944 paParms[i++].setUInt32(strFormats.length() + 1);
945
946 d->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_DROPPED,
947 i,
948 paParms);
949
950 DnDGuestResponse *pResp = d->response();
951 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
952 return S_OK;
953
954 /* Get the resulting action from the guest. */
955 *pResultAction = d->toMainAction(pResp->defAction());
956
957 LogFlowFunc(("resFormat=%s, resAction=%RU32\n",
958 pResp->format().c_str(), pResp->defAction()));
959
960 Bstr(pResp->format()).cloneTo(pstrFormat);
961 }
962 catch (HRESULT hr2)
963 {
964 hr = hr2;
965 }
966
967 return hr;
968}
969
970HRESULT GuestDnD::dragHGPutData(ULONG uScreenId, IN_BSTR bstrFormat,
971 ComSafeArrayIn(BYTE, data), IProgress **ppProgress)
972{
973 DPTR(GuestDnD);
974 const ComObjPtr<Guest> &p = d->p;
975
976 HRESULT hr = S_OK;
977
978 try
979 {
980 Utf8Str strFormat(bstrFormat);
981 com::SafeArray<BYTE> sfaData(ComSafeArrayInArg(data));
982
983 VBOXHGCMSVCPARM paParms[5];
984 int i = 0;
985 paParms[i++].setUInt32(uScreenId);
986 paParms[i++].setPointer((void*)strFormat.c_str(), (uint32_t)strFormat.length() + 1);
987 paParms[i++].setUInt32((uint32_t)strFormat.length() + 1);
988 paParms[i++].setPointer((void*)sfaData.raw(), (uint32_t)sfaData.size());
989 paParms[i++].setUInt32((uint32_t)sfaData.size());
990
991 DnDGuestResponse *pResp = d->response();
992 /* Reset any old progress status. */
993 pResp->resetProgress(p);
994
995 /* Note: The actual data transfer of files/directoies is performed by the
996 * DnD host service. */
997 d->hostCall(DragAndDropSvc::HOST_DND_HG_SND_DATA,
998 i,
999 paParms);
1000
1001 /* Query the progress object to the caller. */
1002 pResp->queryProgressTo(ppProgress);
1003 }
1004 catch (HRESULT hr2)
1005 {
1006 hr = hr2;
1007 }
1008
1009 return hr;
1010}
1011
1012#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1013HRESULT GuestDnD::dragGHPending(ULONG uScreenId,
1014 ComSafeArrayOut(BSTR, formats),
1015 ComSafeArrayOut(DragAndDropAction_T, allowedActions),
1016 DragAndDropAction_T *pDefaultAction)
1017{
1018 DPTR(GuestDnD);
1019 const ComObjPtr<Guest> &p = d->p;
1020
1021 /* Default is ignoring */
1022 *pDefaultAction = DragAndDropAction_Ignore;
1023
1024 HRESULT hr = S_OK;
1025
1026 try
1027 {
1028 VBOXHGCMSVCPARM paParms[1];
1029 int i = 0;
1030 paParms[i++].setUInt32(uScreenId);
1031
1032 d->hostCall(DragAndDropSvc::HOST_DND_GH_REQ_PENDING,
1033 i,
1034 paParms);
1035
1036 DnDGuestResponse *pResp = d->response();
1037 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
1038 return S_OK;
1039
1040 if (isDnDIgnoreAction(pResp->defAction()))
1041 return S_OK;
1042
1043 /* Fetch the default action to use. */
1044 *pDefaultAction = d->toMainAction(pResp->defAction());
1045 d->toFormatSafeArray(pResp->format(), ComSafeArrayOutArg(formats));
1046 d->toMainActions(pResp->allActions(), ComSafeArrayOutArg(allowedActions));
1047
1048 LogFlowFunc(("*pDefaultAction=0x%x\n", *pDefaultAction));
1049 }
1050 catch (HRESULT hr2)
1051 {
1052 hr = hr2;
1053 }
1054
1055 return hr;
1056}
1057
1058HRESULT GuestDnD::dragGHDropped(IN_BSTR bstrFormat, DragAndDropAction_T action,
1059 IProgress **ppProgress)
1060{
1061 DPTR(GuestDnD);
1062 const ComObjPtr<Guest> &p = d->p;
1063
1064 Utf8Str strFormat(bstrFormat);
1065 HRESULT hr = S_OK;
1066
1067 uint32_t uAction = d->toHGCMAction(action);
1068 /* If there is no usable action, ignore this request. */
1069 if (isDnDIgnoreAction(uAction))
1070 return S_OK;
1071
1072 const char *pcszFormat = strFormat.c_str();
1073 bool fNeedsDropDir = DnDMIMENeedsDropDir(pcszFormat, strlen(pcszFormat));
1074 LogFlowFunc(("strFormat=%s, uAction=0x%x, fNeedsDropDir=%RTbool\n",
1075 pcszFormat, uAction, fNeedsDropDir));
1076
1077 DnDGuestResponse *pResp = d->response();
1078 AssertPtr(pResp);
1079
1080 /* Reset any old data. */
1081 pResp->reset();
1082 pResp->resetProgress(p);
1083
1084 /* Set the format we are going to retrieve to have it around
1085 * when retrieving the data later. */
1086 pResp->setFormat(strFormat);
1087
1088 if (fNeedsDropDir)
1089 {
1090 char szDropDir[RTPATH_MAX];
1091 int rc = DnDDirCreateDroppedFiles(szDropDir, sizeof(szDropDir));
1092 if (RT_FAILURE(rc))
1093 return p->setError(VBOX_E_IPRT_ERROR,
1094 p->tr("Unable to create the temporary drag'n drop directory \"%s\" (%Rrc)\n"),
1095 szDropDir, rc);
1096 LogFlowFunc(("Dropped files directory on the host is: %s\n", szDropDir));
1097
1098 pResp->setDropDir(szDropDir);
1099 }
1100
1101 try
1102 {
1103 VBOXHGCMSVCPARM paParms[3];
1104 int i = 0;
1105 paParms[i++].setPointer((void*)strFormat.c_str(), (uint32_t)strFormat.length() + 1);
1106 paParms[i++].setUInt32((uint32_t)strFormat.length() + 1);
1107 paParms[i++].setUInt32(uAction);
1108
1109 d->hostCall(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED,
1110 i,
1111 paParms);
1112
1113 /* Query the progress object to the caller. */
1114 pResp->queryProgressTo(ppProgress);
1115 }
1116 catch (HRESULT hr2)
1117 {
1118 hr = hr2;
1119 }
1120
1121 return hr;
1122}
1123
1124HRESULT GuestDnD::dragGHGetData(ComSafeArrayOut(BYTE, data))
1125{
1126 DPTR(GuestDnD);
1127 const ComObjPtr<Guest> &p = d->p;
1128
1129 HRESULT hr = S_OK;
1130
1131 DnDGuestResponse *pResp = d->response();
1132 if (pResp)
1133 {
1134 com::SafeArray<BYTE> sfaData;
1135
1136 size_t cbData = pResp->size();
1137 if (cbData)
1138 {
1139 const void *pvData = pResp->data();
1140 AssertPtr(pvData);
1141
1142 Utf8Str strFormat = pResp->format();
1143 LogFlowFunc(("strFormat=%s, cbData=%zu, pvData=0x%p\n",
1144 strFormat.c_str(), cbData, pvData));
1145
1146 if (DnDMIMEHasFileURLs(strFormat.c_str(), strFormat.length()))
1147 {
1148 LogFlowFunc(("strDropDir=%s\n", pResp->dropDir().c_str()));
1149
1150 DnDURIList lstURI;
1151 int rc2 = lstURI.RootFromURIData(pvData, cbData, 0 /* fFlags */);
1152 if (RT_SUCCESS(rc2))
1153 {
1154 Utf8Str strURIs = lstURI.RootToString(pResp->dropDir());
1155 size_t cbURIs = strURIs.length();
1156 if (sfaData.resize(cbURIs + 1 /* Include termination */))
1157 memcpy(sfaData.raw(), strURIs.c_str(), cbURIs);
1158 else
1159 hr = E_OUTOFMEMORY;
1160 }
1161 else
1162 hr = VBOX_E_IPRT_ERROR;
1163
1164 LogFlowFunc(("Found %zu root URIs, rc=%Rrc\n", lstURI.RootCount(), rc2));
1165 }
1166 else
1167 {
1168 /* Copy the data into a safe array of bytes. */
1169 if (sfaData.resize(cbData))
1170 memcpy(sfaData.raw(), pvData, cbData);
1171 else
1172 hr = E_OUTOFMEMORY;
1173 }
1174 }
1175
1176 /* Detach in any case, regardless of data size. */
1177 sfaData.detachTo(ComSafeArrayOutArg(data));
1178
1179 /* Delete the data. */
1180 pResp->reset();
1181 }
1182 else
1183 hr = VBOX_E_INVALID_OBJECT_STATE;
1184
1185 LogFlowFunc(("Returning hr=%Rhrc\n", hr));
1186 return hr;
1187}
1188
1189int GuestDnD::onGHSendData(DnDGuestResponse *pResp,
1190 const void *pvData, size_t cbData,
1191 size_t cbTotalSize)
1192{
1193 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
1194 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1195 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1196 AssertReturn(cbTotalSize, VERR_INVALID_PARAMETER);
1197
1198 int rc = pResp->dataAdd(pvData, cbData, NULL /* Current size */);
1199 if (RT_SUCCESS(rc))
1200 rc = pResp->dataSetStatus(cbData, cbTotalSize);
1201
1202 LogFlowFuncLeaveRC(rc);
1203 return rc;
1204}
1205
1206int GuestDnD::onGHSendDir(DnDGuestResponse *pResp,
1207 const char *pszPath, size_t cbPath,
1208 uint32_t fMode)
1209{
1210 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
1211 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1212 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
1213
1214 LogFlowFunc(("pszPath=%s, cbPath=%zu, fMode=0x%x\n",
1215 pszPath, cbPath, fMode));
1216
1217 int rc;
1218 char *pszDir = RTPathJoinA(pResp->dropDir().c_str(), pszPath);
1219 if (pszDir)
1220 {
1221 rc = RTDirCreateFullPath(pszDir, fMode);
1222 RTStrFree(pszDir);
1223 }
1224 else
1225 rc = VERR_NO_MEMORY;
1226
1227 if (RT_SUCCESS(rc))
1228 rc = pResp->dataSetStatus(cbPath);
1229
1230 LogFlowFuncLeaveRC(rc);
1231 return rc;
1232}
1233
1234int GuestDnD::onGHSendFile(DnDGuestResponse *pResp,
1235 const char *pszPath, size_t cbPath,
1236 void *pvData, size_t cbData, uint32_t fMode)
1237{
1238 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
1239 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1240 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
1241
1242 LogFlowFunc(("pszPath=%s, cbPath=%zu, fMode=0x%x\n",
1243 pszPath, cbPath, fMode));
1244
1245 int rc = pResp->writeToFile(pszPath, cbPath, pvData, cbData, fMode);
1246
1247 LogFlowFuncLeaveRC(rc);
1248 return rc;
1249}
1250#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1251
1252/* static */
1253DECLCALLBACK(int) GuestDnD::notifyGuestDragAndDropEvent(void *pvExtension, uint32_t u32Function,
1254 void *pvParms, uint32_t cbParms)
1255{
1256 LogFlowFunc(("pvExtension=%p, u32Function=%RU32, pvParms=%p, cbParms=%RU32\n",
1257 pvExtension, u32Function, pvParms, cbParms));
1258
1259 ComObjPtr<Guest> pGuest = reinterpret_cast<Guest*>(pvExtension);
1260 if (!pGuest->m_pGuestDnD)
1261 return VINF_SUCCESS;
1262
1263 GuestDnD *pGuestDnD = pGuest->m_pGuestDnD;
1264 AssertPtr(pGuestDnD);
1265
1266 GuestDnDPrivate *d = static_cast<GuestDnDPrivate*>(pGuest->m_pGuestDnD->d_ptr);
1267 const ComObjPtr<Guest> &p = d->p;
1268
1269 DnDGuestResponse *pResp = d->response();
1270 if (pResp == NULL)
1271 return VERR_INVALID_PARAMETER;
1272
1273 int rc = VINF_SUCCESS;
1274 switch (u32Function)
1275 {
1276 case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
1277 {
1278 DragAndDropSvc::PVBOXDNDCBHGACKOPDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGACKOPDATA>(pvParms);
1279 AssertPtr(pCBData);
1280 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGACKOPDATA) == cbParms, VERR_INVALID_PARAMETER);
1281 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1282
1283 pResp->setDefAction(pCBData->uAction);
1284
1285 rc = pResp->notifyAboutGuestResponse();
1286 break;
1287 }
1288
1289 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
1290 {
1291 DragAndDropSvc::PVBOXDNDCBHGREQDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGREQDATADATA>(pvParms);
1292 AssertPtr(pCBData);
1293 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGREQDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1294 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1295
1296 pResp->setFormat(pCBData->pszFormat);
1297
1298 rc = pResp->notifyAboutGuestResponse();
1299 break;
1300 }
1301
1302 case DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS:
1303 {
1304 DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA>(pvParms);
1305 AssertPtr(pCBData);
1306 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGEVTPROGRESSDATA) == cbParms, VERR_INVALID_PARAMETER);
1307 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_EVT_PROGRESS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1308
1309 rc = pResp->setProgress(pCBData->uPercentage, pCBData->uState, pCBData->rc);
1310 break;
1311 }
1312
1313#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1314 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
1315 {
1316 DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA>(pvParms);
1317 AssertPtr(pCBData);
1318 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA) == cbParms, VERR_INVALID_PARAMETER);
1319 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1320
1321 pResp->setFormat(pCBData->pszFormat);
1322 pResp->setDefAction(pCBData->uDefAction);
1323 pResp->setAllActions(pCBData->uAllActions);
1324
1325 rc = pResp->notifyAboutGuestResponse();
1326 break;
1327 }
1328
1329 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
1330 {
1331 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
1332 AssertPtr(pCBData);
1333 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1334 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1335
1336 rc = pGuestDnD->onGHSendData(pResp, pCBData->pvData, pCBData->cbData,
1337 pCBData->cbTotalSize);
1338 break;
1339 }
1340
1341 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
1342 {
1343 DragAndDropSvc::PVBOXDNDCBSNDDIRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDIRDATA>(pvParms);
1344 AssertPtr(pCBData);
1345 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDIRDATA) == cbParms, VERR_INVALID_PARAMETER);
1346 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1347
1348 rc = pGuestDnD->onGHSendDir(pResp, pCBData->pszPath, pCBData->cbPath, pCBData->fMode);
1349 break;
1350 }
1351
1352 case DragAndDropSvc::GUEST_DND_GH_SND_FILE:
1353 {
1354 DragAndDropSvc::PVBOXDNDCBSNDFILEDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEDATA>(pvParms);
1355 AssertPtr(pCBData);
1356 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEDATA) == cbParms, VERR_INVALID_PARAMETER);
1357 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1358
1359 rc = pGuestDnD->onGHSendFile(pResp, pCBData->pszFilePath, pCBData->cbFilePath,
1360 pCBData->pvData, pCBData->cbData, pCBData->fMode);
1361 break;
1362 }
1363
1364 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
1365 {
1366 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
1367 AssertPtr(pCBData);
1368 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
1369 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1370
1371 /* Cleanup. */
1372 pResp->reset();
1373 rc = pResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc);
1374 break;
1375 }
1376#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1377 default:
1378 rc = VERR_NOT_SUPPORTED; /* Tell the guest. */
1379 break;
1380 }
1381
1382 LogFlowFunc(("Returning rc=%Rrc\n", rc));
1383 return rc;
1384}
1385#endif /* VBOX_WITH_DRAG_AND_DROP */
1386
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