VirtualBox

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

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

DnD: Updates/bugfixes:

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