VirtualBox

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

Last change on this file since 53517 was 52978, checked in by vboxsync, 10 years ago

IDisplay::GetScreenResolution returns status of the guest monitor.

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