VirtualBox

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

Last change on this file since 58959 was 58519, checked in by vboxsync, 9 years ago

pr7179. DnD part has been changed.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 48.2 KB
Line 
1/* $Id: GuestDnDSourceImpl.cpp 58519 2015-10-30 08:17:19Z vboxsync $ */
2/** @file
3 * VBox Console COM Class implementation - Guest drag and drop source.
4 */
5
6/*
7 * Copyright (C) 2014-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include "GuestImpl.h"
23#include "GuestDnDSourceImpl.h"
24#include "GuestDnDPrivate.h"
25#include "ConsoleImpl.h"
26
27#include "Global.h"
28#include "AutoCaller.h"
29#include "ThreadTask.h"
30
31#include <iprt/asm.h>
32#include <iprt/dir.h>
33#include <iprt/file.h>
34#include <iprt/path.h>
35#include <iprt/uri.h>
36
37#include <iprt/cpp/utils.h> /* For unconst(). */
38
39#include <VBox/com/array.h>
40
41#ifdef LOG_GROUP
42 #undef LOG_GROUP
43#endif
44#define LOG_GROUP LOG_GROUP_GUEST_DND
45#include <VBox/log.h>
46
47/**
48 * Base class for a source task.
49 */
50class GuestDnDSourceTask : public ThreadTask
51{
52public:
53
54 GuestDnDSourceTask(GuestDnDSource *pSource)
55 : ThreadTask("GenericGuestDnDSourceTask")
56 , mSource(pSource)
57 , mRC(VINF_SUCCESS) { }
58
59 virtual ~GuestDnDSourceTask(void) { }
60
61 int getRC(void) const { return mRC; }
62 bool isOk(void) const { return RT_SUCCESS(mRC); }
63 const ComObjPtr<GuestDnDSource> &getSource(void) const { return mSource; }
64
65protected:
66
67 const ComObjPtr<GuestDnDSource> mSource;
68 int mRC;
69};
70
71/**
72 * Task structure for receiving data from a source using
73 * a worker thread.
74 */
75class RecvDataTask : public GuestDnDSourceTask
76{
77public:
78
79 RecvDataTask(GuestDnDSource *pSource, PRECVDATACTX pCtx)
80 : GuestDnDSourceTask(pSource)
81 , mpCtx(pCtx)
82 {
83 m_strTaskName = "dndSrcRcvData";
84 }
85
86 void handler()
87 {
88 int vrc = GuestDnDSource::i_receiveDataThread(*m_pThread, this);
89 }
90
91 virtual ~RecvDataTask(void) { }
92
93 PRECVDATACTX getCtx(void) { return mpCtx; }
94
95protected:
96
97 /** Pointer to receive data context. */
98 PRECVDATACTX mpCtx;
99};
100
101// constructor / destructor
102/////////////////////////////////////////////////////////////////////////////
103
104DEFINE_EMPTY_CTOR_DTOR(GuestDnDSource)
105
106HRESULT GuestDnDSource::FinalConstruct(void)
107{
108 /*
109 * Set the maximum block size this source can handle to 64K. This always has
110 * been hardcoded until now.
111 *
112 * Note: Never ever rely on information from the guest; the host dictates what and
113 * how to do something, so try to negogiate a sensible value here later.
114 */
115 mData.mcbBlockSize = _64K; /** @todo Make this configurable. */
116
117 LogFlowThisFunc(("\n"));
118 return BaseFinalConstruct();
119}
120
121void GuestDnDSource::FinalRelease(void)
122{
123 LogFlowThisFuncEnter();
124 uninit();
125 BaseFinalRelease();
126 LogFlowThisFuncLeave();
127}
128
129// public initializer/uninitializer for internal purposes only
130/////////////////////////////////////////////////////////////////////////////
131
132int GuestDnDSource::init(const ComObjPtr<Guest>& pGuest)
133{
134 LogFlowThisFuncEnter();
135
136 /* Enclose the state transition NotReady->InInit->Ready. */
137 AutoInitSpan autoInitSpan(this);
138 AssertReturn(autoInitSpan.isOk(), E_FAIL);
139
140 unconst(m_pGuest) = pGuest;
141
142 /* Confirm a successful initialization when it's the case. */
143 autoInitSpan.setSucceeded();
144
145 return VINF_SUCCESS;
146}
147
148/**
149 * Uninitializes the instance.
150 * Called from FinalRelease().
151 */
152void GuestDnDSource::uninit(void)
153{
154 LogFlowThisFunc(("\n"));
155
156 /* Enclose the state transition Ready->InUninit->NotReady. */
157 AutoUninitSpan autoUninitSpan(this);
158 if (autoUninitSpan.uninitDone())
159 return;
160}
161
162// implementation of wrapped IDnDBase methods.
163/////////////////////////////////////////////////////////////////////////////
164
165HRESULT GuestDnDSource::isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported)
166{
167#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
168 ReturnComNotImplemented();
169#else /* VBOX_WITH_DRAG_AND_DROP */
170
171 AutoCaller autoCaller(this);
172 if (FAILED(autoCaller.rc())) return autoCaller.rc();
173
174 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
175
176 return GuestDnDBase::i_isFormatSupported(aFormat, aSupported);
177#endif /* VBOX_WITH_DRAG_AND_DROP */
178}
179
180HRESULT GuestDnDSource::getFormats(GuestDnDMIMEList &aFormats)
181{
182#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
183 ReturnComNotImplemented();
184#else /* VBOX_WITH_DRAG_AND_DROP */
185
186 AutoCaller autoCaller(this);
187 if (FAILED(autoCaller.rc())) return autoCaller.rc();
188
189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
190
191 return GuestDnDBase::i_getFormats(aFormats);
192#endif /* VBOX_WITH_DRAG_AND_DROP */
193}
194
195HRESULT GuestDnDSource::addFormats(const GuestDnDMIMEList &aFormats)
196{
197#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
198 ReturnComNotImplemented();
199#else /* VBOX_WITH_DRAG_AND_DROP */
200
201 AutoCaller autoCaller(this);
202 if (FAILED(autoCaller.rc())) return autoCaller.rc();
203
204 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
205
206 return GuestDnDBase::i_addFormats(aFormats);
207#endif /* VBOX_WITH_DRAG_AND_DROP */
208}
209
210HRESULT GuestDnDSource::removeFormats(const GuestDnDMIMEList &aFormats)
211{
212#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
213 ReturnComNotImplemented();
214#else /* VBOX_WITH_DRAG_AND_DROP */
215
216 AutoCaller autoCaller(this);
217 if (FAILED(autoCaller.rc())) return autoCaller.rc();
218
219 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
220
221 return GuestDnDBase::i_removeFormats(aFormats);
222#endif /* VBOX_WITH_DRAG_AND_DROP */
223}
224
225HRESULT GuestDnDSource::getProtocolVersion(ULONG *aProtocolVersion)
226{
227#if !defined(VBOX_WITH_DRAG_AND_DROP)
228 ReturnComNotImplemented();
229#else /* VBOX_WITH_DRAG_AND_DROP */
230
231 AutoCaller autoCaller(this);
232 if (FAILED(autoCaller.rc())) return autoCaller.rc();
233
234 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
235
236 return GuestDnDBase::i_getProtocolVersion(aProtocolVersion);
237#endif /* VBOX_WITH_DRAG_AND_DROP */
238}
239
240// implementation of wrapped IDnDSource methods.
241/////////////////////////////////////////////////////////////////////////////
242
243HRESULT GuestDnDSource::dragIsPending(ULONG uScreenId, GuestDnDMIMEList &aFormats,
244 std::vector<DnDAction_T> &aAllowedActions, DnDAction_T *aDefaultAction)
245{
246#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
247 ReturnComNotImplemented();
248#else /* VBOX_WITH_DRAG_AND_DROP */
249
250 /* aDefaultAction is optional. */
251
252 AutoCaller autoCaller(this);
253 if (FAILED(autoCaller.rc())) return autoCaller.rc();
254
255 /* Determine guest DnD protocol to use. */
256 GuestDnDBase::getProtocolVersion(&mDataBase.m_uProtocolVersion);
257
258 /* Default is ignoring the action. */
259 DnDAction_T defaultAction = DnDAction_Ignore;
260
261 HRESULT hr = S_OK;
262
263 GuestDnDMsg Msg;
264 Msg.setType(HOST_DND_GH_REQ_PENDING);
265 if (mDataBase.m_uProtocolVersion >= 3)
266 Msg.setNextUInt32(0); /** @todo ContextID not used yet. */
267 Msg.setNextUInt32(uScreenId);
268
269 int rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
270 if (RT_SUCCESS(rc))
271 {
272 GuestDnDResponse *pResp = GuestDnDInst()->response();
273 AssertPtr(pResp);
274
275 bool fFetchResult = true;
276
277 rc = pResp->waitForGuestResponse(5000 /* Timeout in ms */);
278 if (RT_FAILURE(rc))
279 fFetchResult = false;
280
281 if ( fFetchResult
282 && isDnDIgnoreAction(pResp->defAction()))
283 fFetchResult = false;
284
285 /* Fetch the default action to use. */
286 if (fFetchResult)
287 {
288 defaultAction = GuestDnD::toMainAction(pResp->defAction());
289 aAllowedActions = GuestDnD::toMainActions(pResp->allActions());
290
291 /*
292 * In the GuestDnDSource case the source formats are from the guest,
293 * as GuestDnDSource acts as a target for the guest. The host always
294 * dictates what's supported and what's not, so filter out all formats
295 * which are not supported by the host.
296 */
297 aFormats = GuestDnD::toFilteredFormatList(m_lstFmtSupported, pResp->formats());
298
299 /* Save the (filtered) formats. */
300 m_lstFmtOffered = aFormats;
301
302 if (m_lstFmtOffered.size())
303 {
304 LogRelMax(3, ("DnD: Offered formats:\n"));
305 for (size_t i = 0; i < m_lstFmtOffered.size(); i++)
306 LogRelMax(3, ("DnD:\tFormat #%zu: %s\n", i, m_lstFmtOffered.at(i).c_str()));
307 }
308 else
309 LogRelMax(3, ("DnD: No compatible format between guest and host found, drag and drop to host not possible\n"));
310 }
311
312 LogFlowFunc(("fFetchResult=%RTbool, defaultAction=0x%x, allActions=0x%x\n",
313 fFetchResult, defaultAction, pResp->allActions()));
314
315 if (aDefaultAction)
316 *aDefaultAction = defaultAction;
317 }
318
319 LogFlowFunc(("hr=%Rhrc\n", hr));
320 return hr;
321#endif /* VBOX_WITH_DRAG_AND_DROP */
322}
323
324HRESULT GuestDnDSource::drop(const com::Utf8Str &aFormat, DnDAction_T aAction, ComPtr<IProgress> &aProgress)
325{
326#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
327 ReturnComNotImplemented();
328#else /* VBOX_WITH_DRAG_AND_DROP */
329
330 AutoCaller autoCaller(this);
331 if (FAILED(autoCaller.rc())) return autoCaller.rc();
332
333 /* Input validation. */
334 if (RT_UNLIKELY((aFormat.c_str()) == NULL || *(aFormat.c_str()) == '\0'))
335 return setError(E_INVALIDARG, tr("No drop format specified"));
336
337 /* Is the specified format in our list of (left over) offered formats? */
338 if (!GuestDnD::isFormatInFormatList(aFormat, m_lstFmtOffered))
339 return setError(E_INVALIDARG, tr("Specified format '%s' is not supported"), aFormat.c_str());
340
341 uint32_t uAction = GuestDnD::toHGCMAction(aAction);
342 if (isDnDIgnoreAction(uAction)) /* If there is no usable action, ignore this request. */
343 return S_OK;
344
345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
346
347 /* At the moment we only support one transfer at a time. */
348 if (mDataBase.m_cTransfersPending)
349 return setError(E_INVALIDARG, tr("Another drop operation already is in progress"));
350
351 /* Dito. */
352 GuestDnDResponse *pResp = GuestDnDInst()->response();
353 AssertPtr(pResp);
354
355 HRESULT hr = pResp->resetProgress(m_pGuest);
356 if (FAILED(hr))
357 return hr;
358
359 RecvDataTask *pTask = NULL;
360 RTTHREAD threadRcv;
361 int rc = S_OK;
362
363 try
364 {
365 mData.mRecvCtx.mIsActive = false;
366 mData.mRecvCtx.mpSource = this;
367 mData.mRecvCtx.mpResp = pResp;
368 mData.mRecvCtx.mFmtReq = aFormat;
369 mData.mRecvCtx.mFmtOffered = m_lstFmtOffered;
370
371 LogRel2(("DnD: Requesting data from guest in format: %s\n", aFormat.c_str()));
372
373 pTask = new RecvDataTask(this, &mData.mRecvCtx);
374 if (!pTask->isOk())
375 {
376 delete pTask;
377 LogRel2(("DnD: Could not create RecvDataTask object \n"));
378 throw hr = E_FAIL;
379 }
380
381 //this function delete pTask in case of exceptions, so there is no need in the call of delete operator
382 hr = pTask->createThread(&threadRcv);
383
384 }
385 catch(std::bad_alloc &)
386 {
387 hr = setError(E_OUTOFMEMORY);
388 }
389 catch(...)
390 {
391 LogRel2(("DnD: Could not create thread for RecvDataTask \n"));
392 hr = E_FAIL;
393 }
394
395 if (SUCCEEDED(hr))
396 {
397 rc = RTThreadUserWait(threadRcv, 30 * 1000 /* 30s timeout */);
398 if (RT_SUCCESS(rc))
399 {
400 mDataBase.m_cTransfersPending++;
401
402 hr = pResp->queryProgressTo(aProgress.asOutParam());
403 ComAssertComRC(hr);
404
405 /* Note: pTask is now owned by the worker thread. */
406 }
407 else
408 hr = setError(VBOX_E_IPRT_ERROR, tr("Waiting for receiving thread failed (%Rrc)"), rc);
409 }
410 else
411 hr = setError(VBOX_E_IPRT_ERROR, tr("Starting thread for GuestDnDSource::i_receiveDataThread failed (%Rrc)"), rc);
412 /* Note: mDataBase.mfTransferIsPending will be set to false again by i_receiveDataThread. */
413
414 LogFlowFunc(("Returning hr=%Rhrc\n", hr));
415 return hr;
416#endif /* VBOX_WITH_DRAG_AND_DROP */
417}
418
419HRESULT GuestDnDSource::receiveData(std::vector<BYTE> &aData)
420{
421#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
422 ReturnComNotImplemented();
423#else /* VBOX_WITH_DRAG_AND_DROP */
424
425 AutoCaller autoCaller(this);
426 if (FAILED(autoCaller.rc())) return autoCaller.rc();
427
428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
429
430 LogFlowThisFunc(("cTransfersPending=%RU32\n", mDataBase.m_cTransfersPending));
431
432 /* Don't allow receiving the actual data until our transfer actually is complete. */
433 if (mDataBase.m_cTransfersPending)
434 return setError(E_FAIL, tr("Current drop operation still in progress"));
435
436 PRECVDATACTX pCtx = &mData.mRecvCtx;
437 HRESULT hr = S_OK;
438
439 try
440 {
441 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFmtRecv.c_str(), pCtx->mFmtRecv.length());
442 if (fHasURIList)
443 {
444 LogRel2(("DnD: Drop directory is: %s\n", pCtx->mURI.getDroppedFiles().GetDirAbs()));
445 int rc2 = pCtx->mURI.toMetaData(aData);
446 if (RT_FAILURE(rc2))
447 hr = E_OUTOFMEMORY;
448 }
449 else
450 {
451 const size_t cbData = pCtx->mData.getMeta().getSize();
452 LogFlowFunc(("cbData=%zu\n", cbData));
453 if (cbData)
454 {
455 /* Copy the data into a safe array of bytes. */
456 aData.resize(cbData);
457 memcpy(&aData.front(), pCtx->mData.getMeta().getData(), cbData);
458 }
459 else
460 aData.resize(0);
461 }
462 }
463 catch (std::bad_alloc &)
464 {
465 hr = E_OUTOFMEMORY;
466 }
467
468 LogFlowFunc(("Returning hr=%Rhrc\n", hr));
469 return hr;
470#endif /* VBOX_WITH_DRAG_AND_DROP */
471}
472
473// implementation of internal methods.
474/////////////////////////////////////////////////////////////////////////////
475
476/* static */
477Utf8Str GuestDnDSource::i_guestErrorToString(int guestRc)
478{
479 Utf8Str strError;
480
481 switch (guestRc)
482 {
483 case VERR_ACCESS_DENIED:
484 strError += Utf8StrFmt(tr("For one or more guest files or directories selected for transferring to the host your guest "
485 "user does not have the appropriate access rights for. Please make sure that all selected "
486 "elements can be accessed and that your guest user has the appropriate rights"));
487 break;
488
489 case VERR_NOT_FOUND:
490 /* Should not happen due to file locking on the guest, but anyway ... */
491 strError += Utf8StrFmt(tr("One or more guest files or directories selected for transferring to the host were not"
492 "found on the guest anymore. This can be the case if the guest files were moved and/or"
493 "altered while the drag and drop operation was in progress"));
494 break;
495
496 case VERR_SHARING_VIOLATION:
497 strError += Utf8StrFmt(tr("One or more guest files or directories selected for transferring to the host were locked. "
498 "Please make sure that all selected elements can be accessed and that your guest user has "
499 "the appropriate rights"));
500 break;
501
502 case VERR_TIMEOUT:
503 strError += Utf8StrFmt(tr("The guest was not able to retrieve the drag and drop data within time"));
504 break;
505
506 default:
507 strError += Utf8StrFmt(tr("Drag and drop error from guest (%Rrc)"), guestRc);
508 break;
509 }
510
511 return strError;
512}
513
514/* static */
515Utf8Str GuestDnDSource::i_hostErrorToString(int hostRc)
516{
517 Utf8Str strError;
518
519 switch (hostRc)
520 {
521 case VERR_ACCESS_DENIED:
522 strError += Utf8StrFmt(tr("For one or more host files or directories selected for transferring to the guest your host "
523 "user does not have the appropriate access rights for. Please make sure that all selected "
524 "elements can be accessed and that your host user has the appropriate rights."));
525 break;
526
527 case VERR_NOT_FOUND:
528 /* Should not happen due to file locking on the host, but anyway ... */
529 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the host were not"
530 "found on the host anymore. This can be the case if the host files were moved and/or"
531 "altered while the drag and drop operation was in progress."));
532 break;
533
534 case VERR_SHARING_VIOLATION:
535 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the guest were locked. "
536 "Please make sure that all selected elements can be accessed and that your host user has "
537 "the appropriate rights."));
538 break;
539
540 default:
541 strError += Utf8StrFmt(tr("Drag and drop error from host (%Rrc)"), hostRc);
542 break;
543 }
544
545 return strError;
546}
547
548#ifdef VBOX_WITH_DRAG_AND_DROP_GH
549int GuestDnDSource::i_onReceiveDataHdr(PRECVDATACTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr)
550{
551 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
552 AssertReturn(pDataHdr, VERR_INVALID_POINTER);
553
554 pCtx->mData.setEstimatedSize(pDataHdr->cbTotal, pDataHdr->cbMeta);
555
556 Assert(pCtx->mURI.getObjToProcess() == 0);
557 pCtx->mURI.reset();
558 pCtx->mURI.setEstimatedObjects(pDataHdr->cObjects);
559
560 /** @todo Handle compression type. */
561 /** @todo Handle checksum type. */
562
563 LogFlowFuncLeave();
564 return VINF_SUCCESS;
565}
566
567int GuestDnDSource::i_onReceiveData(PRECVDATACTX pCtx, PVBOXDNDSNDDATA pSndData)
568{
569 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
570 AssertPtrReturn(pSndData, VERR_INVALID_POINTER);
571
572 int rc = VINF_SUCCESS;
573
574 try
575 {
576 GuestDnDData *pData = &pCtx->mData;
577 GuestDnDURIData *pURI = &pCtx->mURI;
578
579 uint32_t cbData;
580 void *pvData;
581 uint64_t cbTotal;
582 uint32_t cbMeta;
583
584 if (mDataBase.m_uProtocolVersion < 3)
585 {
586 cbData = pSndData->u.v1.cbData;
587 pvData = pSndData->u.v1.pvData;
588
589 /* Sends the total data size to receive for every data chunk. */
590 cbTotal = pSndData->u.v1.cbTotalSize;
591
592 /* Meta data size always is cbData, meaning there cannot be an
593 * extended data chunk transfer by sending further data. */
594 cbMeta = cbData;
595 }
596 else
597 {
598 cbData = pSndData->u.v3.cbData;
599 pvData = pSndData->u.v3.pvData;
600
601 /* Note: Data sizes get updated in i_onReceiveDataHdr(). */
602 cbTotal = pData->getTotal();
603 cbMeta = pData->getMeta().getSize();
604 }
605 Assert(cbTotal);
606
607 if ( cbData == 0
608 || cbData > cbTotal /* Paranoia */)
609 {
610 LogFlowFunc(("Incoming data size invalid: cbData=%RU32, cbToProcess=%RU64\n", cbData, pData->getTotal()));
611 rc = VERR_INVALID_PARAMETER;
612 }
613 else if (cbTotal < cbMeta)
614 {
615 AssertMsgFailed(("cbTotal (%RU64) is smaller than cbMeta (%RU32)\n", cbTotal, cbMeta));
616 rc = VERR_INVALID_PARAMETER;
617 }
618
619 if (RT_SUCCESS(rc))
620 {
621 cbMeta = pData->getMeta().add(pvData, cbData);
622 LogFlowThisFunc(("cbMetaSize=%zu, cbData=%RU32, cbMeta=%RU32, cbTotal=%RU64\n",
623 pData->getMeta().getSize(), cbData, cbMeta, cbTotal));
624 }
625
626 if (RT_SUCCESS(rc))
627 {
628 /*
629 * (Meta) Data transfer complete?
630 */
631 Assert(cbMeta <= pData->getMeta().getSize());
632 if (cbMeta == pData->getMeta().getSize())
633 {
634 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFmtRecv.c_str(), pCtx->mFmtRecv.length());
635 LogFlowThisFunc(("fHasURIList=%RTbool\n", fHasURIList));
636 if (fHasURIList)
637 {
638 /* Try parsing the data as URI list. */
639 rc = pURI->fromRemoteMetaData(pData->getMeta());
640 if (RT_SUCCESS(rc))
641 {
642 if (mDataBase.m_uProtocolVersion < 3)
643 pData->setEstimatedSize(cbTotal, cbMeta);
644
645 /*
646 * Update our process with the data we already received.
647 * Note: The total size will consist of the meta data (in pVecData) and
648 * the actual accumulated file/directory data from the guest.
649 */
650 rc = updateProgress(pData, pCtx->mpResp, (uint32_t)pData->getMeta().getSize());
651 }
652 }
653 }
654 }
655 }
656 catch (std::bad_alloc &)
657 {
658 rc = VERR_NO_MEMORY;
659 }
660
661 LogFlowFuncLeaveRC(rc);
662 return rc;
663}
664
665int GuestDnDSource::i_onReceiveDir(PRECVDATACTX pCtx, const char *pszPath, uint32_t cbPath, uint32_t fMode)
666{
667 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
668 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
669 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
670
671 LogFlowFunc(("pszPath=%s, cbPath=%RU32, fMode=0x%x\n", pszPath, cbPath, fMode));
672
673 /*
674 * Sanity checking.
675 */
676 if ( !cbPath
677 || cbPath > RTPATH_MAX)
678 {
679 LogFlowFunc(("Path length invalid, bailing out\n"));
680 return VERR_INVALID_PARAMETER;
681 }
682
683 int rc = RTStrValidateEncodingEx(pszPath, RTSTR_MAX, 0);
684 if (RT_FAILURE(rc))
685 {
686 LogFlowFunc(("Path validation failed with %Rrc, bailing out\n", rc));
687 return VERR_INVALID_PARAMETER;
688 }
689
690 if (pCtx->mURI.isComplete())
691 {
692 LogFlowFunc(("Data transfer already complete, bailing out\n"));
693 return VERR_INVALID_PARAMETER;
694 }
695
696 GuestDnDURIObjCtx &objCtx = pCtx->mURI.getObj(0); /** @todo Fill in context ID. */
697
698 rc = objCtx.createIntermediate(DnDURIObject::Directory);
699 if (RT_FAILURE(rc))
700 return rc;
701
702 DnDURIObject *pObj = objCtx.getObj();
703 AssertPtr(pObj);
704
705 const char *pszDroppedFilesDir = pCtx->mURI.getDroppedFiles().GetDirAbs();
706 char *pszDir = RTPathJoinA(pszDroppedFilesDir, pszPath);
707 if (pszDir)
708 {
709#ifdef RT_OS_WINDOWS
710 RTPathChangeToDosSlashes(pszDir, true /* fForce */);
711#else
712 RTPathChangeToDosSlashes(pszDir, true /* fForce */);
713#endif
714 rc = RTDirCreateFullPath(pszDir, fMode);
715 if (RT_SUCCESS(rc))
716 {
717 pCtx->mURI.processObject(*pObj);
718 objCtx.reset();
719 LogRel2(("DnD: Created guest directory on host: %s\n", pszDir));
720 }
721 else
722 LogRel(("DnD: Error creating guest directory '%s' on host, rc=%Rrc\n", pszDir, rc));
723
724 RTStrFree(pszDir);
725 }
726 else
727 rc = VERR_NO_MEMORY;
728
729 LogFlowFuncLeaveRC(rc);
730 return rc;
731}
732
733int GuestDnDSource::i_onReceiveFileHdr(PRECVDATACTX pCtx, const char *pszPath, uint32_t cbPath,
734 uint64_t cbSize, uint32_t fMode, uint32_t fFlags)
735{
736 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
737 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
738 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
739 AssertReturn(fMode, VERR_INVALID_PARAMETER);
740 /* fFlags are optional. */
741
742 LogFlowFunc(("pszPath=%s, cbPath=%RU32, cbSize=%RU64, fMode=0x%x, fFlags=0x%x\n", pszPath, cbPath, cbSize, fMode, fFlags));
743
744 /*
745 * Sanity checking.
746 */
747 if ( !cbPath
748 || cbPath > RTPATH_MAX)
749 {
750 return VERR_INVALID_PARAMETER;
751 }
752
753 if (!RTStrIsValidEncoding(pszPath))
754 return VERR_INVALID_PARAMETER;
755
756 if (cbSize > pCtx->mData.getTotal())
757 {
758 AssertMsgFailed(("File size (%RU64) exceeds total size to transfer (%RU64)\n", cbSize, pCtx->mData.getTotal()));
759 return VERR_INVALID_PARAMETER;
760 }
761
762 if (pCtx->mURI.getObjToProcess() && pCtx->mURI.isComplete())
763 return VERR_INVALID_PARAMETER;
764
765 int rc = VINF_SUCCESS;
766
767 do
768 {
769 GuestDnDURIObjCtx &objCtx = pCtx->mURI.getObj(0); /** @todo Fill in context ID. */
770 DnDURIObject *pObj = objCtx.getObj();
771
772 /*
773 * Sanity checking.
774 */
775 if (pObj)
776 {
777 if ( pObj->IsOpen()
778 && !pObj->IsComplete())
779 {
780 AssertMsgFailed(("Object '%s' not complete yet\n", pObj->GetDestPath().c_str()));
781 rc = VERR_WRONG_ORDER;
782 break;
783 }
784
785 if (pObj->IsOpen()) /* File already opened? */
786 {
787 AssertMsgFailed(("Current opened object is '%s', close this first\n", pObj->GetDestPath().c_str()));
788 rc = VERR_WRONG_ORDER;
789 break;
790 }
791 }
792
793 /*
794 * Create new intermediate object to work with.
795 */
796 rc = objCtx.createIntermediate();
797 if (RT_SUCCESS(rc))
798 {
799 pObj = objCtx.getObj();
800
801 const char *pszDroppedFilesDir = pCtx->mURI.getDroppedFiles().GetDirAbs();
802
803 char pszPathAbs[RTPATH_MAX];
804 rc = RTPathJoin(pszPathAbs, sizeof(pszPathAbs), pszDroppedFilesDir, pszPath);
805 if (RT_FAILURE(rc))
806 {
807 LogFlowFunc(("Warning: Rebasing current file failed with rc=%Rrc\n", rc));
808 break;
809 }
810
811 rc = DnDPathSanitize(pszPathAbs, sizeof(pszPathAbs));
812 if (RT_FAILURE(rc))
813 {
814 LogFlowFunc(("Warning: Rebasing current file failed with rc=%Rrc\n", rc));
815 break;
816 }
817
818 LogFunc(("Rebased to: %s\n", pszPathAbs));
819
820 /** @todo Add sparse file support based on fFlags? (Use Open(..., fFlags | SPARSE). */
821 rc = pObj->OpenEx(pszPathAbs, DnDURIObject::File, DnDURIObject::Target,
822 RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE,
823 (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR);
824 }
825
826 if (RT_SUCCESS(rc))
827 {
828 /* Note: Protocol v1 does not send any file sizes, so always 0. */
829 if (mDataBase.m_uProtocolVersion >= 2)
830 rc = pObj->SetSize(cbSize);
831
832 /** @todo Unescpae path before printing. */
833 LogRel2(("DnD: Transferring guest file to host: %s (%RU64 bytes, mode 0x%x)\n",
834 pObj->GetDestPath().c_str(), pObj->GetSize(), pObj->GetMode()));
835
836 /** @todo Set progress object title to current file being transferred? */
837
838 if (!cbSize) /* 0-byte file? Close again. */
839 pObj->Close();
840 }
841
842 if (RT_FAILURE(rc))
843 {
844 LogRel2(("DnD: Error opening/creating guest file '%s' on host, rc=%Rrc\n",
845 pObj->GetDestPath().c_str(), rc));
846 break;
847 }
848
849 } while (0);
850
851 LogFlowFuncLeaveRC(rc);
852 return rc;
853}
854
855int GuestDnDSource::i_onReceiveFileData(PRECVDATACTX pCtx, const void *pvData, uint32_t cbData)
856{
857 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
858 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
859 AssertReturn(cbData, VERR_INVALID_PARAMETER);
860
861 int rc = VINF_SUCCESS;
862
863 LogFlowFunc(("pvData=%p, cbData=%RU32, cbBlockSize=%RU32\n", pvData, cbData, mData.mcbBlockSize));
864
865 /*
866 * Sanity checking.
867 */
868 if (cbData > mData.mcbBlockSize)
869 return VERR_INVALID_PARAMETER;
870
871 do
872 {
873 GuestDnDURIObjCtx &objCtx = pCtx->mURI.getObj(0); /** @todo Fill in context ID. */
874 DnDURIObject *pObj = objCtx.getObj();
875
876 if (!pObj)
877 {
878 LogFlowFunc(("Warning: No current object set\n"));
879 rc = VERR_WRONG_ORDER;
880 break;
881 }
882
883 if (pObj->IsComplete())
884 {
885 LogFlowFunc(("Warning: Object '%s' already completed\n", pObj->GetDestPath().c_str()));
886 rc = VERR_WRONG_ORDER;
887 break;
888 }
889
890 if (!pObj->IsOpen()) /* File opened on host? */
891 {
892 LogFlowFunc(("Warning: Object '%s' not opened\n", pObj->GetDestPath().c_str()));
893 rc = VERR_WRONG_ORDER;
894 break;
895 }
896
897 uint32_t cbWritten;
898 rc = pObj->Write(pvData, cbData, &cbWritten);
899 if (RT_SUCCESS(rc))
900 {
901 Assert(cbWritten <= cbData);
902 if (cbWritten < cbData)
903 {
904 /** @todo What to do when the host's disk is full? */
905 rc = VERR_DISK_FULL;
906 }
907
908 if (RT_SUCCESS(rc))
909 rc = updateProgress(&pCtx->mData, pCtx->mpResp, cbWritten);
910 }
911
912 if (RT_SUCCESS(rc))
913 {
914 if (pObj->IsComplete())
915 {
916 /** @todo Sanitize path. */
917 LogRel2(("DnD: File transfer to host complete: %s\n", pObj->GetDestPath().c_str()));
918 pCtx->mURI.processObject(*pObj);
919 objCtx.reset();
920 }
921 }
922 else
923 {
924 /** @todo What to do when the host's disk is full? */
925 LogRel(("DnD: Error writing guest file to host to '%s': %Rrc\n", pObj->GetDestPath().c_str(), rc));
926 }
927
928 } while (0);
929
930 LogFlowFuncLeaveRC(rc);
931 return rc;
932}
933#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
934
935int GuestDnDSource::i_receiveData(PRECVDATACTX pCtx, RTMSINTERVAL msTimeout)
936{
937 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
938
939 /* Is this context already in receiving state? */
940 if (ASMAtomicReadBool(&pCtx->mIsActive))
941 return VERR_WRONG_ORDER;
942 ASMAtomicWriteBool(&pCtx->mIsActive, true);
943
944 GuestDnD *pInst = GuestDnDInst();
945 if (!pInst)
946 return VERR_INVALID_POINTER;
947
948 GuestDnDResponse *pResp = pCtx->mpResp;
949 AssertPtr(pCtx->mpResp);
950
951 int rc = pCtx->mCBEvent.Reset();
952 if (RT_FAILURE(rc))
953 return rc;
954
955 /*
956 * Reset any old data.
957 */
958 pCtx->mData.reset();
959 pCtx->mURI.reset();
960 pResp->reset();
961
962 /*
963 * Do we need to receive a different format than initially requested?
964 *
965 * For example, receiving a file link as "text/plain" requires still to receive
966 * the file from the guest as "text/uri-list" first, then pointing to
967 * the file path on the host in the "text/plain" data returned.
968 */
969
970 /* Plain text needed? */
971 if (pCtx->mFmtReq.equalsIgnoreCase("text/plain"))
972 {
973 /* Did the guest offer a file? Receive a file instead. */
974 if (GuestDnD::isFormatInFormatList("text/uri-list", pCtx->mFmtOffered))
975 pCtx->mFmtRecv = "text/uri-list";
976
977 /** @todo Add more conversions here. */
978 }
979
980 if (pCtx->mFmtRecv.isEmpty())
981 pCtx->mFmtRecv = pCtx->mFmtReq;
982
983 if (!pCtx->mFmtRecv.equals(pCtx->mFmtReq))
984 LogRel3(("DnD: Requested data in format '%s', receiving in intermediate format '%s' now\n",
985 pCtx->mFmtReq.c_str(), pCtx->mFmtRecv.c_str()));
986
987 /*
988 * Call the appropriate receive handler based on the data format to handle.
989 */
990 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFmtRecv.c_str(), pCtx->mFmtRecv.length());
991 LogFlowFunc(("strFormatReq=%s, strFormatRecv=%s, uAction=0x%x, fHasURIList=%RTbool\n",
992 pCtx->mFmtReq.c_str(), pCtx->mFmtRecv.c_str(), pCtx->mAction, fHasURIList));
993
994 if (fHasURIList)
995 {
996 rc = i_receiveURIData(pCtx, msTimeout);
997 }
998 else
999 {
1000 rc = i_receiveRawData(pCtx, msTimeout);
1001 }
1002
1003 ASMAtomicWriteBool(&pCtx->mIsActive, false);
1004
1005 LogFlowFuncLeaveRC(rc);
1006 return rc;
1007}
1008
1009/* static */
1010DECLCALLBACK(int) GuestDnDSource::i_receiveDataThread(RTTHREAD Thread, void *pvUser)
1011{
1012 LogFlowFunc(("pvUser=%p\n", pvUser));
1013
1014 RecvDataTask *pTask = (RecvDataTask *)pvUser;
1015 AssertPtrReturn(pTask, VERR_INVALID_POINTER);
1016
1017 const ComObjPtr<GuestDnDSource> pThis(pTask->getSource());
1018 Assert(!pThis.isNull());
1019
1020 AutoCaller autoCaller(pThis);
1021 if (FAILED(autoCaller.rc())) return VERR_COM_INVALID_OBJECT_STATE;
1022
1023 int rc = RTThreadUserSignal(Thread);
1024 AssertRC(rc);
1025
1026 rc = pThis->i_receiveData(pTask->getCtx(), RT_INDEFINITE_WAIT /* msTimeout */);
1027
1028 AutoWriteLock alock(pThis COMMA_LOCKVAL_SRC_POS);
1029
1030 Assert(pThis->mDataBase.m_cTransfersPending);
1031 pThis->mDataBase.m_cTransfersPending--;
1032
1033 LogFlowFunc(("pSource=%p returning rc=%Rrc\n", (GuestDnDSource *)pThis, rc));
1034 return rc;
1035}
1036
1037int GuestDnDSource::i_receiveRawData(PRECVDATACTX pCtx, RTMSINTERVAL msTimeout)
1038{
1039 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1040
1041 int rc;
1042
1043 GuestDnDResponse *pResp = pCtx->mpResp;
1044 AssertPtr(pCtx->mpResp);
1045
1046 GuestDnD *pInst = GuestDnDInst();
1047 if (!pInst)
1048 return VERR_INVALID_POINTER;
1049
1050#define REGISTER_CALLBACK(x) \
1051 rc = pResp->setCallback(x, i_receiveRawDataCallback, pCtx); \
1052 if (RT_FAILURE(rc)) \
1053 return rc;
1054
1055#define UNREGISTER_CALLBACK(x) \
1056 rc = pCtx->mpResp->setCallback(x, NULL); \
1057 AssertRC(rc);
1058
1059 /*
1060 * Register callbacks.
1061 */
1062 REGISTER_CALLBACK(GUEST_DND_CONNECT);
1063 REGISTER_CALLBACK(GUEST_DND_DISCONNECT);
1064 REGISTER_CALLBACK(GUEST_DND_GH_EVT_ERROR);
1065 REGISTER_CALLBACK(GUEST_DND_GH_SND_DATA);
1066
1067 do
1068 {
1069 /*
1070 * Receive the raw data.
1071 */
1072 GuestDnDMsg Msg;
1073 Msg.setType(HOST_DND_GH_EVT_DROPPED);
1074 if (mDataBase.m_uProtocolVersion >= 3)
1075 Msg.setNextUInt32(0); /** @todo ContextID not used yet. */
1076 Msg.setNextPointer((void*)pCtx->mFmtRecv.c_str(), (uint32_t)pCtx->mFmtRecv.length() + 1);
1077 Msg.setNextUInt32((uint32_t)pCtx->mFmtRecv.length() + 1);
1078 Msg.setNextUInt32(pCtx->mAction);
1079
1080 /* Make the initial call to the guest by telling that we initiated the "dropped" event on
1081 * the host and therefore now waiting for the actual raw data. */
1082 rc = pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
1083 if (RT_SUCCESS(rc))
1084 {
1085 rc = waitForEvent(&pCtx->mCBEvent, pCtx->mpResp, msTimeout);
1086 if (RT_SUCCESS(rc))
1087 rc = pCtx->mpResp->setProgress(100, DND_PROGRESS_COMPLETE, VINF_SUCCESS);
1088 }
1089
1090 } while (0);
1091
1092 /*
1093 * Unregister callbacks.
1094 */
1095 UNREGISTER_CALLBACK(GUEST_DND_CONNECT);
1096 UNREGISTER_CALLBACK(GUEST_DND_DISCONNECT);
1097 UNREGISTER_CALLBACK(GUEST_DND_GH_EVT_ERROR);
1098 UNREGISTER_CALLBACK(GUEST_DND_GH_SND_DATA);
1099
1100#undef REGISTER_CALLBACK
1101#undef UNREGISTER_CALLBACK
1102
1103 if (RT_FAILURE(rc))
1104 {
1105 if (rc == VERR_CANCELLED)
1106 {
1107 int rc2 = pCtx->mpResp->setProgress(100, DND_PROGRESS_CANCELLED, VINF_SUCCESS);
1108 AssertRC(rc2);
1109
1110 rc2 = sendCancel();
1111 AssertRC(rc2);
1112 }
1113 else if (rc != VERR_GSTDND_GUEST_ERROR) /* Guest-side error are already handled in the callback. */
1114 {
1115 rc = pCtx->mpResp->setProgress(100, DND_PROGRESS_ERROR,
1116 rc, GuestDnDSource::i_hostErrorToString(rc));
1117 }
1118 }
1119
1120 LogFlowFuncLeaveRC(rc);
1121 return rc;
1122}
1123
1124int GuestDnDSource::i_receiveURIData(PRECVDATACTX pCtx, RTMSINTERVAL msTimeout)
1125{
1126 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1127
1128 int rc;
1129
1130 GuestDnDResponse *pResp = pCtx->mpResp;
1131 AssertPtr(pCtx->mpResp);
1132
1133 GuestDnD *pInst = GuestDnDInst();
1134 if (!pInst)
1135 return VERR_INVALID_POINTER;
1136
1137#define REGISTER_CALLBACK(x) \
1138 rc = pResp->setCallback(x, i_receiveURIDataCallback, pCtx); \
1139 if (RT_FAILURE(rc)) \
1140 return rc;
1141
1142#define UNREGISTER_CALLBACK(x) \
1143 { \
1144 int rc2 = pResp->setCallback(x, NULL); \
1145 AssertRC(rc2); \
1146 }
1147
1148 /*
1149 * Register callbacks.
1150 */
1151 /* Guest callbacks. */
1152 REGISTER_CALLBACK(GUEST_DND_CONNECT);
1153 REGISTER_CALLBACK(GUEST_DND_DISCONNECT);
1154 REGISTER_CALLBACK(GUEST_DND_GH_EVT_ERROR);
1155 if (mDataBase.m_uProtocolVersion >= 3)
1156 REGISTER_CALLBACK(GUEST_DND_GH_SND_DATA_HDR);
1157 REGISTER_CALLBACK(GUEST_DND_GH_SND_DATA);
1158 REGISTER_CALLBACK(GUEST_DND_GH_SND_DIR);
1159 if (mDataBase.m_uProtocolVersion >= 2)
1160 REGISTER_CALLBACK(GUEST_DND_GH_SND_FILE_HDR);
1161 REGISTER_CALLBACK(GUEST_DND_GH_SND_FILE_DATA);
1162
1163 DnDDroppedFiles &droppedFiles = pCtx->mURI.getDroppedFiles();
1164
1165 do
1166 {
1167 rc = droppedFiles.OpenTemp(0 /* fFlags */);
1168 if (RT_FAILURE(rc))
1169 break;
1170 LogFlowFunc(("rc=%Rrc, strDropDir=%s\n", rc, droppedFiles.GetDirAbs()));
1171 if (RT_FAILURE(rc))
1172 break;
1173
1174 /*
1175 * Receive the URI list.
1176 */
1177 GuestDnDMsg Msg;
1178 Msg.setType(HOST_DND_GH_EVT_DROPPED);
1179 if (mDataBase.m_uProtocolVersion >= 3)
1180 Msg.setNextUInt32(0); /** @todo ContextID not used yet. */
1181 Msg.setNextPointer((void*)pCtx->mFmtRecv.c_str(), (uint32_t)pCtx->mFmtRecv.length() + 1);
1182 Msg.setNextUInt32((uint32_t)pCtx->mFmtRecv.length() + 1);
1183 Msg.setNextUInt32(pCtx->mAction);
1184
1185 /* Make the initial call to the guest by telling that we initiated the "dropped" event on
1186 * the host and therefore now waiting for the actual URI data. */
1187 rc = pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
1188 if (RT_SUCCESS(rc))
1189 {
1190 LogFlowFunc(("Waiting ...\n"));
1191
1192 rc = waitForEvent(&pCtx->mCBEvent, pCtx->mpResp, msTimeout);
1193 if (RT_SUCCESS(rc))
1194 rc = pCtx->mpResp->setProgress(100, DND_PROGRESS_COMPLETE, VINF_SUCCESS);
1195
1196 LogFlowFunc(("Waiting ended with rc=%Rrc\n", rc));
1197 }
1198
1199 } while (0);
1200
1201 /*
1202 * Unregister callbacks.
1203 */
1204 UNREGISTER_CALLBACK(GUEST_DND_CONNECT);
1205 UNREGISTER_CALLBACK(GUEST_DND_DISCONNECT);
1206 UNREGISTER_CALLBACK(GUEST_DND_GH_EVT_ERROR);
1207 UNREGISTER_CALLBACK(GUEST_DND_GH_SND_DATA_HDR);
1208 UNREGISTER_CALLBACK(GUEST_DND_GH_SND_DATA);
1209 UNREGISTER_CALLBACK(GUEST_DND_GH_SND_DIR);
1210 UNREGISTER_CALLBACK(GUEST_DND_GH_SND_FILE_HDR);
1211 UNREGISTER_CALLBACK(GUEST_DND_GH_SND_FILE_DATA);
1212
1213#undef REGISTER_CALLBACK
1214#undef UNREGISTER_CALLBACK
1215
1216 int rc2;
1217
1218 if (RT_FAILURE(rc))
1219 {
1220 if (rc == VERR_CANCELLED)
1221 {
1222 rc2 = pCtx->mpResp->setProgress(100, DND_PROGRESS_CANCELLED, VINF_SUCCESS);
1223 AssertRC(rc2);
1224
1225 rc2 = sendCancel();
1226 AssertRC(rc2);
1227 }
1228 else if (rc != VERR_GSTDND_GUEST_ERROR) /* Guest-side error are already handled in the callback. */
1229 {
1230 rc = pCtx->mpResp->setProgress(100, DND_PROGRESS_ERROR,
1231 rc, GuestDnDSource::i_hostErrorToString(rc));
1232 }
1233 }
1234
1235 if (RT_FAILURE(rc))
1236 {
1237 rc2 = droppedFiles.Rollback();
1238 if (RT_FAILURE(rc2))
1239 LogRel2(("DnD: Rollback failed with %Rrc\n", rc2));
1240 }
1241
1242 droppedFiles.Close();
1243
1244 LogFlowFuncLeaveRC(rc);
1245 return rc;
1246}
1247
1248/* static */
1249DECLCALLBACK(int) GuestDnDSource::i_receiveRawDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
1250{
1251 PRECVDATACTX pCtx = (PRECVDATACTX)pvUser;
1252 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1253
1254 GuestDnDSource *pThis = pCtx->mpSource;
1255 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1256
1257 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
1258
1259 int rc = VINF_SUCCESS;
1260
1261 int rcCallback = VINF_SUCCESS; /* rc for the callback. */
1262 bool fNotify = false;
1263
1264 switch (uMsg)
1265 {
1266 case GUEST_DND_CONNECT:
1267 /* Nothing to do here (yet). */
1268 break;
1269
1270 case GUEST_DND_DISCONNECT:
1271 rc = VERR_CANCELLED;
1272 break;
1273
1274#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1275 case GUEST_DND_GH_SND_DATA_HDR:
1276 {
1277 PVBOXDNDCBSNDDATAHDRDATA pCBData = reinterpret_cast<PVBOXDNDCBSNDDATAHDRDATA>(pvParms);
1278 AssertPtr(pCBData);
1279 AssertReturn(sizeof(VBOXDNDCBSNDDATAHDRDATA) == cbParms, VERR_INVALID_PARAMETER);
1280 AssertReturn(CB_MAGIC_DND_GH_SND_DATA_HDR == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
1281
1282 rc = pThis->i_onReceiveDataHdr(pCtx, &pCBData->data);
1283 break;
1284 }
1285 case GUEST_DND_GH_SND_DATA:
1286 {
1287 PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<PVBOXDNDCBSNDDATADATA>(pvParms);
1288 AssertPtr(pCBData);
1289 AssertReturn(sizeof(VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1290 AssertReturn(CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
1291
1292 rc = pThis->i_onReceiveData(pCtx, &pCBData->data);
1293 break;
1294 }
1295 case GUEST_DND_GH_EVT_ERROR:
1296 {
1297 PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<PVBOXDNDCBEVTERRORDATA>(pvParms);
1298 AssertPtr(pCBData);
1299 AssertReturn(sizeof(VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
1300 AssertReturn(CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
1301
1302 pCtx->mpResp->reset();
1303
1304 if (RT_SUCCESS(pCBData->rc))
1305 pCBData->rc = VERR_GENERAL_FAILURE; /* Make sure some error is set. */
1306
1307 rc = pCtx->mpResp->setProgress(100, DND_PROGRESS_ERROR, pCBData->rc,
1308 GuestDnDSource::i_guestErrorToString(pCBData->rc));
1309 if (RT_SUCCESS(rc))
1310 rcCallback = VERR_GSTDND_GUEST_ERROR;
1311 break;
1312 }
1313#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1314 default:
1315 rc = VERR_NOT_SUPPORTED;
1316 break;
1317 }
1318
1319 if (RT_FAILURE(rc))
1320 {
1321 int rc2 = pCtx->mCBEvent.Notify(rc);
1322 AssertRC(rc2);
1323 }
1324
1325 LogFlowFuncLeaveRC(rc);
1326 return rc; /* Tell the guest. */
1327}
1328
1329/* static */
1330DECLCALLBACK(int) GuestDnDSource::i_receiveURIDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
1331{
1332 PRECVDATACTX pCtx = (PRECVDATACTX)pvUser;
1333 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1334
1335 GuestDnDSource *pThis = pCtx->mpSource;
1336 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1337
1338 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
1339
1340 int rc = VINF_SUCCESS;
1341
1342 int rcCallback = VINF_SUCCESS; /* rc for the callback. */
1343 bool fNotify = false;
1344
1345 switch (uMsg)
1346 {
1347 case GUEST_DND_CONNECT:
1348 /* Nothing to do here (yet). */
1349 break;
1350
1351 case GUEST_DND_DISCONNECT:
1352 rc = VERR_CANCELLED;
1353 break;
1354
1355#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1356 case GUEST_DND_GH_SND_DATA_HDR:
1357 {
1358 PVBOXDNDCBSNDDATAHDRDATA pCBData = reinterpret_cast<PVBOXDNDCBSNDDATAHDRDATA>(pvParms);
1359 AssertPtr(pCBData);
1360 AssertReturn(sizeof(VBOXDNDCBSNDDATAHDRDATA) == cbParms, VERR_INVALID_PARAMETER);
1361 AssertReturn(CB_MAGIC_DND_GH_SND_DATA_HDR == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
1362
1363 rc = pThis->i_onReceiveDataHdr(pCtx, &pCBData->data);
1364 break;
1365 }
1366 case GUEST_DND_GH_SND_DATA:
1367 {
1368 PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<PVBOXDNDCBSNDDATADATA>(pvParms);
1369 AssertPtr(pCBData);
1370 AssertReturn(sizeof(VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1371 AssertReturn(CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
1372
1373 rc = pThis->i_onReceiveData(pCtx, &pCBData->data);
1374 break;
1375 }
1376 case GUEST_DND_GH_SND_DIR:
1377 {
1378 PVBOXDNDCBSNDDIRDATA pCBData = reinterpret_cast<PVBOXDNDCBSNDDIRDATA>(pvParms);
1379 AssertPtr(pCBData);
1380 AssertReturn(sizeof(VBOXDNDCBSNDDIRDATA) == cbParms, VERR_INVALID_PARAMETER);
1381 AssertReturn(CB_MAGIC_DND_GH_SND_DIR == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
1382
1383 rc = pThis->i_onReceiveDir(pCtx, pCBData->pszPath, pCBData->cbPath, pCBData->fMode);
1384 break;
1385 }
1386 case GUEST_DND_GH_SND_FILE_HDR:
1387 {
1388 PVBOXDNDCBSNDFILEHDRDATA pCBData = reinterpret_cast<PVBOXDNDCBSNDFILEHDRDATA>(pvParms);
1389 AssertPtr(pCBData);
1390 AssertReturn(sizeof(VBOXDNDCBSNDFILEHDRDATA) == cbParms, VERR_INVALID_PARAMETER);
1391 AssertReturn(CB_MAGIC_DND_GH_SND_FILE_HDR == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
1392
1393 rc = pThis->i_onReceiveFileHdr(pCtx, pCBData->pszFilePath, pCBData->cbFilePath,
1394 pCBData->cbSize, pCBData->fMode, pCBData->fFlags);
1395 break;
1396 }
1397 case GUEST_DND_GH_SND_FILE_DATA:
1398 {
1399 PVBOXDNDCBSNDFILEDATADATA pCBData = reinterpret_cast<PVBOXDNDCBSNDFILEDATADATA>(pvParms);
1400 AssertPtr(pCBData);
1401 AssertReturn(sizeof(VBOXDNDCBSNDFILEDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1402 AssertReturn(CB_MAGIC_DND_GH_SND_FILE_DATA == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
1403
1404 if (pThis->mDataBase.m_uProtocolVersion <= 1)
1405 {
1406 /**
1407 * Notes for protocol v1 (< VBox 5.0):
1408 * - Every time this command is being sent it includes the file header,
1409 * so just process both calls here.
1410 * - There was no information whatsoever about the total file size; the old code only
1411 * appended data to the desired file. So just pass 0 as cbSize.
1412 */
1413 rc = pThis->i_onReceiveFileHdr(pCtx, pCBData->u.v1.pszFilePath, pCBData->u.v1.cbFilePath,
1414 0 /* cbSize */, pCBData->u.v1.fMode, 0 /* fFlags */);
1415 if (RT_SUCCESS(rc))
1416 rc = pThis->i_onReceiveFileData(pCtx, pCBData->pvData, pCBData->cbData);
1417 }
1418 else /* Protocol v2 and up. */
1419 rc = pThis->i_onReceiveFileData(pCtx, pCBData->pvData, pCBData->cbData);
1420 break;
1421 }
1422 case GUEST_DND_GH_EVT_ERROR:
1423 {
1424 PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<PVBOXDNDCBEVTERRORDATA>(pvParms);
1425 AssertPtr(pCBData);
1426 AssertReturn(sizeof(VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
1427 AssertReturn(CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
1428
1429 pCtx->mpResp->reset();
1430
1431 if (RT_SUCCESS(pCBData->rc))
1432 pCBData->rc = VERR_GENERAL_FAILURE; /* Make sure some error is set. */
1433
1434 rc = pCtx->mpResp->setProgress(100, DND_PROGRESS_ERROR, pCBData->rc,
1435 GuestDnDSource::i_guestErrorToString(pCBData->rc));
1436 if (RT_SUCCESS(rc))
1437 rcCallback = VERR_GSTDND_GUEST_ERROR;
1438 break;
1439 }
1440#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1441 default:
1442 rc = VERR_NOT_SUPPORTED;
1443 break;
1444 }
1445
1446 if ( RT_FAILURE(rc)
1447 || RT_FAILURE(rcCallback))
1448 {
1449 fNotify = true;
1450 if (RT_SUCCESS(rcCallback))
1451 rcCallback = rc;
1452 }
1453
1454 if (RT_FAILURE(rc))
1455 {
1456 switch (rc)
1457 {
1458 case VERR_NO_DATA:
1459 LogRel2(("DnD: Transfer to host complete\n"));
1460 break;
1461
1462 case VERR_CANCELLED:
1463 LogRel2(("DnD: Transfer to host canceled\n"));
1464 break;
1465
1466 default:
1467 LogRel(("DnD: Error %Rrc occurred, aborting transfer to host\n", rc));
1468 break;
1469 }
1470
1471 /* Unregister this callback. */
1472 AssertPtr(pCtx->mpResp);
1473 int rc2 = pCtx->mpResp->setCallback(uMsg, NULL /* PFNGUESTDNDCALLBACK */);
1474 AssertRC(rc2);
1475 }
1476
1477 /* All data processed? */
1478 if ( pCtx->mURI.isComplete()
1479 && pCtx->mData.isComplete())
1480 {
1481 fNotify = true;
1482 }
1483
1484 LogFlowFunc(("cbProcessed=%RU64, cbToProcess=%RU64, fNotify=%RTbool, rcCallback=%Rrc, rc=%Rrc\n",
1485 pCtx->mData.getProcessed(), pCtx->mData.getTotal(), fNotify, rcCallback, rc));
1486
1487 if (fNotify)
1488 {
1489 int rc2 = pCtx->mCBEvent.Notify(rcCallback);
1490 AssertRC(rc2);
1491 }
1492
1493 LogFlowFuncLeaveRC(rc);
1494 return rc; /* Tell the guest. */
1495}
1496
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