VirtualBox

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

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

Main: a few fixes for -Wunused -Wconversion

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette