VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestDnDPrivate.cpp@ 52371

Last change on this file since 52371 was 52082, checked in by vboxsync, 10 years ago

6813 - DisplayImpl using COM Wrappers

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.2 KB
Line 
1/* $Id: GuestDnDPrivate.cpp 52082 2014-07-17 17:18:56Z vboxsync $ */
2/** @file
3 * Private guest drag and drop code, used by GuestDnDTarget +
4 * GuestDnDSource.
5 */
6
7/*
8 * Copyright (C) 2011-2014 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include "GuestImpl.h"
20#include "AutoCaller.h"
21
22#ifdef VBOX_WITH_DRAG_AND_DROP
23# include "ConsoleImpl.h"
24# include "ProgressImpl.h"
25# include "GuestDnDPrivate.h"
26
27# include <algorithm>
28
29# include <iprt/dir.h>
30# include <iprt/path.h>
31# include <iprt/stream.h>
32# include <iprt/semaphore.h>
33# include <iprt/cpp/utils.h>
34
35# include <VMMDev.h>
36
37# include <VBox/GuestHost/DragAndDrop.h>
38# include <VBox/HostServices/DragAndDropSvc.h>
39
40# ifdef LOG_GROUP
41# undef LOG_GROUP
42# endif
43# define LOG_GROUP LOG_GROUP_GUEST_DND
44# include <VBox/log.h>
45
46/**
47 * Overview:
48 *
49 * Drag and Drop is handled over the internal HGCM service for the host <->
50 * guest communication. Beside that we need to map the Drag and Drop protocols
51 * of the various OS's we support to our internal channels, this is also highly
52 * communicative in both directions. Unfortunately HGCM isn't really designed
53 * for that. Next we have to foul some of the components. This includes to
54 * trick X11 on the guest side, but also Qt needs to be tricked on the host
55 * side a little bit.
56 *
57 * The following components are involved:
58 *
59 * 1. GUI: Uses the Qt classes for Drag and Drop and mainly forward the content
60 * of it to the Main IGuest interface (see UIDnDHandler.cpp).
61 * 2. Main: Public interface for doing Drag and Drop. Also manage the IProgress
62 * interfaces for blocking the caller by showing a progress dialog (see
63 * this file).
64 * 3. HGCM service: Handle all messages from the host to the guest at once and
65 * encapsulate the internal communication details (see dndmanager.cpp and
66 * friends).
67 * 4. Guest additions: Split into the platform neutral part (see
68 * VBoxGuestR3LibDragAndDrop.cpp) and the guest OS specific parts.
69 * Receive/send message from/to the HGCM service and does all guest specific
70 * operations. Currently only X11 is supported (see draganddrop.cpp within
71 * VBoxClient).
72 *
73 * Host -> Guest:
74 * 1. There are DnD Enter, Move, Leave events which are send exactly like this
75 * to the guest. The info includes the pos, mimetypes and allowed actions.
76 * The guest has to respond with an action it would accept, so the GUI could
77 * change the cursor.
78 * 2. On drop, first a drop event is sent. If this is accepted a drop data
79 * event follows. This blocks the GUI and shows some progress indicator.
80 *
81 * Guest -> Host:
82 * 1. The GUI is asking the guest if a DnD event is pending when the user moves
83 * the cursor out of the view window. If so, this returns the mimetypes and
84 * allowed actions.
85 * (2. On every mouse move this is asked again, to make sure the DnD event is
86 * still valid.)
87 * 3. On drop the host request the data from the guest. This blocks the GUI and
88 * shows some progress indicator.
89 *
90 * Some hints:
91 * m_strSupportedFormats here in this file defines the allowed mime-types.
92 * This is necessary because we need special handling for some of the
93 * mime-types. E.g. for URI lists we need to transfer the actual dirs and
94 * files. Text EOL may to be changed. Also unknown mime-types may need special
95 * handling as well, which may lead to undefined behavior in the host/guest, if
96 * not done.
97 *
98 * Dropping of a directory, means recursively transferring _all_ the content.
99 *
100 * Directories and files are placed into the user's temporary directory on the
101 * guest (e.g. /tmp/VirtualBox Dropped Files). We can't delete them after the
102 * DnD operation, because we didn't know what the DnD target does with it. E.g.
103 * it could just be opened in place. This could lead ofc to filling up the disk
104 * within the guest. To inform the user about this, a small app could be
105 * developed which scans this directory regularly and inform the user with a
106 * tray icon hint (and maybe the possibility to clean this up instantly). The
107 * same has to be done in the G->H direction when it is implemented.
108 *
109 * Of course only regularly files are supported. Symlinks are resolved and
110 * transfered as regularly files. First we don't know if the other side support
111 * symlinks at all and second they could point to somewhere in a directory tree
112 * which not exists on the other side.
113 *
114 * The code tries to preserve the file modes of the transfered dirs/files. This
115 * is useful (and maybe necessary) for two things:
116 * 1. If a file is executable, it should be also after the transfer, so the
117 * user can just execute it, without manually tweaking the modes first.
118 * 2. If a dir/file is not accessible by group/others in the host, it shouldn't
119 * be in the guest.
120 * In any case, the user mode is always set to rwx (so that we can access it
121 * ourself, in e.g. for a cleanup case after cancel).
122 *
123 * Cancel is supported in both directions and cleans up all previous steps
124 * (thats is: deleting already transfered dirs/files).
125 *
126 * In general I propose the following changes in the VBox HGCM infrastructure
127 * for the future:
128 * - Currently it isn't really possible to send messages to the guest from the
129 * host. The host informs the guest just that there is something, the guest
130 * than has to ask which message and depending on that send the appropriate
131 * message to the host, which is filled with the right data.
132 * - There is no generic interface for sending bigger memory blocks to/from the
133 * guest. This is now done here, but I guess was also necessary for e.g.
134 * guest execution. So something generic which brake this up into smaller
135 * blocks and send it would be nice (with all the error handling and such
136 * ofc).
137 * - I developed a "protocol" for the DnD communication here. So the host and
138 * the guest have always to match in the revision. This is ofc bad, because
139 * the additions could be outdated easily. So some generic protocol number
140 * support in HGCM for asking the host and the guest of the support version,
141 * would be nice. Ofc at least the host should be able to talk to the guest,
142 * even when the version is below the host one.
143 * All this stuff would be useful for the current services, but also for future
144 * onces.
145 *
146 ** @todo
147 * - ESC doesn't really work (on Windows guests it's already implemented)
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 * - Add support for more mime-types (especially images, csv)
152 * - Test unusual behavior:
153 * - DnD service crash in the guest during a DnD op (e.g. crash of VBoxClient or X11)
154 * - Not expected order of the events between HGCM and the guest
155 * - Security considerations: We transfer a lot of memory between the guest and
156 * the host and even allow the creation of dirs/files. Maybe there should be
157 * limits introduced to preventing DOS attacks or filling up all the memory
158 * (both in the host and the guest).
159 */
160
161GuestDnDResponse::GuestDnDResponse(const ComObjPtr<Guest>& pGuest)
162 : m_EventSem(NIL_RTSEMEVENT)
163 , m_defAction(0)
164 , m_allActions(0)
165 , m_pvData(0)
166 , m_cbData(0)
167 , m_cbDataCurrent(0)
168 , m_cbDataTotal(0)
169 , m_hFile(NIL_RTFILE)
170 , m_parent(pGuest)
171{
172 int rc = RTSemEventCreate(&m_EventSem);
173 AssertRC(rc);
174}
175
176GuestDnDResponse::~GuestDnDResponse(void)
177{
178 reset();
179
180 int rc = RTSemEventDestroy(m_EventSem);
181 AssertRC(rc);
182}
183
184int GuestDnDResponse::dataAdd(const void *pvData, uint32_t cbData,
185 uint32_t *pcbCurSize)
186{
187 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
188 AssertReturn(cbData, VERR_INVALID_PARAMETER);
189 /* pcbCurSize is optional. */
190
191 int rc = VINF_SUCCESS;
192
193 /** @todo Make reallocation scheme a bit smarter here. */
194 m_pvData = RTMemRealloc(m_pvData, m_cbData + cbData);
195 if (m_pvData)
196 {
197 memcpy(&static_cast<uint8_t*>(m_pvData)[m_cbData],
198 pvData, cbData);
199 m_cbData += cbData;
200
201 if (pcbCurSize)
202 *pcbCurSize = m_cbData;
203 }
204 else
205 rc = VERR_NO_MEMORY;
206
207 return rc;
208}
209
210/* static */
211Utf8Str GuestDnDResponse::errorToString(const ComObjPtr<Guest>& pGuest, int guestRc)
212{
213 Utf8Str strError;
214
215 switch (guestRc)
216 {
217 case VERR_ACCESS_DENIED:
218 strError += Utf8StrFmt(pGuest->tr("For one or more guest files or directories selected for transferring to the host your guest "
219 "user does not have the appropriate access rights for. Please make sure that all selected "
220 "elements can be accessed and that your guest user has the appropriate rights."));
221 break;
222
223 case VERR_NOT_FOUND:
224 /* Should not happen due to file locking on the guest, but anyway ... */
225 strError += Utf8StrFmt(pGuest->tr("One or more guest files or directories selected for transferring to the host were not"
226 "found on the guest anymore. This can be the case if the guest files were moved and/or"
227 "altered while the drag'n drop operation was in progress."));
228 break;
229
230 case VERR_SHARING_VIOLATION:
231 strError += Utf8StrFmt(pGuest->tr("One or more guest files or directories selected for transferring to the host were locked. "
232 "Please make sure that all selected elements can be accessed and that your guest user has "
233 "the appropriate rights."));
234 break;
235
236 default:
237 strError += Utf8StrFmt("Drag'n drop guest error (%Rrc)", guestRc);
238 break;
239 }
240
241 return strError;
242}
243
244int GuestDnDResponse::notifyAboutGuestResponse(void)
245{
246 return RTSemEventSignal(m_EventSem);
247}
248
249void GuestDnDResponse::reset(void)
250{
251 LogFlowThisFuncEnter();
252
253 m_defAction = 0;
254 m_allActions = 0;
255
256 m_strDropDir = "";
257 m_strFormat = "";
258
259 if (m_pvData)
260 {
261 RTMemFree(m_pvData);
262 m_pvData = NULL;
263 }
264 m_cbData = 0;
265 m_cbDataCurrent = 0;
266 m_cbDataTotal = 0;
267
268 if (m_hFile != NIL_RTFILE)
269 {
270 RTFileClose(m_hFile);
271 m_hFile = NIL_RTFILE;
272 }
273 m_strFile = "";
274}
275
276HRESULT GuestDnDResponse::resetProgress(const ComObjPtr<Guest>& pParent)
277{
278 m_progress.setNull();
279 HRESULT rc = m_progress.createObject();
280 if (SUCCEEDED(rc))
281 {
282 rc = m_progress->init(static_cast<IGuest*>(pParent),
283 Bstr(pParent->tr("Dropping data")).raw(),
284 TRUE);
285 }
286 return rc;
287}
288
289int GuestDnDResponse::setProgress(unsigned uPercentage,
290 uint32_t uState, int rcOp /* = VINF_SUCCESS */)
291{
292 LogFlowFunc(("uPercentage=%RU32, uState=%RU32, rcOp=%Rrc\n",
293 uPercentage, uState, rcOp));
294
295 int vrc = VINF_SUCCESS;
296 if (!m_progress.isNull())
297 {
298 BOOL fCompleted;
299 HRESULT hr = m_progress->COMGETTER(Completed)(&fCompleted);
300 if (!fCompleted)
301 {
302 if (uState == DragAndDropSvc::DND_PROGRESS_ERROR)
303 {
304 hr = m_progress->i_notifyComplete(VBOX_E_IPRT_ERROR,
305 COM_IIDOF(IGuest),
306 m_parent->getComponentName(),
307 GuestDnDResponse::errorToString(m_parent, rcOp).c_str());
308 reset();
309 }
310 else if (uState == DragAndDropSvc::DND_PROGRESS_CANCELLED)
311 {
312 hr = m_progress->Cancel();
313 if (SUCCEEDED(hr))
314 vrc = VERR_CANCELLED;
315
316 reset();
317 }
318 else /* uState == DragAndDropSvc::DND_PROGRESS_RUNNING */
319 {
320 hr = m_progress->SetCurrentOperationProgress(uPercentage);
321 AssertComRC(hr);
322 if ( uState == DragAndDropSvc::DND_PROGRESS_COMPLETE
323 || uPercentage >= 100)
324 hr = m_progress->i_notifyComplete(S_OK);
325 }
326 }
327 }
328
329 return vrc;
330}
331
332int GuestDnDResponse::dataSetStatus(size_t cbDataAdd, size_t cbDataTotal /* = 0 */)
333{
334 if (cbDataTotal)
335 {
336#ifndef DEBUG_andy
337 AssertMsg(m_cbDataTotal <= cbDataTotal, ("New data size must not be smaller (%zu) than old value (%zu)\n",
338 cbDataTotal, m_cbDataTotal));
339#endif
340 LogFlowFunc(("Updating total data size from %zu to %zu\n", m_cbDataTotal, cbDataTotal));
341 m_cbDataTotal = cbDataTotal;
342 }
343 AssertMsg(m_cbDataTotal, ("m_cbDataTotal must not be <= 0\n"));
344
345 m_cbDataCurrent += cbDataAdd;
346 unsigned int cPercentage = RT_MIN(m_cbDataCurrent * 100.0 / m_cbDataTotal, 100);
347
348 /** @todo Don't use anonymous enums (uint32_t). */
349 uint32_t uStatus = DragAndDropSvc::DND_PROGRESS_RUNNING;
350 if (m_cbDataCurrent >= m_cbDataTotal)
351 uStatus = DragAndDropSvc::DND_PROGRESS_COMPLETE;
352
353#ifdef DEBUG_andy
354 LogFlowFunc(("Updating transfer status (%zu/%zu), status=%ld\n",
355 m_cbDataCurrent, m_cbDataTotal, uStatus));
356#else
357 AssertMsg(m_cbDataCurrent <= m_cbDataTotal,
358 ("More data transferred (%zu) than initially announced (%zu), cbDataAdd=%zu\n",
359 m_cbDataCurrent, m_cbDataTotal, cbDataAdd));
360#endif
361 int rc = setProgress(cPercentage, uStatus);
362
363 /** @todo For now we instantly confirm the cancel. Check if the
364 * guest should first clean up stuff itself and than really confirm
365 * the cancel request by an extra message. */
366 if (rc == VERR_CANCELLED)
367 rc = setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED);
368
369 LogFlowFuncLeaveRC(rc);
370 return rc;
371}
372
373HRESULT GuestDnDResponse::queryProgressTo(IProgress **ppProgress)
374{
375 return m_progress.queryInterfaceTo(ppProgress);
376}
377
378int GuestDnDResponse::waitForGuestResponse(RTMSINTERVAL msTimeout /*= 500 */)
379{
380 int rc = RTSemEventWait(m_EventSem, msTimeout);
381#ifdef DEBUG_andy
382 LogFlowFunc(("msTimeout=%RU32, rc=%Rrc\n", msTimeout, rc));
383#endif
384 return rc;
385}
386
387int GuestDnDResponse::writeToFile(const char *pszPath, size_t cbPath,
388 void *pvData, size_t cbData, uint32_t fMode)
389{
390 /** @todo Support locking more than one file at a time! We
391 * might want to have a table in DnDGuestImpl which
392 * keeps those file pointers around, or extend the
393 * actual protocol for explicit open calls.
394 *
395 * For now we only keep one file open at a time, so if
396 * a client does alternating writes to different files
397 * this function will close the old and re-open the new
398 * file on every call. */
399 int rc;
400 if ( m_hFile == NIL_RTFILE
401 || m_strFile != pszPath)
402 {
403 char *pszFile = RTPathJoinA(m_strDropDir.c_str(), pszPath);
404 if (pszFile)
405 {
406 RTFILE hFile;
407 /** @todo Respect fMode! */
408 rc = RTFileOpen(&hFile, pszFile,
409 RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE
410 | RTFILE_O_WRITE | RTFILE_O_APPEND);
411 if (RT_SUCCESS(rc))
412 {
413 LogFlowFunc(("Opening \"%s\" (fMode=0x%x) for writing ...\n",
414 pszFile, fMode));
415
416 m_hFile = hFile;
417 m_strFile = pszPath;
418 }
419
420 RTStrFree(pszFile);
421 }
422 else
423 rc = VERR_NO_MEMORY;
424 }
425 else
426 rc = VINF_SUCCESS;
427
428 if (RT_SUCCESS(rc))
429 {
430 rc = RTFileWrite(m_hFile, pvData, cbData,
431 NULL /* No partial writes */);
432
433 if (RT_SUCCESS(rc))
434 rc = dataSetStatus(cbData);
435 }
436
437 return rc;
438}
439
440///////////////////////////////////////////////////////////////////////////////
441
442GuestDnD* GuestDnD::s_pInstance = NULL;
443
444GuestDnD::GuestDnD(const ComObjPtr<Guest> &pGuest)
445 : m_pGuest(pGuest)
446{
447 LogFlowFuncEnter();
448
449 m_pResponse = new GuestDnDResponse(pGuest);
450
451 /* List of supported default MIME types. */
452 const com::Utf8Str arrEntries[] = { VBOX_DND_FORMATS_DEFAULT };
453 for (size_t i = 0; i < RT_ELEMENTS(arrEntries); i++)
454 m_strDefaultFormats.push_back(arrEntries[i]);
455}
456
457GuestDnD::~GuestDnD(void)
458{
459 LogFlowFuncEnter();
460
461 if (m_pResponse)
462 delete m_pResponse;
463}
464
465int GuestDnD::adjustScreenCoordinates(ULONG uScreenId, ULONG *puX, ULONG *puY) const
466{
467 /** @todo r=andy Save the current screen's shifting coordinates to speed things up.
468 * Only query for new offsets when the screen ID has changed. */
469
470 /* For multi-monitor support we need to add shift values to the coordinates
471 * (depending on the screen number). */
472 ComObjPtr<Console> pConsole = m_pGuest->i_getConsole();
473 ComPtr<IDisplay> pDisplay;
474 HRESULT hr = pConsole->COMGETTER(Display)(pDisplay.asOutParam());
475 if (FAILED(hr))
476 return hr;
477
478 ULONG dummy;
479 LONG xShift, yShift;
480 hr = pDisplay->GetScreenResolution(uScreenId, &dummy, &dummy, &dummy,
481 &xShift, &yShift);
482 if (FAILED(hr))
483 return hr;
484
485 if (puX)
486 *puX += xShift;
487 if (puY)
488 *puY += yShift;
489
490#ifdef DEBUG_andy
491 LogFlowFunc(("uScreenId=%RU32, x=%RU32, y=%RU32\n",
492 uScreenId, puX ? *puX : 0, puY ? *puY : 0));
493#endif
494 return VINF_SUCCESS;
495}
496
497int GuestDnD::hostCall(uint32_t u32Function, uint32_t cParms, PVBOXHGCMSVCPARM paParms) const
498{
499 Assert(!m_pGuest.isNull());
500 ComObjPtr<Console> pConsole = m_pGuest->i_getConsole();
501
502 /* Forward the information to the VMM device. */
503 Assert(!pConsole.isNull());
504 VMMDev *pVMMDev = pConsole->i_getVMMDev();
505 if (!pVMMDev)
506 return VERR_COM_OBJECT_NOT_FOUND;
507
508 int rc = pVMMDev->hgcmHostCall("VBoxDragAndDropSvc", u32Function,
509 cParms, paParms);
510 LogFlowFunc(("uMsg=%RU32, cParms=%RU32, rc=%Rrc\n",
511 u32Function, cParms, rc));
512 return rc;
513}
514
515/* static */
516com::Utf8Str GuestDnD::toFormatString(const std::vector<com::Utf8Str> &lstSupportedFormats,
517 const std::vector<com::Utf8Str> &lstFormats)
518{
519 com::Utf8Str strFormat;
520 for (size_t i = 0; i < lstFormats.size(); ++i)
521 {
522 const com::Utf8Str &f = lstFormats.at(i);
523 /* Only keep allowed format types. */
524 if (std::find(lstSupportedFormats.begin(),
525 lstSupportedFormats.end(), f) != lstSupportedFormats.end())
526 strFormat += f + "\r\n";
527 }
528
529 return strFormat;
530}
531
532/* static */
533void GuestDnD::toFormatVector(const std::vector<com::Utf8Str> &lstSupportedFormats,
534 const com::Utf8Str &strFormats,
535 std::vector<com::Utf8Str> &vecformats)
536{
537 RTCList<RTCString> lstFormats = strFormats.split("\r\n");
538 size_t i = 0;
539 while (i < lstFormats.size())
540 {
541 /* Only keep allowed format types. */
542 if (std::find(lstSupportedFormats.begin(),
543 lstSupportedFormats.end(), lstFormats.at(i)) == lstSupportedFormats.end())
544 lstFormats.removeAt(i);
545 else
546 ++i;
547 }
548
549 for (i = 0; i < lstFormats.size(); i++)
550 {
551 const Utf8Str &f = lstFormats.at(i);
552 if (std::find(lstSupportedFormats.begin(),
553 lstSupportedFormats.end(), f) != lstSupportedFormats.end())
554 vecformats.push_back(lstFormats[i]);
555 }
556}
557
558/* static */
559uint32_t GuestDnD::toHGCMAction(DnDAction_T enmAction)
560{
561 uint32_t uAction = DND_IGNORE_ACTION;
562 switch (enmAction)
563 {
564 case DnDAction_Copy:
565 uAction = DND_COPY_ACTION;
566 break;
567 case DnDAction_Move:
568 uAction = DND_MOVE_ACTION;
569 break;
570 case DnDAction_Link:
571 /* For now it doesn't seems useful to allow a link
572 action between host & guest. Later? */
573 case DnDAction_Ignore:
574 /* Ignored. */
575 break;
576 default:
577 AssertMsgFailed(("Action %RU32 not recognized!\n", enmAction));
578 break;
579 }
580
581 return uAction;
582}
583
584/* static */
585void GuestDnD::toHGCMActions(DnDAction_T enmDefAction,
586 uint32_t *puDefAction,
587 const std::vector<DnDAction_T> vecAllowedActions,
588 uint32_t *puAllowedActions)
589{
590 if (puDefAction)
591 *puDefAction = toHGCMAction(enmDefAction);
592 if (puAllowedActions)
593 {
594 *puAllowedActions = DND_IGNORE_ACTION;
595
596 /* First convert the allowed actions to a bit array. */
597 for (size_t i = 0; i < vecAllowedActions.size(); ++i)
598 *puAllowedActions |= toHGCMAction(vecAllowedActions[i]);
599
600 /* Second check if the default action is a valid action and if not so try
601 * to find an allowed action. */
602 if (isDnDIgnoreAction(*puAllowedActions))
603 {
604 if (hasDnDCopyAction(*puAllowedActions))
605 *puAllowedActions = DND_COPY_ACTION;
606 else if (hasDnDMoveAction(*puAllowedActions))
607 *puAllowedActions = DND_MOVE_ACTION;
608 }
609 }
610}
611
612/* static */
613DnDAction_T GuestDnD::toMainAction(uint32_t uAction)
614{
615 /* For now it doesn't seems useful to allow a
616 * link action between host & guest. Maybe later! */
617 return (isDnDCopyAction(uAction) ? (DnDAction_T)DnDAction_Copy :
618 isDnDMoveAction(uAction) ? (DnDAction_T)DnDAction_Move :
619 (DnDAction_T)DnDAction_Ignore);
620}
621
622/* static */
623void GuestDnD::toMainActions(uint32_t uActions,
624 std::vector<DnDAction_T> &vecActions)
625{
626 /* For now it doesn't seems useful to allow a
627 * link action between host & guest. Maybe later! */
628 RTCList<DnDAction_T> lstActions;
629 if (hasDnDCopyAction(uActions))
630 lstActions.append(DnDAction_Copy);
631 if (hasDnDMoveAction(uActions))
632 lstActions.append(DnDAction_Move);
633
634 for (size_t i = 0; i < lstActions.size(); ++i)
635 vecActions.push_back(lstActions.at(i));
636}
637
638/* static */
639DECLCALLBACK(int) GuestDnD::notifyDnDDispatcher(void *pvExtension, uint32_t u32Function,
640 void *pvParms, uint32_t cbParms)
641{
642 LogFlowFunc(("pvExtension=%p, u32Function=%RU32, pvParms=%p, cbParms=%RU32\n",
643 pvExtension, u32Function, pvParms, cbParms));
644
645 GuestDnD *pGuestDnD = reinterpret_cast<GuestDnD*>(pvExtension);
646 AssertPtrReturn(pGuestDnD, VERR_INVALID_POINTER);
647
648 GuestDnDResponse *pResp = pGuestDnD->m_pResponse;
649 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
650
651 int rc;
652 switch (u32Function)
653 {
654 case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
655 {
656 DragAndDropSvc::PVBOXDNDCBHGACKOPDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGACKOPDATA>(pvParms);
657 AssertPtr(pCBData);
658 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGACKOPDATA) == cbParms, VERR_INVALID_PARAMETER);
659 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
660
661 pResp->setDefAction(pCBData->uAction);
662
663 rc = pResp->notifyAboutGuestResponse();
664 break;
665 }
666
667 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
668 {
669 DragAndDropSvc::PVBOXDNDCBHGREQDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGREQDATADATA>(pvParms);
670 AssertPtr(pCBData);
671 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGREQDATADATA) == cbParms, VERR_INVALID_PARAMETER);
672 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
673
674 pResp->setFormat(pCBData->pszFormat);
675
676 rc = pResp->notifyAboutGuestResponse();
677 break;
678 }
679
680 case DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS:
681 {
682 DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA pCBData =
683 reinterpret_cast <DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA>(pvParms);
684 AssertPtr(pCBData);
685 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGEVTPROGRESSDATA) == cbParms, VERR_INVALID_PARAMETER);
686 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_EVT_PROGRESS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
687
688 rc = pResp->setProgress(pCBData->uPercentage, pCBData->uState, pCBData->rc);
689 break;
690 }
691
692# ifdef VBOX_WITH_DRAG_AND_DROP_GH
693 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
694 {
695 DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA pCBData =
696 reinterpret_cast <DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA>(pvParms);
697 AssertPtr(pCBData);
698 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA) == cbParms, VERR_INVALID_PARAMETER);
699 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
700
701 pResp->setFormat(pCBData->pszFormat);
702 pResp->setDefAction(pCBData->uDefAction);
703 pResp->setAllActions(pCBData->uAllActions);
704
705 rc = pResp->notifyAboutGuestResponse();
706 break;
707 }
708
709 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
710 {
711 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
712 AssertPtr(pCBData);
713 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
714 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
715
716 rc = pGuestDnD->onGHSendData(pResp, pCBData->pvData, pCBData->cbData,
717 pCBData->cbTotalSize);
718 break;
719 }
720
721 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
722 {
723 DragAndDropSvc::PVBOXDNDCBSNDDIRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDIRDATA>(pvParms);
724 AssertPtr(pCBData);
725 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDIRDATA) == cbParms, VERR_INVALID_PARAMETER);
726 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
727
728 rc = pGuestDnD->onGHSendDir(pResp, pCBData->pszPath, pCBData->cbPath, pCBData->fMode);
729 break;
730 }
731
732 case DragAndDropSvc::GUEST_DND_GH_SND_FILE:
733 {
734 DragAndDropSvc::PVBOXDNDCBSNDFILEDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEDATA>(pvParms);
735 AssertPtr(pCBData);
736 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEDATA) == cbParms, VERR_INVALID_PARAMETER);
737 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
738
739 rc = pGuestDnD->onGHSendFile(pResp, pCBData->pszFilePath, pCBData->cbFilePath,
740 pCBData->pvData, pCBData->cbData, pCBData->fMode);
741 break;
742 }
743
744 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
745 {
746 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
747 AssertPtr(pCBData);
748 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
749 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
750
751 /* Cleanup. */
752 pResp->reset();
753 rc = pResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc);
754 break;
755 }
756# endif /* VBOX_WITH_DRAG_AND_DROP_GH */
757 default:
758 rc = VERR_NOT_SUPPORTED; /* Tell the guest. */
759 break;
760 }
761
762 LogFlowFunc(("Returning rc=%Rrc\n", rc));
763 return rc;
764}
765
766# ifdef VBOX_WITH_DRAG_AND_DROP_GH
767int GuestDnD::onGHSendData(GuestDnDResponse *pResp,
768 const void *pvData, size_t cbData, size_t cbTotalSize)
769{
770 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
771 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
772 AssertReturn(cbData, VERR_INVALID_PARAMETER);
773 AssertReturn(cbTotalSize, VERR_INVALID_PARAMETER);
774
775 int rc = pResp->dataAdd(pvData, cbData, NULL /* Current size */);
776 if (RT_SUCCESS(rc))
777 rc = pResp->dataSetStatus(cbData, cbTotalSize);
778
779 LogFlowFuncLeaveRC(rc);
780 return rc;
781}
782
783int GuestDnD::onGHSendDir(GuestDnDResponse *pResp,
784 const char *pszPath, size_t cbPath, uint32_t fMode)
785{
786 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
787 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
788 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
789
790 LogFlowFunc(("pszPath=%s, cbPath=%zu, fMode=0x%x\n",
791 pszPath, cbPath, fMode));
792
793 int rc;
794 char *pszDir = RTPathJoinA(pResp->dropDir().c_str(), pszPath);
795 if (pszDir)
796 {
797 rc = RTDirCreateFullPath(pszDir, fMode);
798 RTStrFree(pszDir);
799 }
800 else
801 rc = VERR_NO_MEMORY;
802
803 if (RT_SUCCESS(rc))
804 rc = pResp->dataSetStatus(cbPath);
805
806 LogFlowFuncLeaveRC(rc);
807 return rc;
808}
809
810int GuestDnD::onGHSendFile(GuestDnDResponse *pResp,
811 const char *pszPath, size_t cbPath,
812 void *pvData, size_t cbData, uint32_t fMode)
813{
814 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
815 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
816 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
817
818 LogFlowFunc(("pszPath=%s, cbPath=%zu, fMode=0x%x\n",
819 pszPath, cbPath, fMode));
820
821 int rc = pResp->writeToFile(pszPath, cbPath,
822 pvData, cbData, fMode);
823 LogFlowFuncLeaveRC(rc);
824 return rc;
825}
826# endif /* VBOX_WITH_DRAG_AND_DROP_GH */
827
828///////////////////////////////////////////////////////////////////////////////
829
830GuestDnDBase::GuestDnDBase(void)
831{
832 m_strFormats = GuestDnDInst()->defaultFormats();
833}
834
835HRESULT GuestDnDBase::isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported)
836{
837 *aSupported = std::find(m_strFormats.begin(),
838 m_strFormats.end(), aFormat) != m_strFormats.end()
839 ? TRUE : FALSE;
840 return S_OK;
841}
842
843HRESULT GuestDnDBase::getFormats(std::vector<com::Utf8Str> &aFormats)
844{
845 aFormats = m_strFormats;
846
847 return S_OK;
848}
849
850HRESULT GuestDnDBase::addFormats(const std::vector<com::Utf8Str> &aFormats)
851{
852 for (size_t i = 0; i < aFormats.size(); ++i)
853 {
854 Utf8Str strFormat = aFormats.at(i);
855 if (std::find(m_strFormats.begin(),
856 m_strFormats.end(), strFormat) == m_strFormats.end())
857 {
858 m_strFormats.push_back(strFormat);
859 }
860 }
861
862 return S_OK;
863}
864
865HRESULT GuestDnDBase::removeFormats(const std::vector<com::Utf8Str> &aFormats)
866{
867 for (size_t i = 0; i < aFormats.size(); ++i)
868 {
869 Utf8Str strFormat = aFormats.at(i);
870 std::vector<com::Utf8Str>::iterator itFormat = std::find(m_strFormats.begin(),
871 m_strFormats.end(), strFormat);
872 if (itFormat != m_strFormats.end())
873 m_strFormats.erase(itFormat);
874 }
875
876 return S_OK;
877}
878
879#endif /* VBOX_WITH_DRAG_AND_DROP */
880
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