VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestDnDSourceImpl.cpp

Last change on this file was 103415, checked in by vboxsync, 3 months ago

Additions,Main,VMM,Runtime: Fix some unused expression warnings, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 60.0 KB
RevLine 
[51476]1/* $Id: GuestDnDSourceImpl.cpp 103415 2024-02-19 07:52:27Z vboxsync $ */
2/** @file
[55180]3 * VBox Console COM Class implementation - Guest drag and drop source.
[51476]4 */
5
6/*
[98103]7 * Copyright (C) 2014-2023 Oracle and/or its affiliates.
[51476]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
[51476]26 */
27
28
[57358]29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
[67914]32#define LOG_GROUP LOG_GROUP_GUEST_DND //LOG_GROUP_MAIN_GUESTDNDSOURCE
33#include "LoggingNew.h"
34
[51476]35#include "GuestImpl.h"
36#include "GuestDnDSourceImpl.h"
37#include "GuestDnDPrivate.h"
[55644]38#include "ConsoleImpl.h"
[51476]39
40#include "Global.h"
41#include "AutoCaller.h"
[58519]42#include "ThreadTask.h"
[51476]43
[55644]44#include <iprt/asm.h>
[55422]45#include <iprt/dir.h>
46#include <iprt/file.h>
47#include <iprt/path.h>
[55524]48#include <iprt/uri.h>
[55422]49
[51476]50#include <iprt/cpp/utils.h> /* For unconst(). */
51
52#include <VBox/com/array.h>
53
54
[55422]55/**
56 * Base class for a source task.
57 */
[58519]58class GuestDnDSourceTask : public ThreadTask
[55422]59{
60public:
[51476]61
[55422]62 GuestDnDSourceTask(GuestDnDSource *pSource)
[58519]63 : ThreadTask("GenericGuestDnDSourceTask")
64 , mSource(pSource)
65 , mRC(VINF_SUCCESS) { }
[55422]66
67 virtual ~GuestDnDSourceTask(void) { }
68
[85681]69 /** Returns the overall result of the task. */
[55422]70 int getRC(void) const { return mRC; }
[85681]71 /** Returns if the overall result of the task is ok (succeeded) or not. */
[55422]72 bool isOk(void) const { return RT_SUCCESS(mRC); }
73
74protected:
75
[85681]76 /** COM object pointer to the parent (source). */
[55422]77 const ComObjPtr<GuestDnDSource> mSource;
[85681]78 /** Overall result of the task. */
[55422]79 int mRC;
80};
81
82/**
83 * Task structure for receiving data from a source using
84 * a worker thread.
85 */
[85020]86class GuestDnDRecvDataTask : public GuestDnDSourceTask
[55422]87{
88public:
89
[85020]90 GuestDnDRecvDataTask(GuestDnDSource *pSource, GuestDnDRecvCtx *pCtx)
[55422]91 : GuestDnDSourceTask(pSource)
[58519]92 , mpCtx(pCtx)
93 {
94 m_strTaskName = "dndSrcRcvData";
95 }
[55422]96
[58519]97 void handler()
98 {
[85537]99 LogFlowThisFunc(("\n"));
100
101 const ComObjPtr<GuestDnDSource> pThis(mSource);
102 Assert(!pThis.isNull());
103
104 AutoCaller autoCaller(pThis);
[98262]105 if (FAILED(autoCaller.hrc()))
[85537]106 return;
107
108 int vrc = pThis->i_receiveData(mpCtx, RT_INDEFINITE_WAIT /* msTimeout */);
109 if (RT_FAILURE(vrc)) /* In case we missed some error handling within i_receiveData(). */
110 {
111 if (vrc != VERR_CANCELLED)
112 LogRel(("DnD: Receiving data from guest failed with %Rrc\n", vrc));
113
114 /* Make sure to fire a cancel request to the guest side in case something went wrong. */
115 pThis->sendCancel();
116 }
[58519]117 }
118
[85020]119 virtual ~GuestDnDRecvDataTask(void) { }
[55422]120
121protected:
122
123 /** Pointer to receive data context. */
[85018]124 GuestDnDRecvCtx *mpCtx;
[55422]125};
126
[51476]127// constructor / destructor
128/////////////////////////////////////////////////////////////////////////////
129
[97780]130GuestDnDSource::GuestDnDSource(void)
131 : GuestDnDBase(this) { }
[51476]132
[97780]133GuestDnDSource::~GuestDnDSource(void) { }
134
[51476]135HRESULT GuestDnDSource::FinalConstruct(void)
136{
[57221]137 /*
138 * Set the maximum block size this source can handle to 64K. This always has
139 * been hardcoded until now.
140 *
141 * Note: Never ever rely on information from the guest; the host dictates what and
142 * how to do something, so try to negogiate a sensible value here later.
143 */
[85557]144 mData.mcbBlockSize = DND_DEFAULT_CHUNK_SIZE; /** @todo Make this configurable. */
[55514]145
[51476]146 LogFlowThisFunc(("\n"));
147 return BaseFinalConstruct();
148}
149
150void GuestDnDSource::FinalRelease(void)
151{
152 LogFlowThisFuncEnter();
153 uninit();
154 BaseFinalRelease();
155 LogFlowThisFuncLeave();
156}
157
158// public initializer/uninitializer for internal purposes only
159/////////////////////////////////////////////////////////////////////////////
160
[85743]161HRESULT GuestDnDSource::init(const ComObjPtr<Guest>& pGuest)
[51476]162{
163 LogFlowThisFuncEnter();
164
165 /* Enclose the state transition NotReady->InInit->Ready. */
166 AutoInitSpan autoInitSpan(this);
167 AssertReturn(autoInitSpan.isOk(), E_FAIL);
168
169 unconst(m_pGuest) = pGuest;
170
[85739]171 /* Set the response we're going to use for this object.
172 *
173 * At the moment we only have one response total, as we
174 * don't allow
175 * 1) parallel transfers (multiple G->H at the same time)
176 * nor 2) mixed transfers (G->H + H->G at the same time).
177 */
[85744]178 m_pState = GuestDnDInst()->getState();
179 AssertPtrReturn(m_pState, E_POINTER);
[85739]180
[51476]181 /* Confirm a successful initialization when it's the case. */
182 autoInitSpan.setSucceeded();
183
[85743]184 return S_OK;
[51476]185}
186
187/**
188 * Uninitializes the instance.
189 * Called from FinalRelease().
190 */
191void GuestDnDSource::uninit(void)
192{
193 LogFlowThisFunc(("\n"));
194
195 /* Enclose the state transition Ready->InUninit->NotReady. */
196 AutoUninitSpan autoUninitSpan(this);
197 if (autoUninitSpan.uninitDone())
198 return;
199}
200
[51556]201// implementation of wrapped IDnDBase methods.
[51476]202/////////////////////////////////////////////////////////////////////////////
203
[55422]204HRESULT GuestDnDSource::isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported)
[51556]205{
206#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
207 ReturnComNotImplemented();
208#else /* VBOX_WITH_DRAG_AND_DROP */
209
[51620]210 AutoCaller autoCaller(this);
[98262]211 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
[51620]212
213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
214
[85559]215 *aSupported = GuestDnDBase::i_isFormatSupported(aFormat) ? TRUE : FALSE;
216
217 return S_OK;
[51556]218#endif /* VBOX_WITH_DRAG_AND_DROP */
219}
220
[57221]221HRESULT GuestDnDSource::getFormats(GuestDnDMIMEList &aFormats)
[51556]222{
223#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
224 ReturnComNotImplemented();
225#else /* VBOX_WITH_DRAG_AND_DROP */
226
[51620]227 AutoCaller autoCaller(this);
[98262]228 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
[51620]229
230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
231
[85558]232 aFormats = GuestDnDBase::i_getFormats();
233
234 return S_OK;
[51556]235#endif /* VBOX_WITH_DRAG_AND_DROP */
236}
237
[57221]238HRESULT GuestDnDSource::addFormats(const GuestDnDMIMEList &aFormats)
[51556]239{
240#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
241 ReturnComNotImplemented();
242#else /* VBOX_WITH_DRAG_AND_DROP */
243
[51620]244 AutoCaller autoCaller(this);
[98262]245 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
[51620]246
247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
248
[55422]249 return GuestDnDBase::i_addFormats(aFormats);
[51556]250#endif /* VBOX_WITH_DRAG_AND_DROP */
251}
252
[57221]253HRESULT GuestDnDSource::removeFormats(const GuestDnDMIMEList &aFormats)
[51556]254{
255#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
256 ReturnComNotImplemented();
257#else /* VBOX_WITH_DRAG_AND_DROP */
258
[51620]259 AutoCaller autoCaller(this);
[98262]260 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
[51620]261
262 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
263
[55422]264 return GuestDnDBase::i_removeFormats(aFormats);
[51556]265#endif /* VBOX_WITH_DRAG_AND_DROP */
266}
267
[55640]268// implementation of wrapped IDnDSource methods.
[51556]269/////////////////////////////////////////////////////////////////////////////
270
[57221]271HRESULT GuestDnDSource::dragIsPending(ULONG uScreenId, GuestDnDMIMEList &aFormats,
[55539]272 std::vector<DnDAction_T> &aAllowedActions, DnDAction_T *aDefaultAction)
[51476]273{
274#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
275 ReturnComNotImplemented();
276#else /* VBOX_WITH_DRAG_AND_DROP */
277
[57221]278 /* aDefaultAction is optional. */
279
[51476]280 AutoCaller autoCaller(this);
[98262]281 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
[51476]282
283 /* Default is ignoring the action. */
[59842]284 if (aDefaultAction)
285 *aDefaultAction = DnDAction_Ignore;
[51476]286
[97783]287 GuestDnDState *pState = GuestDnDInst()->getState();
[97784]288 AssertPtr(pState);
[51476]289
[97784]290 /* Check if any operation is active, and if so, bail out, returning an ignore action (see above). */
291 if (pState->get() != VBOXDNDSTATE_UNKNOWN)
292 return S_OK;
293
294 pState->set(VBOXDNDSTATE_QUERY_FORMATS);
295
[97783]296 HRESULT hrc = S_OK;
297
[55520]298 GuestDnDMsg Msg;
[85745]299 Msg.setType(HOST_DND_FN_GH_REQ_PENDING);
[85744]300 if (m_pState->m_uProtocolVersion >= 3)
[85554]301 Msg.appendUInt32(0); /** @todo ContextID not used yet. */
302 Msg.appendUInt32(uScreenId);
[51476]303
[97783]304 int vrc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
305 if (RT_SUCCESS(vrc))
[51476]306 {
[97783]307 int vrcGuest;
308 vrc = pState->waitForGuestResponseEx(100 /* Timeout in ms */, &vrcGuest);
309 if (RT_SUCCESS(vrc))
[51476]310 {
[97783]311 if (!isDnDIgnoreAction(pState->getActionDefault()))
[59842]312 {
[97783]313 /*
314 * In the GuestDnDSource case the source formats are from the guest,
315 * as GuestDnDSource acts as a target for the guest. The host always
316 * dictates what's supported and what's not, so filter out all formats
317 * which are not supported by the host.
318 */
[97822]319 GuestDnDMIMEList const &lstGuest = pState->formats();
320 GuestDnDMIMEList const lstFiltered = GuestDnD::toFilteredFormatList(m_lstFmtSupported, lstGuest);
[97783]321 if (lstFiltered.size())
322 {
323 LogRel2(("DnD: Host offered the following formats:\n"));
324 for (size_t i = 0; i < lstFiltered.size(); i++)
325 LogRel2(("DnD:\tFormat #%zu: %s\n", i, lstFiltered.at(i).c_str()));
[51476]326
[97783]327 aFormats = lstFiltered;
328 aAllowedActions = GuestDnD::toMainActions(pState->getActionsAllowed());
329 if (aDefaultAction)
330 *aDefaultAction = GuestDnD::toMainAction(pState->getActionDefault());
[51476]331
[97783]332 /* Apply the (filtered) formats list. */
333 m_lstFmtOffered = lstFiltered;
334 }
335 else
[97822]336 {
337 bool fSetError = true; /* Whether to set an error and reset or not. */
338
339 /*
340 * HACK ALERT: As we now expose an error (via i_setErrorAndReset(), see below) back to the API client, we
341 * have to add a kludge here. Older X11-based Guest Additions report "TARGETS, MULTIPLE" back
342 * to us, even if they don't offer any other *supported* formats of the host. This then in turn
343 * would lead to exposing an error, whereas we just should ignore those specific X11-based
344 * formats. For anything other we really want to be notified by setting an error though.
345 */
346 if ( lstGuest.size() == 2
347 && GuestDnD::isFormatInFormatList("TARGETS", lstGuest)
348 && GuestDnD::isFormatInFormatList("MULTIPLE", lstGuest))
349 {
350 fSetError = false;
351 }
352 /* HACK ALERT END */
353
354 if (fSetError)
355 hrc = i_setErrorAndReset(tr("Negotiation of formats between guest and host failed!\n\nHost offers: %s\n\nGuest offers: %s"),
356 GuestDnD::toFormatString(m_lstFmtSupported , ",").c_str(),
357 GuestDnD::toFormatString(pState->formats() , ",").c_str());
358 else /* Just silently reset. */
359 i_reset();
360 }
[51476]361 }
[97783]362 /* Note: Don't report an error here when the action is "ignore" -- that only means that the current window on the guest
363 simply doesn't support the format or drag and drop at all. */
[51476]364 }
[97783]365 else
366 hrc = i_setErrorAndReset(vrc == VERR_DND_GUEST_ERROR ? vrcGuest : vrc, tr("Requesting pending data from guest failed"));
[51476]367 }
[97783]368 else
[97835]369 {
370 switch (vrc)
371 {
372 case VERR_ACCESS_DENIED:
373 {
374 hrc = i_setErrorAndReset(tr("Dragging from guest to host not allowed -- make sure that the correct drag'n drop mode is set"));
375 break;
376 }
[51476]377
[97835]378 case VERR_NOT_SUPPORTED:
379 {
380 hrc = i_setErrorAndReset(tr("Dragging from guest to host not supported by guest -- make sure that the Guest Additions are properly installed and running"));
381 break;
382 }
383
384 default:
385 {
386 hrc = i_setErrorAndReset(vrc, tr("Sending drag pending event to guest failed"));
387 break;
388 }
389 }
390 }
391
[97784]392 pState->set(VBOXDNDSTATE_UNKNOWN);
393
[98273]394 LogFlowFunc(("hrc=%Rhrc\n", hrc));
[97783]395 return hrc;
[51476]396#endif /* VBOX_WITH_DRAG_AND_DROP */
397}
398
[55539]399HRESULT GuestDnDSource::drop(const com::Utf8Str &aFormat, DnDAction_T aAction, ComPtr<IProgress> &aProgress)
[51476]400{
401#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
402 ReturnComNotImplemented();
403#else /* VBOX_WITH_DRAG_AND_DROP */
404
[55549]405 AutoCaller autoCaller(this);
[98262]406 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
[55549]407
[59842]408 LogFunc(("aFormat=%s, aAction=%RU32\n", aFormat.c_str(), aAction));
409
[51476]410 /* Input validation. */
411 if (RT_UNLIKELY((aFormat.c_str()) == NULL || *(aFormat.c_str()) == '\0'))
412 return setError(E_INVALIDARG, tr("No drop format specified"));
413
[57221]414 /* Is the specified format in our list of (left over) offered formats? */
415 if (!GuestDnD::isFormatInFormatList(aFormat, m_lstFmtOffered))
416 return setError(E_INVALIDARG, tr("Specified format '%s' is not supported"), aFormat.c_str());
417
[85537]418 /* Check that the given action is supported by us. */
[74439]419 VBOXDNDACTION dndAction = GuestDnD::toHGCMAction(aAction);
420 if (isDnDIgnoreAction(dndAction)) /* If there is no usable action, ignore this request. */
[51476]421 return S_OK;
422
[58212]423 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
424
[85537]425 /* Check if this object still is in a pending state and bail out if so. */
426 if (m_fIsPending)
427 return setError(E_FAIL, tr("Current drop operation to host still in progress"));
428
429 /* Reset our internal state. */
430 i_reset();
431
[58212]432 /* At the moment we only support one transfer at a time. */
[85537]433 if (GuestDnDInst()->getSourceCount())
434 return setError(E_INVALIDARG, tr("Another drag and drop operation to the host already is in progress"));
[55520]435
[85537]436 /* Reset progress object. */
[85744]437 GuestDnDState *pState = GuestDnDInst()->getState();
438 AssertPtr(pState);
[98273]439 HRESULT hrc = pState->resetProgress(m_pGuest, tr("Dropping data to host"));
440 if (FAILED(hrc))
441 return hrc;
[51476]442
[85020]443 GuestDnDRecvDataTask *pTask = NULL;
[58519]444
[55539]445 try
446 {
[85402]447 mData.mRecvCtx.pSource = this;
[85744]448 mData.mRecvCtx.pState = pState;
[85537]449 mData.mRecvCtx.enmAction = dndAction;
[85402]450 mData.mRecvCtx.strFmtReq = aFormat;
451 mData.mRecvCtx.lstFmtOffered = m_lstFmtOffered;
[51476]452
[85537]453 LogRel2(("DnD: Requesting data from guest in format '%s'\n", aFormat.c_str()));
[57221]454
[85020]455 pTask = new GuestDnDRecvDataTask(this, &mData.mRecvCtx);
[58519]456 if (!pTask->isOk())
457 {
458 delete pTask;
[85537]459 LogRel2(("DnD: Receive data task failed to initialize\n"));
[98273]460 throw hrc = E_FAIL;
[58519]461 }
[55963]462
[85451]463 /* Drop write lock before creating thread. */
464 alock.release();
465
[59842]466 /* This function delete pTask in case of exceptions,
467 * so there is no need in the call of delete operator. */
[98273]468 hrc = pTask->createThreadWithType(RTTHREADTYPE_MAIN_WORKER);
[78745]469 pTask = NULL; /* Note: pTask is now owned by the worker thread. */
[58519]470 }
[63159]471 catch (std::bad_alloc &)
[58519]472 {
[98273]473 hrc = E_OUTOFMEMORY;
[58519]474 }
[63159]475 catch (...)
[58519]476 {
[63471]477 LogRel2(("DnD: Could not create thread for data receiving task\n"));
[98273]478 hrc = E_FAIL;
[58519]479 }
480
[98273]481 if (SUCCEEDED(hrc))
[58519]482 {
[85537]483 /* Register ourselves at the DnD manager. */
484 GuestDnDInst()->registerSource(this);
[85451]485
[98273]486 hrc = pState->queryProgressTo(aProgress.asOutParam());
487 ComAssertComRC(hrc);
[51476]488 }
[58519]489 else
[98273]490 hrc = i_setErrorAndReset(tr("Starting thread for GuestDnDSource failed (%Rhrc)"), hrc);
[51476]491
[98273]492 LogFlowFunc(("Returning hrc=%Rhrc\n", hrc));
493 return hrc;
[51476]494#endif /* VBOX_WITH_DRAG_AND_DROP */
495}
496
497HRESULT GuestDnDSource::receiveData(std::vector<BYTE> &aData)
498{
499#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
500 ReturnComNotImplemented();
501#else /* VBOX_WITH_DRAG_AND_DROP */
502
503 AutoCaller autoCaller(this);
[98262]504 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
[51476]505
[85537]506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
[55520]507
[85371]508 /* Don't allow receiving the actual data until our current transfer is complete. */
[85537]509 if (m_fIsPending)
510 return setError(E_FAIL, tr("Current drop operation to host still in progress"));
[58212]511
[98273]512 HRESULT hrc = S_OK;
[51476]513
[55520]514 try
[51476]515 {
[85371]516 GuestDnDRecvCtx *pCtx = &mData.mRecvCtx;
[85402]517 if (DnDMIMENeedsDropDir(pCtx->strFmtRecv.c_str(), pCtx->strFmtRecv.length()))
[51476]518 {
[85402]519 PDNDDROPPEDFILES pDF = &pCtx->Transfer.DroppedFiles;
[85371]520
521 const char *pcszDropDirAbs = DnDDroppedFilesGetDirAbs(pDF);
522 AssertPtr(pcszDropDirAbs);
523
[85537]524 LogRel2(("DnD: Using drop directory '%s', got %RU64 root entries\n",
525 pcszDropDirAbs, DnDTransferListGetRootCount(&pCtx->Transfer.List)));
[85371]526
[85537]527 /* We return the data as "text/uri-list" MIME data here. */
528 char *pszBuf = NULL;
529 size_t cbBuf = 0;
[98273]530 int vrc = DnDTransferListGetRootsEx(&pCtx->Transfer.List, DNDTRANSFERLISTFMT_URI,
531 pcszDropDirAbs, DND_PATH_SEPARATOR_STR, &pszBuf, &cbBuf);
532 if (RT_SUCCESS(vrc))
[85371]533 {
[85537]534 Assert(cbBuf);
535 AssertPtr(pszBuf);
536
[85371]537 aData.resize(cbBuf);
538 memcpy(&aData.front(), pszBuf, cbBuf);
539 RTStrFree(pszBuf);
540 }
541 else
[98273]542 LogRel(("DnD: Unable to build source root list, vrc=%Rrc\n", vrc));
[55520]543 }
[85537]544 else /* Raw data. */
[55520]545 {
[85371]546 if (pCtx->Meta.cbData)
[58329]547 {
548 /* Copy the data into a safe array of bytes. */
[85371]549 aData.resize(pCtx->Meta.cbData);
550 memcpy(&aData.front(), pCtx->Meta.pvData, pCtx->Meta.cbData);
[58329]551 }
552 else
553 aData.resize(0);
[51476]554 }
555 }
[55520]556 catch (std::bad_alloc &)
557 {
[98273]558 hrc = E_OUTOFMEMORY;
[55520]559 }
[51476]560
[98273]561 LogFlowFunc(("Returning hrc=%Rhrc\n", hrc));
562 return hrc;
[51476]563#endif /* VBOX_WITH_DRAG_AND_DROP */
564}
565
[55422]566// implementation of internal methods.
567/////////////////////////////////////////////////////////////////////////////
568
[85681]569/**
570 * Returns an error string from a guest DnD error.
571 *
572 * @returns Error string.
573 * @param guestRc Guest error to return error string for.
574 */
[55963]575/* static */
576Utf8Str GuestDnDSource::i_guestErrorToString(int guestRc)
577{
578 Utf8Str strError;
579
580 switch (guestRc)
581 {
582 case VERR_ACCESS_DENIED:
583 strError += Utf8StrFmt(tr("For one or more guest files or directories selected for transferring to the host your guest "
584 "user does not have the appropriate access rights for. Please make sure that all selected "
[56553]585 "elements can be accessed and that your guest user has the appropriate rights"));
[55963]586 break;
587
588 case VERR_NOT_FOUND:
589 /* Should not happen due to file locking on the guest, but anyway ... */
590 strError += Utf8StrFmt(tr("One or more guest files or directories selected for transferring to the host were not"
591 "found on the guest anymore. This can be the case if the guest files were moved and/or"
[56553]592 "altered while the drag and drop operation was in progress"));
[55963]593 break;
594
595 case VERR_SHARING_VIOLATION:
596 strError += Utf8StrFmt(tr("One or more guest files or directories selected for transferring to the host were locked. "
597 "Please make sure that all selected elements can be accessed and that your guest user has "
[56553]598 "the appropriate rights"));
[55963]599 break;
600
[56506]601 case VERR_TIMEOUT:
[56553]602 strError += Utf8StrFmt(tr("The guest was not able to retrieve the drag and drop data within time"));
[56506]603 break;
604
[55963]605 default:
606 strError += Utf8StrFmt(tr("Drag and drop error from guest (%Rrc)"), guestRc);
607 break;
608 }
609
610 return strError;
611}
612
[85681]613/**
614 * Returns an error string from a host DnD error.
615 *
616 * @returns Error string.
617 * @param hostRc Host error to return error string for.
618 */
[55963]619/* static */
620Utf8Str GuestDnDSource::i_hostErrorToString(int hostRc)
621{
622 Utf8Str strError;
623
624 switch (hostRc)
625 {
626 case VERR_ACCESS_DENIED:
627 strError += Utf8StrFmt(tr("For one or more host files or directories selected for transferring to the guest your host "
628 "user does not have the appropriate access rights for. Please make sure that all selected "
629 "elements can be accessed and that your host user has the appropriate rights."));
630 break;
631
[59851]632 case VERR_DISK_FULL:
633 strError += Utf8StrFmt(tr("Host disk ran out of space (disk is full)."));
634 break;
635
[55963]636 case VERR_NOT_FOUND:
637 /* Should not happen due to file locking on the host, but anyway ... */
638 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the host were not"
639 "found on the host anymore. This can be the case if the host files were moved and/or"
640 "altered while the drag and drop operation was in progress."));
641 break;
642
643 case VERR_SHARING_VIOLATION:
644 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the guest were locked. "
645 "Please make sure that all selected elements can be accessed and that your host user has "
646 "the appropriate rights."));
647 break;
648
649 default:
650 strError += Utf8StrFmt(tr("Drag and drop error from host (%Rrc)"), hostRc);
651 break;
652 }
653
654 return strError;
655}
656
[85681]657/**
658 * Resets all internal data and state.
659 */
[85537]660void GuestDnDSource::i_reset(void)
661{
[97788]662 LogRel2(("DnD: Source reset\n"));
[85537]663
664 mData.mRecvCtx.reset();
665
666 m_fIsPending = false;
667
668 /* Unregister ourselves from the DnD manager. */
669 GuestDnDInst()->unregisterSource(this);
670}
671
[55965]672#ifdef VBOX_WITH_DRAG_AND_DROP_GH
[85537]673
[85371]674/**
675 * Handles receiving a send data header from the guest.
676 *
677 * @returns VBox status code.
678 * @param pCtx Receive context to use.
679 * @param pDataHdr Pointer to send data header from the guest.
680 */
[85018]681int GuestDnDSource::i_onReceiveDataHdr(GuestDnDRecvCtx *pCtx, PVBOXDNDSNDDATAHDR pDataHdr)
[55422]682{
[85371]683 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
684 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
[58212]685
[85436]686 LogRel2(("DnD: Receiving %RU64 bytes total data (%RU32 bytes meta data, %RU64 objects) from guest ...\n",
[85371]687 pDataHdr->cbTotal, pDataHdr->cbMeta, pDataHdr->cObjects));
[58212]688
[85371]689 AssertReturn(pDataHdr->cbTotal >= pDataHdr->cbMeta, VERR_INVALID_PARAMETER);
[58212]690
[85537]691 pCtx->Meta.cbAnnounced = pDataHdr->cbMeta;
692 pCtx->cbExtra = pDataHdr->cbTotal - pDataHdr->cbMeta;
[85371]693
[85402]694 Assert(pCtx->Transfer.cObjToProcess == 0); /* Sanity. */
695 Assert(pCtx->Transfer.cObjProcessed == 0);
[85371]696
[85402]697 pCtx->Transfer.reset();
[85371]698
[85402]699 pCtx->Transfer.cObjToProcess = pDataHdr->cObjects;
[85371]700
[58212]701 /** @todo Handle compression type. */
702 /** @todo Handle checksum type. */
703
704 LogFlowFuncLeave();
705 return VINF_SUCCESS;
706}
707
[85371]708/**
[85681]709 * Main function for receiving data from the guest.
[85371]710 *
711 * @returns VBox status code.
712 * @param pCtx Receive context to use.
713 * @param pSndData Pointer to send data block from the guest.
714 */
[85018]715int GuestDnDSource::i_onReceiveData(GuestDnDRecvCtx *pCtx, PVBOXDNDSNDDATA pSndData)
[58212]716{
[55422]717 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
[58212]718 AssertPtrReturn(pSndData, VERR_INVALID_POINTER);
[55422]719
[98273]720 int vrc = VINF_SUCCESS;
[55422]721
722 try
723 {
[85402]724 GuestDnDTransferRecvData *pTransfer = &pCtx->Transfer;
[58212]725
[85402]726 size_t cbData;
727 void *pvData;
[85537]728 size_t cbTotalAnnounced;
729 size_t cbMetaAnnounced;
[58212]730
[85744]731 if (m_pState->m_uProtocolVersion < 3)
[55422]732 {
[58329]733 cbData = pSndData->u.v1.cbData;
734 pvData = pSndData->u.v1.pvData;
[58212]735
736 /* Sends the total data size to receive for every data chunk. */
[85537]737 cbTotalAnnounced = pSndData->u.v1.cbTotalSize;
[58212]738
739 /* Meta data size always is cbData, meaning there cannot be an
740 * extended data chunk transfer by sending further data. */
[85537]741 cbMetaAnnounced = cbData;
[58212]742 }
743 else
744 {
[58329]745 cbData = pSndData->u.v3.cbData;
746 pvData = pSndData->u.v3.pvData;
[58212]747
[85371]748 /* Note: Data sizes get initialized in i_onReceiveDataHdr().
749 * So just use the set values here. */
[85537]750 cbTotalAnnounced = pCtx->getTotalAnnounced();
751 cbMetaAnnounced = pCtx->Meta.cbAnnounced;
[58212]752 }
753
[85537]754 if (cbData > cbTotalAnnounced)
[58212]755 {
[85537]756 AssertMsgFailed(("Incoming data size invalid: cbData=%zu, cbTotal=%zu\n", cbData, cbTotalAnnounced));
[98273]757 vrc = VERR_INVALID_PARAMETER;
[55422]758 }
[85537]759 else if ( cbTotalAnnounced == 0
760 || cbTotalAnnounced < cbMetaAnnounced)
[55422]761 {
[85537]762 AssertMsgFailed(("cbTotal (%zu) is smaller than cbMeta (%zu)\n", cbTotalAnnounced, cbMetaAnnounced));
[98273]763 vrc = VERR_INVALID_PARAMETER;
[55422]764 }
765
[98273]766 if (RT_FAILURE(vrc))
767 return vrc;
[85371]768
[85436]769 AssertReturn(cbData <= mData.mcbBlockSize, VERR_BUFFER_OVERFLOW);
770
[85537]771 const size_t cbMetaRecv = pCtx->Meta.add(pvData, cbData);
772 AssertReturn(cbMetaRecv <= pCtx->Meta.cbData, VERR_BUFFER_OVERFLOW);
[85371]773
[85537]774 LogFlowThisFunc(("cbData=%zu, cbMetaRecv=%zu, cbMetaAnnounced=%zu, cbTotalAnnounced=%zu\n",
775 cbData, cbMetaRecv, cbMetaAnnounced, cbTotalAnnounced));
[85371]776
[85537]777 LogRel2(("DnD: %RU8%% of meta data complete (%zu/%zu bytes)\n",
778 (uint8_t)(cbMetaRecv * 100 / RT_MAX(cbMetaAnnounced, 1)), cbMetaRecv, cbMetaAnnounced));
779
[85371]780 /*
781 * (Meta) Data transfer complete?
782 */
[85537]783 if (cbMetaAnnounced == cbMetaRecv)
[55422]784 {
[85371]785 LogRel2(("DnD: Receiving meta data complete\n"));
[55422]786
[85402]787 if (DnDMIMENeedsDropDir(pCtx->strFmtRecv.c_str(), pCtx->strFmtRecv.length()))
[55422]788 {
[98273]789 vrc = DnDTransferListInitEx(&pTransfer->List,
790 DnDDroppedFilesGetDirAbs(&pTransfer->DroppedFiles), DNDTRANSFERLISTFMT_NATIVE);
791 if (RT_SUCCESS(vrc))
792 vrc = DnDTransferListAppendRootsFromBuffer(&pTransfer->List, DNDTRANSFERLISTFMT_URI,
793 (const char *)pCtx->Meta.pvData, pCtx->Meta.cbData,
794 DND_PATH_SEPARATOR_STR, DNDTRANSFERLIST_FLAGS_NONE);
[85371]795 /* Validation. */
[98273]796 if (RT_SUCCESS(vrc))
[55422]797 {
[85402]798 uint64_t cRoots = DnDTransferListGetRootCount(&pTransfer->List);
[85537]799
800 LogRel2(("DnD: Received %RU64 root entries from guest\n", cRoots));
801
[85371]802 if ( cRoots == 0
803 || cRoots > pTransfer->cObjToProcess)
[85537]804 {
805 LogRel(("DnD: Number of root entries invalid / mismatch: Got %RU64, expected %RU64\n",
806 cRoots, pTransfer->cObjToProcess));
[98273]807 vrc = VERR_INVALID_PARAMETER;
[85537]808 }
[85371]809 }
[55512]810
[98273]811 if (RT_SUCCESS(vrc))
[85371]812 {
813 /* Update our process with the data we already received. */
[98273]814 vrc = updateProgress(pCtx, pCtx->pState, cbMetaAnnounced);
815 AssertRC(vrc);
[55422]816 }
[85371]817
[98273]818 if (RT_FAILURE(vrc))
819 LogRel(("DnD: Error building root entry list, vrc=%Rrc\n", vrc));
[55422]820 }
[85371]821 else /* Raw data. */
822 {
[98273]823 vrc = updateProgress(pCtx, pCtx->pState, cbData);
824 AssertRC(vrc);
[85371]825 }
826
[98273]827 if (RT_FAILURE(vrc))
828 LogRel(("DnD: Error receiving meta data, vrc=%Rrc\n", vrc));
[55422]829 }
830 }
831 catch (std::bad_alloc &)
832 {
[98273]833 vrc = VERR_NO_MEMORY;
[55422]834 }
835
[98273]836 LogFlowFuncLeaveRC(vrc);
837 return vrc;
[55422]838}
839
[85681]840
[85018]841int GuestDnDSource::i_onReceiveDir(GuestDnDRecvCtx *pCtx, const char *pszPath, uint32_t cbPath, uint32_t fMode)
[55422]842{
843 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
844 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
845 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
846
[55524]847 LogFlowFunc(("pszPath=%s, cbPath=%RU32, fMode=0x%x\n", pszPath, cbPath, fMode));
[55422]848
[85402]849 const PDNDTRANSFEROBJECT pObj = &pCtx->Transfer.ObjCur;
850 const PDNDDROPPEDFILES pDF = &pCtx->Transfer.DroppedFiles;
[58212]851
[98273]852 int vrc = DnDTransferObjectInitEx(pObj, DNDTRANSFEROBJTYPE_DIRECTORY, DnDDroppedFilesGetDirAbs(pDF), pszPath);
853 if (RT_SUCCESS(vrc))
[58329]854 {
[85371]855 const char *pcszPathAbs = DnDTransferObjectGetSourcePath(pObj);
856 AssertPtr(pcszPathAbs);
[58212]857
[98273]858 vrc = RTDirCreateFullPath(pcszPathAbs, fMode);
859 if (RT_SUCCESS(vrc))
[58329]860 {
[85402]861 pCtx->Transfer.cObjProcessed++;
862 if (pCtx->Transfer.cObjProcessed <= pCtx->Transfer.cObjToProcess)
[98273]863 vrc = DnDDroppedFilesAddDir(pDF, pcszPathAbs);
[85371]864 else
[98273]865 vrc = VERR_TOO_MUCH_DATA;
[59851]866
[85371]867 DnDTransferObjectDestroy(pObj);
[59851]868
[98273]869 if (RT_FAILURE(vrc))
[85371]870 LogRel2(("DnD: Created guest directory '%s' on host\n", pcszPathAbs));
[58329]871 }
[58212]872 else
[98273]873 LogRel(("DnD: Error creating guest directory '%s' on host, vrc=%Rrc\n", pcszPathAbs, vrc));
[55422]874 }
875
[98273]876 if (RT_FAILURE(vrc))
877 LogRel(("DnD: Receiving guest directory '%s' failed with vrc=%Rrc\n", pszPath, vrc));
[85371]878
[98273]879 LogFlowFuncLeaveRC(vrc);
880 return vrc;
[55422]881}
882
[85371]883/**
884 * Receives a file header from the guest.
885 *
886 * @returns VBox status code.
887 * @param pCtx Receive context to use.
888 * @param pszPath File path of file to use.
889 * @param cbPath Size (in bytes, including terminator) of file path.
890 * @param cbSize File size (in bytes) to receive.
891 * @param fMode File mode to use.
892 * @param fFlags Additional receive flags; not used yet.
893 */
[85018]894int GuestDnDSource::i_onReceiveFileHdr(GuestDnDRecvCtx *pCtx, const char *pszPath, uint32_t cbPath,
[55422]895 uint64_t cbSize, uint32_t fMode, uint32_t fFlags)
896{
897 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
898 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
899 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
900 AssertReturn(fMode, VERR_INVALID_PARAMETER);
901 /* fFlags are optional. */
902
[85371]903 RT_NOREF(fFlags);
904
[55422]905 LogFlowFunc(("pszPath=%s, cbPath=%RU32, cbSize=%RU64, fMode=0x%x, fFlags=0x%x\n", pszPath, cbPath, cbSize, fMode, fFlags));
906
[85371]907 AssertMsgReturn(cbSize <= pCtx->cbExtra,
908 ("File size (%RU64) exceeds extra size to transfer (%RU64)\n", cbSize, pCtx->cbExtra), VERR_INVALID_PARAMETER);
909 AssertMsgReturn( pCtx->isComplete() == false
[85402]910 && pCtx->Transfer.cObjToProcess,
[85371]911 ("Data transfer already complete, bailing out\n"), VERR_INVALID_PARAMETER);
[58212]912
[98273]913 int vrc = VINF_SUCCESS;
[55422]914
915 do
916 {
[85402]917 const PDNDTRANSFEROBJECT pObj = &pCtx->Transfer.ObjCur;
[57500]918
[85371]919 if ( DnDTransferObjectIsOpen(pObj)
920 && !DnDTransferObjectIsComplete(pObj))
[55422]921 {
[85371]922 AssertMsgFailed(("Object '%s' not complete yet\n", DnDTransferObjectGetSourcePath(pObj)));
[98273]923 vrc = VERR_WRONG_ORDER;
[85371]924 break;
[55422]925 }
926
[85402]927 const PDNDDROPPEDFILES pDF = &pCtx->Transfer.DroppedFiles;
[58329]928
[98273]929 vrc = DnDTransferObjectInitEx(pObj, DNDTRANSFEROBJTYPE_FILE, DnDDroppedFilesGetDirAbs(pDF), pszPath);
930 AssertRCBreak(vrc);
[55422]931
[85371]932 const char *pcszSource = DnDTransferObjectGetSourcePath(pObj);
[103415]933 AssertPtrBreakStmt(pcszSource, vrc = VERR_INVALID_POINTER);
[55422]934
[85371]935 /** @todo Add sparse file support based on fFlags? (Use Open(..., fFlags | SPARSE). */
[98273]936 vrc = DnDTransferObjectOpen(pObj, RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE,
[85371]937 (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR, DNDTRANSFEROBJECT_FLAGS_NONE);
[98273]938 if (RT_FAILURE(vrc))
[85371]939 {
[98273]940 LogRel(("DnD: Error opening/creating guest file '%s' on host, vrc=%Rrc\n", pcszSource, vrc));
[85371]941 break;
[57500]942 }
943
[85371]944 /* Note: Protocol v1 does not send any file sizes, so always 0. */
[85744]945 if (m_pState->m_uProtocolVersion >= 2)
[98273]946 vrc = DnDTransferObjectSetSize(pObj, cbSize);
[55525]947
[85371]948 /** @todo Unescape path before printing. */
949 LogRel2(("DnD: Transferring guest file '%s' to host (%RU64 bytes, mode %#x)\n",
950 pcszSource, DnDTransferObjectGetSize(pObj), DnDTransferObjectGetMode(pObj)));
[55556]951
[85371]952 /** @todo Set progress object title to current file being transferred? */
[55556]953
[85371]954 if (DnDTransferObjectIsComplete(pObj)) /* 0-byte file? We're done already. */
955 {
956 LogRel2(("DnD: Transferring guest file '%s' (0 bytes) to host complete\n", pcszSource));
957
[85402]958 pCtx->Transfer.cObjProcessed++;
959 if (pCtx->Transfer.cObjProcessed <= pCtx->Transfer.cObjToProcess)
[78089]960 {
[85371]961 /* Add for having a proper rollback. */
[98273]962 vrc = DnDDroppedFilesAddFile(pDF, pcszSource);
[85371]963 }
964 else
[98273]965 vrc = VERR_TOO_MUCH_DATA;
[78089]966
[85371]967 DnDTransferObjectDestroy(pObj);
[55422]968 }
[57500]969
[55422]970 } while (0);
971
[98273]972 if (RT_FAILURE(vrc))
973 LogRel(("DnD: Error receiving guest file header, vrc=%Rrc\n", vrc));
[74714]974
[98273]975 LogFlowFuncLeaveRC(vrc);
976 return vrc;
[55422]977}
978
[85681]979/**
980 * Receives file data from the guest.
981 *
982 * @returns VBox status code.
983 * @param pCtx Receive context to use.
984 * @param pvData Pointer to file data received from the guest.
985 * @param pCtx Size (in bytes) of file data received from the guest.
986 */
[85018]987int GuestDnDSource::i_onReceiveFileData(GuestDnDRecvCtx *pCtx, const void *pvData, uint32_t cbData)
[55422]988{
989 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
990 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
991 AssertReturn(cbData, VERR_INVALID_PARAMETER);
992
[98273]993 int vrc = VINF_SUCCESS;
[55422]994
[58329]995 LogFlowFunc(("pvData=%p, cbData=%RU32, cbBlockSize=%RU32\n", pvData, cbData, mData.mcbBlockSize));
996
[58212]997 /*
998 * Sanity checking.
999 */
1000 if (cbData > mData.mcbBlockSize)
1001 return VERR_INVALID_PARAMETER;
1002
[55422]1003 do
1004 {
[85402]1005 const PDNDTRANSFEROBJECT pObj = &pCtx->Transfer.ObjCur;
[58212]1006
[85371]1007 const char *pcszSource = DnDTransferObjectGetSourcePath(pObj);
[103415]1008 AssertPtrBreakStmt(pcszSource, vrc = VERR_INVALID_POINTER);
[55422]1009
[85436]1010 AssertMsgReturn(DnDTransferObjectIsOpen(pObj),
1011 ("Object '%s' not open (anymore)\n", pcszSource), VERR_WRONG_ORDER);
1012 AssertMsgReturn(DnDTransferObjectIsComplete(pObj) == false,
1013 ("Object '%s' already marked as complete\n", pcszSource), VERR_WRONG_ORDER);
1014
[85371]1015 uint32_t cbWritten;
[98273]1016 vrc = DnDTransferObjectWrite(pObj, pvData, cbData, &cbWritten);
1017 if (RT_FAILURE(vrc))
1018 LogRel(("DnD: Error writing guest file data for '%s', vrc=%Rrc\n", pcszSource, vrc));
[85371]1019
1020 Assert(cbWritten <= cbData);
1021 if (cbWritten < cbData)
[55422]1022 {
[85371]1023 LogRel(("DnD: Only written %RU32 of %RU32 bytes of guest file '%s' -- disk full?\n",
1024 cbWritten, cbData, pcszSource));
[98273]1025 vrc = VERR_IO_GEN_FAILURE; /** @todo Find a better vrc. */
[55422]1026 break;
1027 }
1028
[98273]1029 vrc = updateProgress(pCtx, pCtx->pState, cbWritten);
1030 AssertRCBreak(vrc);
[85371]1031
1032 if (DnDTransferObjectIsComplete(pObj))
[55422]1033 {
[85371]1034 LogRel2(("DnD: Transferring guest file '%s' to host complete\n", pcszSource));
[55512]1035
[85402]1036 pCtx->Transfer.cObjProcessed++;
1037 if (pCtx->Transfer.cObjProcessed > pCtx->Transfer.cObjToProcess)
[98273]1038 vrc = VERR_TOO_MUCH_DATA;
[55422]1039
[85371]1040 DnDTransferObjectDestroy(pObj);
[55422]1041 }
1042
1043 } while (0);
1044
[98273]1045 if (RT_FAILURE(vrc))
1046 LogRel(("DnD: Error receiving guest file data, vrc=%Rrc\n", vrc));
[74714]1047
[98273]1048 LogFlowFuncLeaveRC(vrc);
1049 return vrc;
[55422]1050}
1051#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1052
[63183]1053/**
[85681]1054 * Main function to receive DnD data from the guest.
1055 *
1056 * @returns VBox status code.
1057 * @param pCtx Receive context to use.
1058 * @param msTimeout Timeout (in ms) to wait for receiving data.
[63183]1059 */
[85018]1060int GuestDnDSource::i_receiveData(GuestDnDRecvCtx *pCtx, RTMSINTERVAL msTimeout)
[55422]1061{
1062 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1063
[85537]1064 /* Sanity. */
1065 AssertMsgReturn(pCtx->enmAction,
1066 ("Action to perform is none when it shouldn't\n"), VERR_INVALID_PARAMETER);
1067 AssertMsgReturn(pCtx->strFmtReq.isNotEmpty(),
1068 ("Requested format from host is empty when it shouldn't\n"), VERR_INVALID_PARAMETER);
[55422]1069
[55539]1070 /*
[57221]1071 * Do we need to receive a different format than initially requested?
1072 *
[59842]1073 * For example, receiving a file link as "text/plain" requires still to receive
[57221]1074 * the file from the guest as "text/uri-list" first, then pointing to
1075 * the file path on the host in the "text/plain" data returned.
1076 */
1077
[59842]1078 bool fFoundFormat = true; /* Whether we've found a common format between host + guest. */
1079
[85402]1080 LogFlowFunc(("strFmtReq=%s, strFmtRecv=%s, enmAction=0x%x\n",
1081 pCtx->strFmtReq.c_str(), pCtx->strFmtRecv.c_str(), pCtx->enmAction));
[59842]1082
1083 /* Plain text wanted? */
[85402]1084 if ( pCtx->strFmtReq.equalsIgnoreCase("text/plain")
1085 || pCtx->strFmtReq.equalsIgnoreCase("text/plain;charset=utf-8"))
[57221]1086 {
1087 /* Did the guest offer a file? Receive a file instead. */
[85402]1088 if (GuestDnD::isFormatInFormatList("text/uri-list", pCtx->lstFmtOffered))
1089 pCtx->strFmtRecv = "text/uri-list";
[59842]1090 /* Guest only offers (plain) text. */
1091 else
[85402]1092 pCtx->strFmtRecv = "text/plain;charset=utf-8";
[57221]1093
1094 /** @todo Add more conversions here. */
1095 }
[59842]1096 /* File(s) wanted? */
[85402]1097 else if (pCtx->strFmtReq.equalsIgnoreCase("text/uri-list"))
[59842]1098 {
1099 /* Does the guest support sending files? */
[85402]1100 if (GuestDnD::isFormatInFormatList("text/uri-list", pCtx->lstFmtOffered))
1101 pCtx->strFmtRecv = "text/uri-list";
[59842]1102 else /* Bail out. */
1103 fFoundFormat = false;
1104 }
[57221]1105
[98273]1106 int vrc = VINF_SUCCESS;
[85537]1107
[59842]1108 if (fFoundFormat)
1109 {
[85402]1110 if (!pCtx->strFmtRecv.equals(pCtx->strFmtReq))
[78084]1111 LogRel2(("DnD: Requested data in format '%s', receiving in intermediate format '%s' now\n",
[85402]1112 pCtx->strFmtReq.c_str(), pCtx->strFmtRecv.c_str()));
[57221]1113
[59842]1114 /*
1115 * Call the appropriate receive handler based on the data format to handle.
1116 */
[85402]1117 bool fURIData = DnDMIMENeedsDropDir(pCtx->strFmtRecv.c_str(), pCtx->strFmtRecv.length());
[59842]1118 if (fURIData)
[98273]1119 vrc = i_receiveTransferData(pCtx, msTimeout);
[59842]1120 else
[98273]1121 vrc = i_receiveRawData(pCtx, msTimeout);
[55539]1122 }
[59842]1123 else /* Just inform the user (if verbose release logging is enabled). */
[55539]1124 {
[85402]1125 LogRel(("DnD: The guest does not support format '%s':\n", pCtx->strFmtReq.c_str()));
[78084]1126 LogRel(("DnD: Guest offered the following formats:\n"));
[85402]1127 for (size_t i = 0; i < pCtx->lstFmtOffered.size(); i++)
1128 LogRel(("DnD:\tFormat #%zu: %s\n", i, pCtx->lstFmtOffered.at(i).c_str()));
[85537]1129
[98273]1130 vrc = VERR_NOT_SUPPORTED;
[55539]1131 }
[55422]1132
[98273]1133 if (RT_FAILURE(vrc))
[85537]1134 {
[98273]1135 LogRel(("DnD: Receiving data from guest failed with %Rrc\n", vrc));
[55422]1136
[85564]1137 /* Let the guest side know first. */
1138 sendCancel();
1139
[85537]1140 /* Reset state. */
1141 i_reset();
1142 }
1143
[98273]1144 LogFlowFuncLeaveRC(vrc);
1145 return vrc;
[55422]1146}
1147
[85681]1148/**
1149 * Receives raw (meta) data from the guest.
1150 *
1151 * @returns VBox status code.
1152 * @param pCtx Receive context to use.
1153 * @param msTimeout Timeout (in ms) to wait for receiving data.
1154 */
[85018]1155int GuestDnDSource::i_receiveRawData(GuestDnDRecvCtx *pCtx, RTMSINTERVAL msTimeout)
[55422]1156{
1157 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1158
[98273]1159 int vrc;
[55422]1160
[59842]1161 LogFlowFuncEnter();
1162
[85744]1163 GuestDnDState *pState = pCtx->pState;
1164 AssertPtr(pCtx->pState);
[55422]1165
[85537]1166 GuestDnD *pInst = GuestDnDInst();
[55422]1167 if (!pInst)
1168 return VERR_INVALID_POINTER;
1169
[98273]1170#define REGISTER_CALLBACK(x) do { \
1171 vrc = pState->setCallback(x, i_receiveRawDataCallback, pCtx); \
1172 if (RT_FAILURE(vrc)) \
1173 return vrc; \
[60050]1174 } while (0)
[55422]1175
[98273]1176#define UNREGISTER_CALLBACK(x) do { \
1177 int vrc2 = pState->setCallback(x, NULL); \
1178 AssertRC(vrc2); \
[60050]1179 } while (0)
[55422]1180
1181 /*
1182 * Register callbacks.
1183 */
[85745]1184 REGISTER_CALLBACK(GUEST_DND_FN_CONNECT);
1185 REGISTER_CALLBACK(GUEST_DND_FN_DISCONNECT);
[97749]1186 REGISTER_CALLBACK(GUEST_DND_FN_EVT_ERROR);
[85744]1187 if (m_pState->m_uProtocolVersion >= 3)
[85745]1188 REGISTER_CALLBACK(GUEST_DND_FN_GH_SND_DATA_HDR);
1189 REGISTER_CALLBACK(GUEST_DND_FN_GH_SND_DATA);
[55422]1190
1191 do
1192 {
1193 /*
1194 * Receive the raw data.
1195 */
1196 GuestDnDMsg Msg;
[85745]1197 Msg.setType(HOST_DND_FN_GH_EVT_DROPPED);
[85744]1198 if (m_pState->m_uProtocolVersion >= 3)
[85554]1199 Msg.appendUInt32(0); /** @todo ContextID not used yet. */
1200 Msg.appendPointer((void*)pCtx->strFmtRecv.c_str(), (uint32_t)pCtx->strFmtRecv.length() + 1);
1201 Msg.appendUInt32((uint32_t)pCtx->strFmtRecv.length() + 1);
1202 Msg.appendUInt32(pCtx->enmAction);
[55422]1203
1204 /* Make the initial call to the guest by telling that we initiated the "dropped" event on
1205 * the host and therefore now waiting for the actual raw data. */
[98273]1206 vrc = pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
1207 if (RT_SUCCESS(vrc))
[55963]1208 {
[98273]1209 vrc = waitForEvent(&pCtx->EventCallback, pCtx->pState, msTimeout);
1210 if (RT_SUCCESS(vrc))
1211 vrc = pCtx->pState->setProgress(100, DND_PROGRESS_COMPLETE, VINF_SUCCESS);
[55963]1212 }
[55422]1213
1214 } while (0);
1215
1216 /*
1217 * Unregister callbacks.
1218 */
[85745]1219 UNREGISTER_CALLBACK(GUEST_DND_FN_CONNECT);
1220 UNREGISTER_CALLBACK(GUEST_DND_FN_DISCONNECT);
[97749]1221 UNREGISTER_CALLBACK(GUEST_DND_FN_EVT_ERROR);
[85744]1222 if (m_pState->m_uProtocolVersion >= 3)
[85745]1223 UNREGISTER_CALLBACK(GUEST_DND_FN_GH_SND_DATA_HDR);
1224 UNREGISTER_CALLBACK(GUEST_DND_FN_GH_SND_DATA);
[55422]1225
1226#undef REGISTER_CALLBACK
1227#undef UNREGISTER_CALLBACK
1228
[98273]1229 if (RT_FAILURE(vrc))
[55571]1230 {
[98273]1231 if (vrc == VERR_CANCELLED) /* Transfer was cancelled by the host. */
[56506]1232 {
[74495]1233 /*
1234 * Now that we've cleaned up tell the guest side to cancel.
1235 * This does not imply we're waiting for the guest to react, as the
1236 * host side never must depend on anything from the guest.
1237 */
[98273]1238 int vrc2 = sendCancel();
1239 AssertRC(vrc2);
[56506]1240
[98273]1241 vrc2 = pCtx->pState->setProgress(100, DND_PROGRESS_CANCELLED);
1242 AssertRC(vrc2);
[56506]1243 }
[98273]1244 else if (vrc != VERR_DND_GUEST_ERROR) /* Guest-side error are already handled in the callback. */
[56506]1245 {
[98273]1246 int vrc2 = pCtx->pState->setProgress(100, DND_PROGRESS_ERROR,
1247 vrc, GuestDnDSource::i_hostErrorToString(vrc));
1248 AssertRC(vrc2);
[56506]1249 }
[74495]1250
[98273]1251 vrc = VINF_SUCCESS; /* The error was handled by the setProgress() calls above. */
[55571]1252 }
1253
[98273]1254 LogFlowFuncLeaveRC(vrc);
1255 return vrc;
[55422]1256}
1257
[85681]1258/**
1259 * Receives transfer data (files / directories / ...) from the guest.
1260 *
1261 * @returns VBox status code.
1262 * @param pCtx Receive context to use.
1263 * @param msTimeout Timeout (in ms) to wait for receiving data.
1264 */
[85371]1265int GuestDnDSource::i_receiveTransferData(GuestDnDRecvCtx *pCtx, RTMSINTERVAL msTimeout)
[55422]1266{
1267 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1268
[98273]1269 int vrc;
[55422]1270
[59842]1271 LogFlowFuncEnter();
1272
[85744]1273 GuestDnDState *pState = pCtx->pState;
1274 AssertPtr(pCtx->pState);
[55422]1275
[85537]1276 GuestDnD *pInst = GuestDnDInst();
[55422]1277 if (!pInst)
1278 return VERR_INVALID_POINTER;
1279
[98273]1280#define REGISTER_CALLBACK(x) do { \
1281 vrc = pState->setCallback(x, i_receiveTransferDataCallback, pCtx); \
1282 if (RT_FAILURE(vrc)) \
1283 return vrc; \
[60050]1284 } while (0)
[55422]1285
[98273]1286#define UNREGISTER_CALLBACK(x) do { \
1287 int vrc2 = pState->setCallback(x, NULL); \
1288 AssertRC(vrc2); \
[60050]1289 } while (0)
[55422]1290
1291 /*
1292 * Register callbacks.
1293 */
1294 /* Guest callbacks. */
[85745]1295 REGISTER_CALLBACK(GUEST_DND_FN_CONNECT);
1296 REGISTER_CALLBACK(GUEST_DND_FN_DISCONNECT);
[97749]1297 REGISTER_CALLBACK(GUEST_DND_FN_EVT_ERROR);
[85744]1298 if (m_pState->m_uProtocolVersion >= 3)
[85745]1299 REGISTER_CALLBACK(GUEST_DND_FN_GH_SND_DATA_HDR);
1300 REGISTER_CALLBACK(GUEST_DND_FN_GH_SND_DATA);
1301 REGISTER_CALLBACK(GUEST_DND_FN_GH_SND_DIR);
[85744]1302 if (m_pState->m_uProtocolVersion >= 2)
[85745]1303 REGISTER_CALLBACK(GUEST_DND_FN_GH_SND_FILE_HDR);
1304 REGISTER_CALLBACK(GUEST_DND_FN_GH_SND_FILE_DATA);
[55422]1305
[85402]1306 const PDNDDROPPEDFILES pDF = &pCtx->Transfer.DroppedFiles;
[57776]1307
[55422]1308 do
1309 {
[98273]1310 vrc = DnDDroppedFilesOpenTemp(pDF, 0 /* fFlags */);
1311 if (RT_FAILURE(vrc))
[78085]1312 {
[98273]1313 LogRel(("DnD: Opening dropped files directory '%s' on the host failed with vrc=%Rrc\n",
1314 DnDDroppedFilesGetDirAbs(pDF), vrc));
[55422]1315 break;
[78085]1316 }
[55422]1317
1318 /*
[85371]1319 * Receive the transfer list.
[55422]1320 */
1321 GuestDnDMsg Msg;
[85745]1322 Msg.setType(HOST_DND_FN_GH_EVT_DROPPED);
[85744]1323 if (m_pState->m_uProtocolVersion >= 3)
[85554]1324 Msg.appendUInt32(0); /** @todo ContextID not used yet. */
1325 Msg.appendPointer((void*)pCtx->strFmtRecv.c_str(), (uint32_t)pCtx->strFmtRecv.length() + 1);
1326 Msg.appendUInt32((uint32_t)pCtx->strFmtRecv.length() + 1);
1327 Msg.appendUInt32(pCtx->enmAction);
[55422]1328
1329 /* Make the initial call to the guest by telling that we initiated the "dropped" event on
[55571]1330 * the host and therefore now waiting for the actual URI data. */
[98273]1331 vrc = pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
1332 if (RT_SUCCESS(vrc))
[55963]1333 {
[56506]1334 LogFlowFunc(("Waiting ...\n"));
1335
[98273]1336 vrc = waitForEvent(&pCtx->EventCallback, pCtx->pState, msTimeout);
1337 if (RT_SUCCESS(vrc))
1338 vrc = pCtx->pState->setProgress(100, DND_PROGRESS_COMPLETE, VINF_SUCCESS);
[56506]1339
[98273]1340 LogFlowFunc(("Waiting ended with vrc=%Rrc\n", vrc));
[55963]1341 }
[55422]1342
1343 } while (0);
1344
1345 /*
1346 * Unregister callbacks.
1347 */
[85745]1348 UNREGISTER_CALLBACK(GUEST_DND_FN_CONNECT);
1349 UNREGISTER_CALLBACK(GUEST_DND_FN_DISCONNECT);
[97749]1350 UNREGISTER_CALLBACK(GUEST_DND_FN_EVT_ERROR);
[85745]1351 UNREGISTER_CALLBACK(GUEST_DND_FN_GH_SND_DATA_HDR);
1352 UNREGISTER_CALLBACK(GUEST_DND_FN_GH_SND_DATA);
1353 UNREGISTER_CALLBACK(GUEST_DND_FN_GH_SND_DIR);
1354 UNREGISTER_CALLBACK(GUEST_DND_FN_GH_SND_FILE_HDR);
1355 UNREGISTER_CALLBACK(GUEST_DND_FN_GH_SND_FILE_DATA);
[55422]1356
1357#undef REGISTER_CALLBACK
1358#undef UNREGISTER_CALLBACK
1359
[98273]1360 if (RT_FAILURE(vrc))
[55571]1361 {
[98273]1362 int vrc2 = DnDDroppedFilesRollback(pDF);
1363 if (RT_FAILURE(vrc2))
[74495]1364 LogRel(("DnD: Deleting left over temporary files failed (%Rrc), please remove directory '%s' manually\n",
[98273]1365 vrc2, DnDDroppedFilesGetDirAbs(pDF)));
[74495]1366
[98273]1367 if (vrc == VERR_CANCELLED)
[56506]1368 {
[74495]1369 /*
1370 * Now that we've cleaned up tell the guest side to cancel.
1371 * This does not imply we're waiting for the guest to react, as the
1372 * host side never must depend on anything from the guest.
1373 */
[98273]1374 vrc2 = sendCancel();
1375 AssertRC(vrc2);
[56506]1376
[98273]1377 vrc2 = pCtx->pState->setProgress(100, DND_PROGRESS_CANCELLED);
1378 AssertRC(vrc2);
[85564]1379
1380 /* Cancelling is not an error, just set success here. */
[98273]1381 vrc = VINF_SUCCESS;
[56506]1382 }
[98273]1383 else if (vrc != VERR_DND_GUEST_ERROR) /* Guest-side error are already handled in the callback. */
[56506]1384 {
[98273]1385 vrc2 = pCtx->pState->setProgress(100, DND_PROGRESS_ERROR, vrc, GuestDnDSource::i_hostErrorToString(vrc2));
1386 AssertRC(vrc2);
[56506]1387 }
[57776]1388 }
[57654]1389
[85371]1390 DnDDroppedFilesClose(pDF);
[57654]1391
[98273]1392 LogFlowFuncLeaveRC(vrc);
1393 return vrc;
[55422]1394}
1395
[85681]1396/**
1397 * Static HGCM service callback which handles receiving raw data.
1398 *
1399 * @returns VBox status code. Will get sent back to the host service.
1400 * @param uMsg HGCM message ID (function number).
1401 * @param pvParms Pointer to additional message data. Optional and can be NULL.
1402 * @param cbParms Size (in bytes) additional message data. Optional and can be 0.
1403 * @param pvUser User-supplied pointer on callback registration.
1404 */
[55422]1405/* static */
1406DECLCALLBACK(int) GuestDnDSource::i_receiveRawDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
1407{
[85018]1408 GuestDnDRecvCtx *pCtx = (GuestDnDRecvCtx *)pvUser;
[55422]1409 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1410
[85402]1411 GuestDnDSource *pThis = pCtx->pSource;
[55422]1412 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1413
1414 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
1415
[98273]1416 int vrc = VINF_SUCCESS;
[55422]1417
[98273]1418 int vrcCallback = VINF_SUCCESS; /* vrc for the callback. */
[59842]1419 bool fNotify = false;
[55963]1420
[55422]1421 switch (uMsg)
1422 {
[85745]1423 case GUEST_DND_FN_CONNECT:
[58329]1424 /* Nothing to do here (yet). */
1425 break;
1426
[85745]1427 case GUEST_DND_FN_DISCONNECT:
[98273]1428 vrc = VERR_CANCELLED;
[58329]1429 break;
1430
[55428]1431#ifdef VBOX_WITH_DRAG_AND_DROP_GH
[85745]1432 case GUEST_DND_FN_GH_SND_DATA_HDR:
[55422]1433 {
[58212]1434 PVBOXDNDCBSNDDATAHDRDATA pCBData = reinterpret_cast<PVBOXDNDCBSNDDATAHDRDATA>(pvParms);
[55422]1435 AssertPtr(pCBData);
[58212]1436 AssertReturn(sizeof(VBOXDNDCBSNDDATAHDRDATA) == cbParms, VERR_INVALID_PARAMETER);
[58257]1437 AssertReturn(CB_MAGIC_DND_GH_SND_DATA_HDR == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
[55422]1438
[98273]1439 vrc = pThis->i_onReceiveDataHdr(pCtx, &pCBData->data);
[55422]1440 break;
1441 }
[85745]1442 case GUEST_DND_FN_GH_SND_DATA:
[55422]1443 {
[58212]1444 PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<PVBOXDNDCBSNDDATADATA>(pvParms);
[55422]1445 AssertPtr(pCBData);
[58212]1446 AssertReturn(sizeof(VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
[58257]1447 AssertReturn(CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
[55422]1448
[98273]1449 vrc = pThis->i_onReceiveData(pCtx, &pCBData->data);
[58212]1450 break;
1451 }
[97749]1452 case GUEST_DND_FN_EVT_ERROR:
[58212]1453 {
1454 PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<PVBOXDNDCBEVTERRORDATA>(pvParms);
1455 AssertPtr(pCBData);
1456 AssertReturn(sizeof(VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
[97750]1457 AssertReturn(CB_MAGIC_DND_EVT_ERROR == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
[58212]1458
[85744]1459 pCtx->pState->reset();
[55963]1460
1461 if (RT_SUCCESS(pCBData->rc))
[59842]1462 {
1463 AssertMsgFailed(("Received guest error with no error code set\n"));
[55963]1464 pCBData->rc = VERR_GENERAL_FAILURE; /* Make sure some error is set. */
[59842]1465 }
1466 else if (pCBData->rc == VERR_WRONG_ORDER)
[98273]1467 vrc = pCtx->pState->setProgress(100, DND_PROGRESS_CANCELLED);
[59842]1468 else
[98273]1469 vrc = pCtx->pState->setProgress(100, DND_PROGRESS_ERROR, pCBData->rc,
1470 GuestDnDSource::i_guestErrorToString(pCBData->rc));
[55963]1471
[59842]1472 LogRel3(("DnD: Guest reported data transfer error: %Rrc\n", pCBData->rc));
1473
[98273]1474 if (RT_SUCCESS(vrc))
1475 vrcCallback = VERR_DND_GUEST_ERROR;
[55422]1476 break;
1477 }
[55428]1478#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
[55422]1479 default:
[98273]1480 vrc = VERR_NOT_SUPPORTED;
[55422]1481 break;
1482 }
1483
[98273]1484 if ( RT_FAILURE(vrc)
1485 || RT_FAILURE(vrcCallback))
[59842]1486 {
1487 fNotify = true;
[98273]1488 if (RT_SUCCESS(vrcCallback))
1489 vrcCallback = vrc;
[59842]1490 }
1491
[98273]1492 if (RT_FAILURE(vrc))
[55422]1493 {
[98273]1494 switch (vrc)
[59842]1495 {
1496 case VERR_NO_DATA:
1497 LogRel2(("DnD: Data transfer to host complete\n"));
1498 break;
1499
1500 case VERR_CANCELLED:
1501 LogRel2(("DnD: Data transfer to host canceled\n"));
1502 break;
1503
1504 default:
[98273]1505 LogRel(("DnD: Error %Rrc occurred, aborting data transfer to host\n", vrc));
[59842]1506 break;
1507 }
1508
1509 /* Unregister this callback. */
[85744]1510 AssertPtr(pCtx->pState);
[98273]1511 int vrc2 = pCtx->pState->setCallback(uMsg, NULL /* PFNGUESTDNDCALLBACK */);
1512 AssertRC(vrc2);
[55422]1513 }
1514
[59842]1515 /* All data processed? */
[85371]1516 if (pCtx->isComplete())
[59842]1517 fNotify = true;
1518
[98273]1519 LogFlowFunc(("cbProcessed=%RU64, cbExtra=%RU64, fNotify=%RTbool, vrcCallback=%Rrc, vrc=%Rrc\n",
1520 pCtx->cbProcessed, pCtx->cbExtra, fNotify, vrcCallback, vrc));
[59842]1521
1522 if (fNotify)
1523 {
[98273]1524 int vrc2 = pCtx->EventCallback.Notify(vrcCallback);
1525 AssertRC(vrc2);
[59842]1526 }
1527
[98273]1528 LogFlowFuncLeaveRC(vrc);
1529 return vrc; /* Tell the guest. */
[55422]1530}
1531
[85681]1532/**
1533 * Static HGCM service callback which handles receiving transfer data from the guest.
1534 *
1535 * @returns VBox status code. Will get sent back to the host service.
1536 * @param uMsg HGCM message ID (function number).
1537 * @param pvParms Pointer to additional message data. Optional and can be NULL.
1538 * @param cbParms Size (in bytes) additional message data. Optional and can be 0.
1539 * @param pvUser User-supplied pointer on callback registration.
1540 */
[55422]1541/* static */
[85402]1542DECLCALLBACK(int) GuestDnDSource::i_receiveTransferDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
[55422]1543{
[85018]1544 GuestDnDRecvCtx *pCtx = (GuestDnDRecvCtx *)pvUser;
[55422]1545 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1546
[85402]1547 GuestDnDSource *pThis = pCtx->pSource;
[55422]1548 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1549
1550 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
1551
[98273]1552 int vrc = VINF_SUCCESS;
[55823]1553
[98273]1554 int vrcCallback = VINF_SUCCESS; /* vrc for the callback. */
[55512]1555 bool fNotify = false;
[55422]1556
1557 switch (uMsg)
1558 {
[85745]1559 case GUEST_DND_FN_CONNECT:
[58329]1560 /* Nothing to do here (yet). */
1561 break;
1562
[85745]1563 case GUEST_DND_FN_DISCONNECT:
[98273]1564 vrc = VERR_CANCELLED;
[58329]1565 break;
1566
[55428]1567#ifdef VBOX_WITH_DRAG_AND_DROP_GH
[85745]1568 case GUEST_DND_FN_GH_SND_DATA_HDR:
[55422]1569 {
[58212]1570 PVBOXDNDCBSNDDATAHDRDATA pCBData = reinterpret_cast<PVBOXDNDCBSNDDATAHDRDATA>(pvParms);
[55422]1571 AssertPtr(pCBData);
[58212]1572 AssertReturn(sizeof(VBOXDNDCBSNDDATAHDRDATA) == cbParms, VERR_INVALID_PARAMETER);
[58257]1573 AssertReturn(CB_MAGIC_DND_GH_SND_DATA_HDR == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
[55422]1574
[98273]1575 vrc = pThis->i_onReceiveDataHdr(pCtx, &pCBData->data);
[55422]1576 break;
1577 }
[85745]1578 case GUEST_DND_FN_GH_SND_DATA:
[55422]1579 {
[58212]1580 PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<PVBOXDNDCBSNDDATADATA>(pvParms);
[55422]1581 AssertPtr(pCBData);
[58212]1582 AssertReturn(sizeof(VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
[58257]1583 AssertReturn(CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
[55422]1584
[98273]1585 vrc = pThis->i_onReceiveData(pCtx, &pCBData->data);
[58212]1586 break;
1587 }
[85745]1588 case GUEST_DND_FN_GH_SND_DIR:
[58212]1589 {
1590 PVBOXDNDCBSNDDIRDATA pCBData = reinterpret_cast<PVBOXDNDCBSNDDIRDATA>(pvParms);
1591 AssertPtr(pCBData);
1592 AssertReturn(sizeof(VBOXDNDCBSNDDIRDATA) == cbParms, VERR_INVALID_PARAMETER);
[58257]1593 AssertReturn(CB_MAGIC_DND_GH_SND_DIR == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
[58212]1594
[98273]1595 vrc = pThis->i_onReceiveDir(pCtx, pCBData->pszPath, pCBData->cbPath, pCBData->fMode);
[55422]1596 break;
1597 }
[85745]1598 case GUEST_DND_FN_GH_SND_FILE_HDR:
[55422]1599 {
[58212]1600 PVBOXDNDCBSNDFILEHDRDATA pCBData = reinterpret_cast<PVBOXDNDCBSNDFILEHDRDATA>(pvParms);
[55422]1601 AssertPtr(pCBData);
[58212]1602 AssertReturn(sizeof(VBOXDNDCBSNDFILEHDRDATA) == cbParms, VERR_INVALID_PARAMETER);
[58257]1603 AssertReturn(CB_MAGIC_DND_GH_SND_FILE_HDR == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
[55422]1604
[98273]1605 vrc = pThis->i_onReceiveFileHdr(pCtx, pCBData->pszFilePath, pCBData->cbFilePath,
1606 pCBData->cbSize, pCBData->fMode, pCBData->fFlags);
[55422]1607 break;
1608 }
[85745]1609 case GUEST_DND_FN_GH_SND_FILE_DATA:
[55422]1610 {
[58212]1611 PVBOXDNDCBSNDFILEDATADATA pCBData = reinterpret_cast<PVBOXDNDCBSNDFILEDATADATA>(pvParms);
[55422]1612 AssertPtr(pCBData);
[58212]1613 AssertReturn(sizeof(VBOXDNDCBSNDFILEDATADATA) == cbParms, VERR_INVALID_PARAMETER);
[58257]1614 AssertReturn(CB_MAGIC_DND_GH_SND_FILE_DATA == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
[55422]1615
[85744]1616 if (pThis->m_pState->m_uProtocolVersion <= 1)
[55422]1617 {
[98273]1618 /*
[55422]1619 * Notes for protocol v1 (< VBox 5.0):
1620 * - Every time this command is being sent it includes the file header,
1621 * so just process both calls here.
1622 * - There was no information whatsoever about the total file size; the old code only
1623 * appended data to the desired file. So just pass 0 as cbSize.
1624 */
[98273]1625 vrc = pThis->i_onReceiveFileHdr(pCtx, pCBData->u.v1.pszFilePath, pCBData->u.v1.cbFilePath,
1626 0 /* cbSize */, pCBData->u.v1.fMode, 0 /* fFlags */);
1627 if (RT_SUCCESS(vrc))
1628 vrc = pThis->i_onReceiveFileData(pCtx, pCBData->pvData, pCBData->cbData);
[55422]1629 }
1630 else /* Protocol v2 and up. */
[98273]1631 vrc = pThis->i_onReceiveFileData(pCtx, pCBData->pvData, pCBData->cbData);
[55422]1632 break;
1633 }
[97749]1634 case GUEST_DND_FN_EVT_ERROR:
[55422]1635 {
[58212]1636 PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<PVBOXDNDCBEVTERRORDATA>(pvParms);
[55422]1637 AssertPtr(pCBData);
[58212]1638 AssertReturn(sizeof(VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
[97750]1639 AssertReturn(CB_MAGIC_DND_EVT_ERROR == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
[55422]1640
[85744]1641 pCtx->pState->reset();
[56506]1642
[55823]1643 if (RT_SUCCESS(pCBData->rc))
[59842]1644 {
1645 AssertMsgFailed(("Received guest error with no error code set\n"));
[55823]1646 pCBData->rc = VERR_GENERAL_FAILURE; /* Make sure some error is set. */
[59842]1647 }
1648 else if (pCBData->rc == VERR_WRONG_ORDER)
[98273]1649 vrc = pCtx->pState->setProgress(100, DND_PROGRESS_CANCELLED);
[59842]1650 else
[98273]1651 vrc = pCtx->pState->setProgress(100, DND_PROGRESS_ERROR, pCBData->rc,
1652 GuestDnDSource::i_guestErrorToString(pCBData->rc));
[56506]1653
[59842]1654 LogRel3(("DnD: Guest reported file transfer error: %Rrc\n", pCBData->rc));
1655
[98273]1656 if (RT_SUCCESS(vrc))
1657 vrcCallback = VERR_DND_GUEST_ERROR;
[55422]1658 break;
1659 }
[55428]1660#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
[55422]1661 default:
[98273]1662 vrc = VERR_NOT_SUPPORTED;
[55422]1663 break;
1664 }
1665
[98273]1666 if ( RT_FAILURE(vrc)
1667 || RT_FAILURE(vrcCallback))
[55823]1668 {
[55512]1669 fNotify = true;
[98273]1670 if (RT_SUCCESS(vrcCallback))
1671 vrcCallback = vrc;
[55823]1672 }
[55512]1673
[98273]1674 if (RT_FAILURE(vrc))
[55963]1675 {
[98273]1676 switch (vrc)
[55963]1677 {
1678 case VERR_NO_DATA:
[59842]1679 LogRel2(("DnD: File transfer to host complete\n"));
[55963]1680 break;
1681
1682 case VERR_CANCELLED:
[59842]1683 LogRel2(("DnD: File transfer to host canceled\n"));
[55963]1684 break;
1685
1686 default:
[98273]1687 LogRel(("DnD: Error %Rrc occurred, aborting file transfer to host\n", vrc));
[55963]1688 break;
1689 }
1690
1691 /* Unregister this callback. */
[85744]1692 AssertPtr(pCtx->pState);
[98273]1693 int vrc2 = pCtx->pState->setCallback(uMsg, NULL /* PFNGUESTDNDCALLBACK */);
1694 AssertRC(vrc2);
[55963]1695 }
1696
[58212]1697 /* All data processed? */
[85402]1698 if ( pCtx->Transfer.isComplete()
[85371]1699 && pCtx->isComplete())
[55512]1700 fNotify = true;
[55422]1701
[98273]1702 LogFlowFunc(("cbProcessed=%RU64, cbExtra=%RU64, fNotify=%RTbool, vrcCallback=%Rrc, vrc=%Rrc\n",
1703 pCtx->cbProcessed, pCtx->cbExtra, fNotify, vrcCallback, vrc));
[55512]1704
1705 if (fNotify)
1706 {
[98273]1707 int vrc2 = pCtx->EventCallback.Notify(
1708 vrcCallback);
1709 AssertRC(vrc2);
[55512]1710 }
1711
[98273]1712 LogFlowFuncLeaveRC(vrc);
1713 return vrc; /* Tell the guest. */
[55422]1714}
1715
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use