VirtualBox

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

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

DnD: Removed obsolete COMPOUND_TEXT; spaces.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.4 KB
Line 
1/* $Id: GuestDnDPrivate.cpp 51529 2014-06-04 12:11:43Z 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 MIME types.
452 * Note: If you add something here, make sure you test it with all supported guest OSes!
453 ** @todo Make this MIME list configurable / extendable (by extra data?). Currently
454 * this is done hardcoded on every guest platform (*NIX/Windows).
455 */
456 const com::Utf8Str arrEntries[] = {
457 "text/uri-list",
458 /* Text */
459 "text/plain;charset=utf-8",
460 "UTF8_STRING",
461 "text/plain",
462 "TEXT",
463 "STRING",
464 /* OpenOffice formats */
465 "application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\"",
466 "application/x-openoffice-drawing;windows_formatname=\"Drawing Format\"",
467 };
468
469 for (size_t i = 0; i < RT_ELEMENTS(arrEntries); i++)
470 m_strSupportedFormats.push_back(arrEntries[0]);
471}
472
473GuestDnD::~GuestDnD(void)
474{
475 LogFlowFuncEnter();
476
477 if (m_pResponse)
478 delete m_pResponse;
479}
480
481int GuestDnD::adjustScreenCoordinates(ULONG uScreenId, ULONG *puX, ULONG *puY) const
482{
483 /** @todo r=andy Save the current screen's shifting coordinates to speed things up.
484 * Only query for new offsets when the screen ID has changed. */
485
486 /* For multi-monitor support we need to add shift values to the coordinates
487 * (depending on the screen number). */
488 ComObjPtr<Console> pConsole = m_pGuest->getConsole();
489 ComPtr<IDisplay> pDisplay;
490 HRESULT hr = pConsole->COMGETTER(Display)(pDisplay.asOutParam());
491 if (FAILED(hr))
492 return hr;
493
494 LONG xShift, yShift;
495 hr = pDisplay->GetScreenResolution(uScreenId, NULL, NULL, NULL,
496 &xShift, &yShift);
497 if (FAILED(hr))
498 return hr;
499
500 if (puX)
501 *puX += xShift;
502 if (puY)
503 *puY += yShift;
504
505#ifdef DEBUG_andy
506 LogFlowFunc(("uScreenId=%RU32, x=%RU32, y=%RU32\n",
507 uScreenId, puX ? *puX : 0, puY ? *puY : 0));
508#endif
509 return VINF_SUCCESS;
510}
511
512int GuestDnD::hostCall(uint32_t u32Function, uint32_t cParms, PVBOXHGCMSVCPARM paParms) const
513{
514 Assert(!m_pGuest.isNull());
515 ComObjPtr<Console> pConsole = m_pGuest->getConsole();
516
517 /* Forward the information to the VMM device. */
518 Assert(!pConsole.isNull());
519 VMMDev *pVMMDev = pConsole->getVMMDev();
520 if (!pVMMDev)
521 return VERR_COM_OBJECT_NOT_FOUND;
522
523 int rc = pVMMDev->hgcmHostCall("VBoxDragAndDropSvc", u32Function,
524 cParms, paParms);
525 LogFlowFunc(("uMsg=%RU32, cParms=%RU32, rc=%Rrc\n",
526 u32Function, cParms, rc));
527 return rc;
528}
529
530/* static */
531com::Utf8Str GuestDnD::toFormatString(const std::vector<com::Utf8Str> &lstSupportedFormats,
532 const std::vector<com::Utf8Str> &lstFormats)
533{
534 com::Utf8Str strFormat;
535 for (size_t i = 0; i < lstFormats.size(); ++i)
536 {
537 const com::Utf8Str &f = lstFormats.at(i);
538 /* Only keep allowed format types. */
539 if (std::find(lstSupportedFormats.begin(),
540 lstSupportedFormats.end(), f) != lstSupportedFormats.end())
541 strFormat += f + "\r\n";
542 }
543
544 return strFormat;
545}
546
547/* static */
548void GuestDnD::toFormatVector(const std::vector<com::Utf8Str> &lstSupportedFormats,
549 const com::Utf8Str &strFormats,
550 std::vector<com::Utf8Str> &vecformats)
551{
552 RTCList<RTCString> lstFormats = strFormats.split("\r\n");
553 size_t i = 0;
554 while (i < lstFormats.size())
555 {
556 /* Only keep allowed format types. */
557 if (std::find(lstSupportedFormats.begin(),
558 lstSupportedFormats.end(), lstFormats.at(i)) == lstSupportedFormats.end())
559 lstFormats.removeAt(i);
560 else
561 ++i;
562 }
563
564 for (i = 0; i < lstFormats.size(); i++)
565 {
566 const Utf8Str &f = lstFormats.at(i);
567 if (std::find(lstSupportedFormats.begin(),
568 lstSupportedFormats.end(), f) != lstSupportedFormats.end())
569 vecformats.push_back(lstFormats[i]);
570 }
571}
572
573/* static */
574uint32_t GuestDnD::toHGCMAction(DnDAction_T enmAction)
575{
576 uint32_t uAction = DND_IGNORE_ACTION;
577 switch (enmAction)
578 {
579 case DnDAction_Copy:
580 uAction = DND_COPY_ACTION;
581 break;
582 case DnDAction_Move:
583 uAction = DND_MOVE_ACTION;
584 break;
585 case DnDAction_Link:
586 /* For now it doesn't seems useful to allow a link
587 action between host & guest. Later? */
588 case DnDAction_Ignore:
589 /* Ignored. */
590 break;
591 default:
592 AssertMsgFailed(("Action %RU32 not recognized!\n", enmAction));
593 break;
594 }
595
596 return uAction;
597}
598
599/* static */
600void GuestDnD::toHGCMActions(DnDAction_T enmDefAction,
601 uint32_t *puDefAction,
602 const std::vector<DnDAction_T> vecAllowedActions,
603 uint32_t *puAllowedActions)
604{
605 if (puDefAction)
606 *puDefAction = toHGCMAction(enmDefAction);
607 if (puAllowedActions)
608 {
609 *puAllowedActions = DND_IGNORE_ACTION;
610
611 /* First convert the allowed actions to a bit array. */
612 for (size_t i = 0; i < vecAllowedActions.size(); ++i)
613 *puAllowedActions |= toHGCMAction(vecAllowedActions[i]);
614
615 /* Second check if the default action is a valid action and if not so try
616 * to find an allowed action. */
617 if (isDnDIgnoreAction(*puAllowedActions))
618 {
619 if (hasDnDCopyAction(*puAllowedActions))
620 *puAllowedActions = DND_COPY_ACTION;
621 else if (hasDnDMoveAction(*puAllowedActions))
622 *puAllowedActions = DND_MOVE_ACTION;
623 }
624 }
625}
626
627/* static */
628DnDAction_T GuestDnD::toMainAction(uint32_t uAction)
629{
630 /* For now it doesn't seems useful to allow a
631 * link action between host & guest. Maybe later! */
632 return (isDnDCopyAction(uAction) ? (DnDAction_T)DnDAction_Copy :
633 isDnDMoveAction(uAction) ? (DnDAction_T)DnDAction_Move :
634 (DnDAction_T)DnDAction_Ignore);
635}
636
637/* static */
638void GuestDnD::toMainActions(uint32_t uActions,
639 std::vector<DnDAction_T> &vecActions)
640{
641 /* For now it doesn't seems useful to allow a
642 * link action between host & guest. Maybe later! */
643 RTCList<DnDAction_T> lstActions;
644 if (hasDnDCopyAction(uActions))
645 lstActions.append(DnDAction_Copy);
646 if (hasDnDMoveAction(uActions))
647 lstActions.append(DnDAction_Move);
648
649 for (size_t i = 0; i < lstActions.size(); ++i)
650 vecActions.push_back(lstActions.at(i));
651}
652
653/* static */
654DECLCALLBACK(int) GuestDnD::notifyDnDDispatcher(void *pvExtension, uint32_t u32Function,
655 void *pvParms, uint32_t cbParms)
656{
657 LogFlowFunc(("pvExtension=%p, u32Function=%RU32, pvParms=%p, cbParms=%RU32\n",
658 pvExtension, u32Function, pvParms, cbParms));
659
660 GuestDnD *pGuestDnD = reinterpret_cast<GuestDnD*>(pvExtension);
661 AssertPtrReturn(pGuestDnD, VERR_INVALID_POINTER);
662
663 GuestDnDResponse *pResp = pGuestDnD->m_pResponse;
664 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
665
666 int rc;
667 switch (u32Function)
668 {
669 case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
670 {
671 DragAndDropSvc::PVBOXDNDCBHGACKOPDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGACKOPDATA>(pvParms);
672 AssertPtr(pCBData);
673 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGACKOPDATA) == cbParms, VERR_INVALID_PARAMETER);
674 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
675
676 pResp->setDefAction(pCBData->uAction);
677
678 rc = pResp->notifyAboutGuestResponse();
679 break;
680 }
681
682 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
683 {
684 DragAndDropSvc::PVBOXDNDCBHGREQDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGREQDATADATA>(pvParms);
685 AssertPtr(pCBData);
686 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGREQDATADATA) == cbParms, VERR_INVALID_PARAMETER);
687 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
688
689 pResp->setFormat(pCBData->pszFormat);
690
691 rc = pResp->notifyAboutGuestResponse();
692 break;
693 }
694
695 case DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS:
696 {
697 DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA pCBData =
698 reinterpret_cast <DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA>(pvParms);
699 AssertPtr(pCBData);
700 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGEVTPROGRESSDATA) == cbParms, VERR_INVALID_PARAMETER);
701 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_EVT_PROGRESS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
702
703 rc = pResp->setProgress(pCBData->uPercentage, pCBData->uState, pCBData->rc);
704 break;
705 }
706
707# ifdef VBOX_WITH_DRAG_AND_DROP_GH
708 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
709 {
710 DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA pCBData =
711 reinterpret_cast <DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA>(pvParms);
712 AssertPtr(pCBData);
713 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA) == cbParms, VERR_INVALID_PARAMETER);
714 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
715
716 pResp->setFormat(pCBData->pszFormat);
717 pResp->setDefAction(pCBData->uDefAction);
718 pResp->setAllActions(pCBData->uAllActions);
719
720 rc = pResp->notifyAboutGuestResponse();
721 break;
722 }
723
724 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
725 {
726 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
727 AssertPtr(pCBData);
728 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
729 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
730
731 rc = pGuestDnD->onGHSendData(pResp, pCBData->pvData, pCBData->cbData,
732 pCBData->cbTotalSize);
733 break;
734 }
735
736 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
737 {
738 DragAndDropSvc::PVBOXDNDCBSNDDIRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDIRDATA>(pvParms);
739 AssertPtr(pCBData);
740 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDIRDATA) == cbParms, VERR_INVALID_PARAMETER);
741 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
742
743 rc = pGuestDnD->onGHSendDir(pResp, pCBData->pszPath, pCBData->cbPath, pCBData->fMode);
744 break;
745 }
746
747 case DragAndDropSvc::GUEST_DND_GH_SND_FILE:
748 {
749 DragAndDropSvc::PVBOXDNDCBSNDFILEDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEDATA>(pvParms);
750 AssertPtr(pCBData);
751 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEDATA) == cbParms, VERR_INVALID_PARAMETER);
752 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
753
754 rc = pGuestDnD->onGHSendFile(pResp, pCBData->pszFilePath, pCBData->cbFilePath,
755 pCBData->pvData, pCBData->cbData, pCBData->fMode);
756 break;
757 }
758
759 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
760 {
761 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
762 AssertPtr(pCBData);
763 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
764 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
765
766 /* Cleanup. */
767 pResp->reset();
768 rc = pResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc);
769 break;
770 }
771# endif /* VBOX_WITH_DRAG_AND_DROP_GH */
772 default:
773 rc = VERR_NOT_SUPPORTED; /* Tell the guest. */
774 break;
775 }
776
777 LogFlowFunc(("Returning rc=%Rrc\n", rc));
778 return rc;
779}
780
781# ifdef VBOX_WITH_DRAG_AND_DROP_GH
782int GuestDnD::onGHSendData(GuestDnDResponse *pResp,
783 const void *pvData, size_t cbData, size_t cbTotalSize)
784{
785 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
786 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
787 AssertReturn(cbData, VERR_INVALID_PARAMETER);
788 AssertReturn(cbTotalSize, VERR_INVALID_PARAMETER);
789
790 int rc = pResp->dataAdd(pvData, cbData, NULL /* Current size */);
791 if (RT_SUCCESS(rc))
792 rc = pResp->dataSetStatus(cbData, cbTotalSize);
793
794 LogFlowFuncLeaveRC(rc);
795 return rc;
796}
797
798int GuestDnD::onGHSendDir(GuestDnDResponse *pResp,
799 const char *pszPath, size_t cbPath, uint32_t fMode)
800{
801 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
802 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
803 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
804
805 LogFlowFunc(("pszPath=%s, cbPath=%zu, fMode=0x%x\n",
806 pszPath, cbPath, fMode));
807
808 int rc;
809 char *pszDir = RTPathJoinA(pResp->dropDir().c_str(), pszPath);
810 if (pszDir)
811 {
812 rc = RTDirCreateFullPath(pszDir, fMode);
813 RTStrFree(pszDir);
814 }
815 else
816 rc = VERR_NO_MEMORY;
817
818 if (RT_SUCCESS(rc))
819 rc = pResp->dataSetStatus(cbPath);
820
821 LogFlowFuncLeaveRC(rc);
822 return rc;
823}
824
825int GuestDnD::onGHSendFile(GuestDnDResponse *pResp,
826 const char *pszPath, size_t cbPath,
827 void *pvData, size_t cbData, uint32_t fMode)
828{
829 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
830 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
831 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
832
833 LogFlowFunc(("pszPath=%s, cbPath=%zu, fMode=0x%x\n",
834 pszPath, cbPath, fMode));
835
836 int rc = pResp->writeToFile(pszPath, cbPath,
837 pvData, cbData, fMode);
838 LogFlowFuncLeaveRC(rc);
839 return rc;
840}
841# endif /* VBOX_WITH_DRAG_AND_DROP_GH */
842
843#endif /* VBOX_WITH_DRAG_AND_DROP */
844
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