VirtualBox

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

Last change on this file since 51797 was 51612, checked in by vboxsync, 10 years ago

6813 Use of server side API wrapper code - ConsoleImpl.cpp

  • 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 51612 2014-06-12 16:46:20Z 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->getConsole();
473 ComPtr<IDisplay> pDisplay;
474 HRESULT hr = pConsole->COMGETTER(Display)(pDisplay.asOutParam());
475 if (FAILED(hr))
476 return hr;
477
478 LONG xShift, yShift;
479 hr = pDisplay->GetScreenResolution(uScreenId, NULL, NULL, NULL,
480 &xShift, &yShift);
481 if (FAILED(hr))
482 return hr;
483
484 if (puX)
485 *puX += xShift;
486 if (puY)
487 *puY += yShift;
488
489#ifdef DEBUG_andy
490 LogFlowFunc(("uScreenId=%RU32, x=%RU32, y=%RU32\n",
491 uScreenId, puX ? *puX : 0, puY ? *puY : 0));
492#endif
493 return VINF_SUCCESS;
494}
495
496int GuestDnD::hostCall(uint32_t u32Function, uint32_t cParms, PVBOXHGCMSVCPARM paParms) const
497{
498 Assert(!m_pGuest.isNull());
499 ComObjPtr<Console> pConsole = m_pGuest->getConsole();
500
501 /* Forward the information to the VMM device. */
502 Assert(!pConsole.isNull());
503 VMMDev *pVMMDev = pConsole->i_getVMMDev();
504 if (!pVMMDev)
505 return VERR_COM_OBJECT_NOT_FOUND;
506
507 int rc = pVMMDev->hgcmHostCall("VBoxDragAndDropSvc", u32Function,
508 cParms, paParms);
509 LogFlowFunc(("uMsg=%RU32, cParms=%RU32, rc=%Rrc\n",
510 u32Function, cParms, rc));
511 return rc;
512}
513
514/* static */
515com::Utf8Str GuestDnD::toFormatString(const std::vector<com::Utf8Str> &lstSupportedFormats,
516 const std::vector<com::Utf8Str> &lstFormats)
517{
518 com::Utf8Str strFormat;
519 for (size_t i = 0; i < lstFormats.size(); ++i)
520 {
521 const com::Utf8Str &f = lstFormats.at(i);
522 /* Only keep allowed format types. */
523 if (std::find(lstSupportedFormats.begin(),
524 lstSupportedFormats.end(), f) != lstSupportedFormats.end())
525 strFormat += f + "\r\n";
526 }
527
528 return strFormat;
529}
530
531/* static */
532void GuestDnD::toFormatVector(const std::vector<com::Utf8Str> &lstSupportedFormats,
533 const com::Utf8Str &strFormats,
534 std::vector<com::Utf8Str> &vecformats)
535{
536 RTCList<RTCString> lstFormats = strFormats.split("\r\n");
537 size_t i = 0;
538 while (i < lstFormats.size())
539 {
540 /* Only keep allowed format types. */
541 if (std::find(lstSupportedFormats.begin(),
542 lstSupportedFormats.end(), lstFormats.at(i)) == lstSupportedFormats.end())
543 lstFormats.removeAt(i);
544 else
545 ++i;
546 }
547
548 for (i = 0; i < lstFormats.size(); i++)
549 {
550 const Utf8Str &f = lstFormats.at(i);
551 if (std::find(lstSupportedFormats.begin(),
552 lstSupportedFormats.end(), f) != lstSupportedFormats.end())
553 vecformats.push_back(lstFormats[i]);
554 }
555}
556
557/* static */
558uint32_t GuestDnD::toHGCMAction(DnDAction_T enmAction)
559{
560 uint32_t uAction = DND_IGNORE_ACTION;
561 switch (enmAction)
562 {
563 case DnDAction_Copy:
564 uAction = DND_COPY_ACTION;
565 break;
566 case DnDAction_Move:
567 uAction = DND_MOVE_ACTION;
568 break;
569 case DnDAction_Link:
570 /* For now it doesn't seems useful to allow a link
571 action between host & guest. Later? */
572 case DnDAction_Ignore:
573 /* Ignored. */
574 break;
575 default:
576 AssertMsgFailed(("Action %RU32 not recognized!\n", enmAction));
577 break;
578 }
579
580 return uAction;
581}
582
583/* static */
584void GuestDnD::toHGCMActions(DnDAction_T enmDefAction,
585 uint32_t *puDefAction,
586 const std::vector<DnDAction_T> vecAllowedActions,
587 uint32_t *puAllowedActions)
588{
589 if (puDefAction)
590 *puDefAction = toHGCMAction(enmDefAction);
591 if (puAllowedActions)
592 {
593 *puAllowedActions = DND_IGNORE_ACTION;
594
595 /* First convert the allowed actions to a bit array. */
596 for (size_t i = 0; i < vecAllowedActions.size(); ++i)
597 *puAllowedActions |= toHGCMAction(vecAllowedActions[i]);
598
599 /* Second check if the default action is a valid action and if not so try
600 * to find an allowed action. */
601 if (isDnDIgnoreAction(*puAllowedActions))
602 {
603 if (hasDnDCopyAction(*puAllowedActions))
604 *puAllowedActions = DND_COPY_ACTION;
605 else if (hasDnDMoveAction(*puAllowedActions))
606 *puAllowedActions = DND_MOVE_ACTION;
607 }
608 }
609}
610
611/* static */
612DnDAction_T GuestDnD::toMainAction(uint32_t uAction)
613{
614 /* For now it doesn't seems useful to allow a
615 * link action between host & guest. Maybe later! */
616 return (isDnDCopyAction(uAction) ? (DnDAction_T)DnDAction_Copy :
617 isDnDMoveAction(uAction) ? (DnDAction_T)DnDAction_Move :
618 (DnDAction_T)DnDAction_Ignore);
619}
620
621/* static */
622void GuestDnD::toMainActions(uint32_t uActions,
623 std::vector<DnDAction_T> &vecActions)
624{
625 /* For now it doesn't seems useful to allow a
626 * link action between host & guest. Maybe later! */
627 RTCList<DnDAction_T> lstActions;
628 if (hasDnDCopyAction(uActions))
629 lstActions.append(DnDAction_Copy);
630 if (hasDnDMoveAction(uActions))
631 lstActions.append(DnDAction_Move);
632
633 for (size_t i = 0; i < lstActions.size(); ++i)
634 vecActions.push_back(lstActions.at(i));
635}
636
637/* static */
638DECLCALLBACK(int) GuestDnD::notifyDnDDispatcher(void *pvExtension, uint32_t u32Function,
639 void *pvParms, uint32_t cbParms)
640{
641 LogFlowFunc(("pvExtension=%p, u32Function=%RU32, pvParms=%p, cbParms=%RU32\n",
642 pvExtension, u32Function, pvParms, cbParms));
643
644 GuestDnD *pGuestDnD = reinterpret_cast<GuestDnD*>(pvExtension);
645 AssertPtrReturn(pGuestDnD, VERR_INVALID_POINTER);
646
647 GuestDnDResponse *pResp = pGuestDnD->m_pResponse;
648 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
649
650 int rc;
651 switch (u32Function)
652 {
653 case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
654 {
655 DragAndDropSvc::PVBOXDNDCBHGACKOPDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGACKOPDATA>(pvParms);
656 AssertPtr(pCBData);
657 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGACKOPDATA) == cbParms, VERR_INVALID_PARAMETER);
658 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
659
660 pResp->setDefAction(pCBData->uAction);
661
662 rc = pResp->notifyAboutGuestResponse();
663 break;
664 }
665
666 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
667 {
668 DragAndDropSvc::PVBOXDNDCBHGREQDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGREQDATADATA>(pvParms);
669 AssertPtr(pCBData);
670 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGREQDATADATA) == cbParms, VERR_INVALID_PARAMETER);
671 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
672
673 pResp->setFormat(pCBData->pszFormat);
674
675 rc = pResp->notifyAboutGuestResponse();
676 break;
677 }
678
679 case DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS:
680 {
681 DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA pCBData =
682 reinterpret_cast <DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA>(pvParms);
683 AssertPtr(pCBData);
684 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGEVTPROGRESSDATA) == cbParms, VERR_INVALID_PARAMETER);
685 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_EVT_PROGRESS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
686
687 rc = pResp->setProgress(pCBData->uPercentage, pCBData->uState, pCBData->rc);
688 break;
689 }
690
691# ifdef VBOX_WITH_DRAG_AND_DROP_GH
692 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
693 {
694 DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA pCBData =
695 reinterpret_cast <DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA>(pvParms);
696 AssertPtr(pCBData);
697 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA) == cbParms, VERR_INVALID_PARAMETER);
698 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
699
700 pResp->setFormat(pCBData->pszFormat);
701 pResp->setDefAction(pCBData->uDefAction);
702 pResp->setAllActions(pCBData->uAllActions);
703
704 rc = pResp->notifyAboutGuestResponse();
705 break;
706 }
707
708 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
709 {
710 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
711 AssertPtr(pCBData);
712 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
713 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
714
715 rc = pGuestDnD->onGHSendData(pResp, pCBData->pvData, pCBData->cbData,
716 pCBData->cbTotalSize);
717 break;
718 }
719
720 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
721 {
722 DragAndDropSvc::PVBOXDNDCBSNDDIRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDIRDATA>(pvParms);
723 AssertPtr(pCBData);
724 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDIRDATA) == cbParms, VERR_INVALID_PARAMETER);
725 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
726
727 rc = pGuestDnD->onGHSendDir(pResp, pCBData->pszPath, pCBData->cbPath, pCBData->fMode);
728 break;
729 }
730
731 case DragAndDropSvc::GUEST_DND_GH_SND_FILE:
732 {
733 DragAndDropSvc::PVBOXDNDCBSNDFILEDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEDATA>(pvParms);
734 AssertPtr(pCBData);
735 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEDATA) == cbParms, VERR_INVALID_PARAMETER);
736 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
737
738 rc = pGuestDnD->onGHSendFile(pResp, pCBData->pszFilePath, pCBData->cbFilePath,
739 pCBData->pvData, pCBData->cbData, pCBData->fMode);
740 break;
741 }
742
743 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
744 {
745 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
746 AssertPtr(pCBData);
747 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
748 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
749
750 /* Cleanup. */
751 pResp->reset();
752 rc = pResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc);
753 break;
754 }
755# endif /* VBOX_WITH_DRAG_AND_DROP_GH */
756 default:
757 rc = VERR_NOT_SUPPORTED; /* Tell the guest. */
758 break;
759 }
760
761 LogFlowFunc(("Returning rc=%Rrc\n", rc));
762 return rc;
763}
764
765# ifdef VBOX_WITH_DRAG_AND_DROP_GH
766int GuestDnD::onGHSendData(GuestDnDResponse *pResp,
767 const void *pvData, size_t cbData, size_t cbTotalSize)
768{
769 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
770 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
771 AssertReturn(cbData, VERR_INVALID_PARAMETER);
772 AssertReturn(cbTotalSize, VERR_INVALID_PARAMETER);
773
774 int rc = pResp->dataAdd(pvData, cbData, NULL /* Current size */);
775 if (RT_SUCCESS(rc))
776 rc = pResp->dataSetStatus(cbData, cbTotalSize);
777
778 LogFlowFuncLeaveRC(rc);
779 return rc;
780}
781
782int GuestDnD::onGHSendDir(GuestDnDResponse *pResp,
783 const char *pszPath, size_t cbPath, uint32_t fMode)
784{
785 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
786 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
787 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
788
789 LogFlowFunc(("pszPath=%s, cbPath=%zu, fMode=0x%x\n",
790 pszPath, cbPath, fMode));
791
792 int rc;
793 char *pszDir = RTPathJoinA(pResp->dropDir().c_str(), pszPath);
794 if (pszDir)
795 {
796 rc = RTDirCreateFullPath(pszDir, fMode);
797 RTStrFree(pszDir);
798 }
799 else
800 rc = VERR_NO_MEMORY;
801
802 if (RT_SUCCESS(rc))
803 rc = pResp->dataSetStatus(cbPath);
804
805 LogFlowFuncLeaveRC(rc);
806 return rc;
807}
808
809int GuestDnD::onGHSendFile(GuestDnDResponse *pResp,
810 const char *pszPath, size_t cbPath,
811 void *pvData, size_t cbData, uint32_t fMode)
812{
813 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
814 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
815 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
816
817 LogFlowFunc(("pszPath=%s, cbPath=%zu, fMode=0x%x\n",
818 pszPath, cbPath, fMode));
819
820 int rc = pResp->writeToFile(pszPath, cbPath,
821 pvData, cbData, fMode);
822 LogFlowFuncLeaveRC(rc);
823 return rc;
824}
825# endif /* VBOX_WITH_DRAG_AND_DROP_GH */
826
827///////////////////////////////////////////////////////////////////////////////
828
829GuestDnDBase::GuestDnDBase(void)
830{
831 m_strFormats = GuestDnDInst()->defaultFormats();
832}
833
834HRESULT GuestDnDBase::isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported)
835{
836 *aSupported = std::find(m_strFormats.begin(),
837 m_strFormats.end(), aFormat) != m_strFormats.end()
838 ? TRUE : FALSE;
839 return S_OK;
840}
841
842HRESULT GuestDnDBase::getFormats(std::vector<com::Utf8Str> &aFormats)
843{
844 aFormats = m_strFormats;
845
846 return S_OK;
847}
848
849HRESULT GuestDnDBase::addFormats(const std::vector<com::Utf8Str> &aFormats)
850{
851 for (size_t i = 0; i < aFormats.size(); ++i)
852 {
853 Utf8Str strFormat = aFormats.at(i);
854 if (std::find(m_strFormats.begin(),
855 m_strFormats.end(), strFormat) == m_strFormats.end())
856 {
857 m_strFormats.push_back(strFormat);
858 }
859 }
860
861 return S_OK;
862}
863
864HRESULT GuestDnDBase::removeFormats(const std::vector<com::Utf8Str> &aFormats)
865{
866 for (size_t i = 0; i < aFormats.size(); ++i)
867 {
868 Utf8Str strFormat = aFormats.at(i);
869 std::vector<com::Utf8Str>::iterator itFormat = std::find(m_strFormats.begin(),
870 m_strFormats.end(), strFormat);
871 if (itFormat != m_strFormats.end())
872 m_strFormats.erase(itFormat);
873 }
874
875 return S_OK;
876}
877
878#endif /* VBOX_WITH_DRAG_AND_DROP */
879
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