VirtualBox

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

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

DnD: GuestDnDSourceImpl: Simpler guest->host accounting; some logging adjustments.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.3 KB
Line 
1/* $Id: GuestDnDSourceImpl.cpp 56215 2015-06-03 11:02:03Z 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#include <VBox/GuestHost/DragAndDrop.h>
40#include <VBox/HostServices/DragAndDropSvc.h>
41
42#ifdef LOG_GROUP
43 #undef LOG_GROUP
44#endif
45#define LOG_GROUP LOG_GROUP_GUEST_DND
46#include <VBox/log.h>
47
48/**
49 * Base class for a source task.
50 */
51class GuestDnDSourceTask
52{
53public:
54
55 GuestDnDSourceTask(GuestDnDSource *pSource)
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 virtual ~RecvDataTask(void) { }
84
85 PRECVDATACTX getCtx(void) { return mpCtx; }
86
87protected:
88
89 /** Pointer to receive data context. */
90 PRECVDATACTX mpCtx;
91};
92
93// constructor / destructor
94/////////////////////////////////////////////////////////////////////////////
95
96DEFINE_EMPTY_CTOR_DTOR(GuestDnDSource)
97
98HRESULT GuestDnDSource::FinalConstruct(void)
99{
100 /* Set the maximum block size this source can handle to 64K. This always has
101 * been hardcoded until now. */
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 mData.mcbBlockSize = _64K; /** @todo Make this configurable. */
105
106 LogFlowThisFunc(("\n"));
107 return BaseFinalConstruct();
108}
109
110void GuestDnDSource::FinalRelease(void)
111{
112 LogFlowThisFuncEnter();
113 uninit();
114 BaseFinalRelease();
115 LogFlowThisFuncLeave();
116}
117
118// public initializer/uninitializer for internal purposes only
119/////////////////////////////////////////////////////////////////////////////
120
121int GuestDnDSource::init(const ComObjPtr<Guest>& pGuest)
122{
123 LogFlowThisFuncEnter();
124
125 /* Enclose the state transition NotReady->InInit->Ready. */
126 AutoInitSpan autoInitSpan(this);
127 AssertReturn(autoInitSpan.isOk(), E_FAIL);
128
129 unconst(m_pGuest) = pGuest;
130
131 /* Confirm a successful initialization when it's the case. */
132 autoInitSpan.setSucceeded();
133
134 return VINF_SUCCESS;
135}
136
137/**
138 * Uninitializes the instance.
139 * Called from FinalRelease().
140 */
141void GuestDnDSource::uninit(void)
142{
143 LogFlowThisFunc(("\n"));
144
145 /* Enclose the state transition Ready->InUninit->NotReady. */
146 AutoUninitSpan autoUninitSpan(this);
147 if (autoUninitSpan.uninitDone())
148 return;
149}
150
151// implementation of wrapped IDnDBase methods.
152/////////////////////////////////////////////////////////////////////////////
153
154HRESULT GuestDnDSource::isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported)
155{
156#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
157 ReturnComNotImplemented();
158#else /* VBOX_WITH_DRAG_AND_DROP */
159
160 AutoCaller autoCaller(this);
161 if (FAILED(autoCaller.rc())) return autoCaller.rc();
162
163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
164
165 return GuestDnDBase::i_isFormatSupported(aFormat, aSupported);
166#endif /* VBOX_WITH_DRAG_AND_DROP */
167}
168
169HRESULT GuestDnDSource::getFormats(std::vector<com::Utf8Str> &aFormats)
170{
171#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
172 ReturnComNotImplemented();
173#else /* VBOX_WITH_DRAG_AND_DROP */
174
175 AutoCaller autoCaller(this);
176 if (FAILED(autoCaller.rc())) return autoCaller.rc();
177
178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
179
180 return GuestDnDBase::i_getFormats(aFormats);
181#endif /* VBOX_WITH_DRAG_AND_DROP */
182}
183
184HRESULT GuestDnDSource::addFormats(const std::vector<com::Utf8Str> &aFormats)
185{
186#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
187 ReturnComNotImplemented();
188#else /* VBOX_WITH_DRAG_AND_DROP */
189
190 AutoCaller autoCaller(this);
191 if (FAILED(autoCaller.rc())) return autoCaller.rc();
192
193 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
194
195 return GuestDnDBase::i_addFormats(aFormats);
196#endif /* VBOX_WITH_DRAG_AND_DROP */
197}
198
199HRESULT GuestDnDSource::removeFormats(const std::vector<com::Utf8Str> &aFormats)
200{
201#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
202 ReturnComNotImplemented();
203#else /* VBOX_WITH_DRAG_AND_DROP */
204
205 AutoCaller autoCaller(this);
206 if (FAILED(autoCaller.rc())) return autoCaller.rc();
207
208 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
209
210 return GuestDnDBase::i_removeFormats(aFormats);
211#endif /* VBOX_WITH_DRAG_AND_DROP */
212}
213
214HRESULT GuestDnDSource::getProtocolVersion(ULONG *aProtocolVersion)
215{
216#if !defined(VBOX_WITH_DRAG_AND_DROP)
217 ReturnComNotImplemented();
218#else /* VBOX_WITH_DRAG_AND_DROP */
219
220 AutoCaller autoCaller(this);
221 if (FAILED(autoCaller.rc())) return autoCaller.rc();
222
223 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
224
225 return GuestDnDBase::i_getProtocolVersion(aProtocolVersion);
226#endif /* VBOX_WITH_DRAG_AND_DROP */
227}
228
229// implementation of wrapped IDnDSource methods.
230/////////////////////////////////////////////////////////////////////////////
231
232HRESULT GuestDnDSource::dragIsPending(ULONG uScreenId, std::vector<com::Utf8Str> &aFormats,
233 std::vector<DnDAction_T> &aAllowedActions, DnDAction_T *aDefaultAction)
234{
235#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
236 ReturnComNotImplemented();
237#else /* VBOX_WITH_DRAG_AND_DROP */
238
239 AutoCaller autoCaller(this);
240 if (FAILED(autoCaller.rc())) return autoCaller.rc();
241
242 /* Determine guest DnD protocol to use. */
243 GuestDnDBase::getProtocolVersion(&mDataBase.mProtocolVersion);
244
245 /* Default is ignoring the action. */
246 DnDAction_T defaultAction = DnDAction_Ignore;
247
248 HRESULT hr = S_OK;
249
250 GuestDnDMsg Msg;
251 Msg.setType(DragAndDropSvc::HOST_DND_GH_REQ_PENDING);
252 Msg.setNextUInt32(uScreenId);
253
254 int rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
255 if (RT_SUCCESS(rc))
256 {
257 bool fFetchResult = true;
258 GuestDnDResponse *pResp = GuestDnDInst()->response();
259 if (pResp)
260 {
261 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
262 fFetchResult = false;
263
264 if (isDnDIgnoreAction(pResp->defAction()))
265 fFetchResult = false;
266
267 /* Fetch the default action to use. */
268 if (fFetchResult)
269 {
270 defaultAction = GuestDnD::toMainAction(pResp->defAction());
271
272 GuestDnD::toFormatVector(m_strFormats, pResp->format(), aFormats);
273 GuestDnD::toMainActions(pResp->allActions(), aAllowedActions);
274 }
275 }
276
277 if (aDefaultAction)
278 *aDefaultAction = defaultAction;
279 }
280
281 LogFlowFunc(("hr=%Rhrc, defaultAction=0x%x\n", hr, defaultAction));
282 return hr;
283#endif /* VBOX_WITH_DRAG_AND_DROP */
284}
285
286HRESULT GuestDnDSource::drop(const com::Utf8Str &aFormat, DnDAction_T aAction, ComPtr<IProgress> &aProgress)
287{
288#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
289 ReturnComNotImplemented();
290#else /* VBOX_WITH_DRAG_AND_DROP */
291
292 AutoCaller autoCaller(this);
293 if (FAILED(autoCaller.rc())) return autoCaller.rc();
294
295 /* Input validation. */
296 if (RT_UNLIKELY((aFormat.c_str()) == NULL || *(aFormat.c_str()) == '\0'))
297 return setError(E_INVALIDARG, tr("No drop format specified"));
298
299 uint32_t uAction = GuestDnD::toHGCMAction(aAction);
300 if (isDnDIgnoreAction(uAction)) /* If there is no usable action, ignore this request. */
301 return S_OK;
302
303 /* Note: At the moment we only support one transfer at a time. */
304 if (ASMAtomicReadBool(&mDataBase.mfTransferIsPending))
305 return setError(E_INVALIDARG, tr("Another drop operation already is in progress"));
306
307 /* Gets reset when the thread is finished. */
308 ASMAtomicWriteBool(&mDataBase.mfTransferIsPending, true);
309
310 /* Dito. */
311 GuestDnDResponse *pResp = GuestDnDInst()->response();
312 AssertPtr(pResp);
313
314 HRESULT hr = pResp->resetProgress(m_pGuest);
315 if (FAILED(hr))
316 return hr;
317
318 try
319 {
320 mData.mRecvCtx.mIsActive = false;
321 mData.mRecvCtx.mpSource = this;
322 mData.mRecvCtx.mpResp = pResp;
323 mData.mRecvCtx.mFormat = aFormat;
324
325 RecvDataTask *pTask = new RecvDataTask(this, &mData.mRecvCtx);
326 AssertReturn(pTask->isOk(), pTask->getRC());
327
328 LogFlowFunc(("Starting thread ...\n"));
329
330 int rc = RTThreadCreate(NULL, GuestDnDSource::i_receiveDataThread,
331 (void *)pTask, 0, RTTHREADTYPE_MAIN_WORKER, 0, "dndSrcRcvData");
332 if (RT_SUCCESS(rc))
333 {
334 hr = pResp->queryProgressTo(aProgress.asOutParam());
335 ComAssertComRC(hr);
336
337 /* Note: pTask is now owned by the worker thread. */
338 }
339 else
340 hr = setError(VBOX_E_IPRT_ERROR, tr("Starting thread failed (%Rrc)"), rc);
341 }
342 catch(std::bad_alloc &)
343 {
344 hr = setError(E_OUTOFMEMORY);
345 }
346
347 /* Note: mDataBase.mfTransferIsPending will be set to false again by i_receiveDataThread. */
348
349 LogFlowFunc(("Returning hr=%Rhrc\n", hr));
350 return hr;
351#endif /* VBOX_WITH_DRAG_AND_DROP */
352}
353
354HRESULT GuestDnDSource::receiveData(std::vector<BYTE> &aData)
355{
356#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
357 ReturnComNotImplemented();
358#else /* VBOX_WITH_DRAG_AND_DROP */
359
360 /* Input validation. */
361
362 AutoCaller autoCaller(this);
363 if (FAILED(autoCaller.rc())) return autoCaller.rc();
364
365 /* Don't allow receiving the actual data until our transfer
366 * actually is complete. */
367 if (ASMAtomicReadBool(&mDataBase.mfTransferIsPending))
368 return setError(E_INVALIDARG, tr("Current drop operation still in progress"));
369
370 PRECVDATACTX pCtx = &mData.mRecvCtx;
371
372 if (pCtx->mData.vecData.empty())
373 {
374 aData.resize(0);
375 return S_OK;
376 }
377
378 HRESULT hr = S_OK;
379 size_t cbData;
380
381 try
382 {
383 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFormat.c_str(), pCtx->mFormat.length());
384 if (fHasURIList)
385 {
386 Utf8Str strURIs = pCtx->mURI.lstURI.RootToString(RTCString(DnDDirDroppedFilesGetDirAbs(&pCtx->mURI.mDropDir)));
387 cbData = strURIs.length();
388
389 LogFlowFunc(("Found %zu root URIs (%zu bytes)\n", pCtx->mURI.lstURI.RootCount(), cbData));
390
391 aData.resize(cbData + 1 /* Include termination */);
392 memcpy(&aData.front(), strURIs.c_str(), cbData);
393 }
394 else
395 {
396 cbData = pCtx->mData.vecData.size();
397
398 /* Copy the data into a safe array of bytes. */
399 aData.resize(cbData);
400 memcpy(&aData.front(), &pCtx->mData.vecData[0], cbData);
401 }
402 }
403 catch (std::bad_alloc &)
404 {
405 hr = E_OUTOFMEMORY;
406 }
407
408 LogFlowFunc(("Returning cbData=%zu, hr=%Rhrc\n", cbData, hr));
409 return hr;
410#endif /* VBOX_WITH_DRAG_AND_DROP */
411}
412
413// implementation of internal methods.
414/////////////////////////////////////////////////////////////////////////////
415
416/* static */
417Utf8Str GuestDnDSource::i_guestErrorToString(int guestRc)
418{
419 Utf8Str strError;
420
421 switch (guestRc)
422 {
423 case VERR_ACCESS_DENIED:
424 strError += Utf8StrFmt(tr("For one or more guest files or directories selected for transferring to the host your guest "
425 "user does not have the appropriate access rights for. Please make sure that all selected "
426 "elements can be accessed and that your guest user has the appropriate rights."));
427 break;
428
429 case VERR_NOT_FOUND:
430 /* Should not happen due to file locking on the guest, but anyway ... */
431 strError += Utf8StrFmt(tr("One or more guest files or directories selected for transferring to the host were not"
432 "found on the guest anymore. This can be the case if the guest files were moved and/or"
433 "altered while the drag and drop operation was in progress."));
434 break;
435
436 case VERR_SHARING_VIOLATION:
437 strError += Utf8StrFmt(tr("One or more guest files or directories selected for transferring to the host were locked. "
438 "Please make sure that all selected elements can be accessed and that your guest user has "
439 "the appropriate rights."));
440 break;
441
442 default:
443 strError += Utf8StrFmt(tr("Drag and drop error from guest (%Rrc)"), guestRc);
444 break;
445 }
446
447 return strError;
448}
449
450/* static */
451Utf8Str GuestDnDSource::i_hostErrorToString(int hostRc)
452{
453 Utf8Str strError;
454
455 switch (hostRc)
456 {
457 case VERR_ACCESS_DENIED:
458 strError += Utf8StrFmt(tr("For one or more host files or directories selected for transferring to the guest your host "
459 "user does not have the appropriate access rights for. Please make sure that all selected "
460 "elements can be accessed and that your host user has the appropriate rights."));
461 break;
462
463 case VERR_NOT_FOUND:
464 /* Should not happen due to file locking on the host, but anyway ... */
465 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the host were not"
466 "found on the host anymore. This can be the case if the host files were moved and/or"
467 "altered while the drag and drop operation was in progress."));
468 break;
469
470 case VERR_SHARING_VIOLATION:
471 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the guest were locked. "
472 "Please make sure that all selected elements can be accessed and that your host user has "
473 "the appropriate rights."));
474 break;
475
476 default:
477 strError += Utf8StrFmt(tr("Drag and drop error from host (%Rrc)"), hostRc);
478 break;
479 }
480
481 return strError;
482}
483
484#ifdef VBOX_WITH_DRAG_AND_DROP_GH
485int GuestDnDSource::i_onReceiveData(PRECVDATACTX pCtx, const void *pvData, uint32_t cbData, uint64_t cbTotalSize)
486{
487 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
488 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
489 AssertReturn(cbData, VERR_INVALID_PARAMETER);
490 AssertReturn(cbTotalSize, VERR_INVALID_PARAMETER);
491
492 LogFlowFunc(("cbData=%RU32, cbTotalSize=%RU64\n", cbData, cbTotalSize));
493
494 int rc = VINF_SUCCESS;
495
496 try
497 {
498 if ( cbData > cbTotalSize
499 || cbData > mData.mcbBlockSize)
500 {
501 LogFlowFunc(("Data sizes invalid: cbData=%RU32, cbTotalSize=%RU64\n", cbData, cbTotalSize));
502 rc = VERR_INVALID_PARAMETER;
503 }
504 else if (cbData < pCtx->mData.vecData.size())
505 {
506 AssertMsgFailed(("New size (%RU64) is smaller than current size (%zu)\n", cbTotalSize, pCtx->mData.vecData.size()));
507 rc = VERR_INVALID_PARAMETER;
508 }
509
510 if (RT_SUCCESS(rc))
511 {
512 pCtx->mData.vecData.insert(pCtx->mData.vecData.begin(), (BYTE *)pvData, (BYTE *)pvData + cbData);
513
514 LogFlowFunc(("vecDataSize=%zu, cbData=%RU32, cbTotalSize=%RU64\n", pCtx->mData.vecData.size(), cbData, cbTotalSize));
515
516 /* Data transfer complete? */
517 Assert(cbData <= pCtx->mData.vecData.size());
518 if (cbData == pCtx->mData.vecData.size())
519 {
520 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFormat.c_str(), pCtx->mFormat.length());
521 LogFlowFunc(("fHasURIList=%RTbool, cbTotalSize=%RU32\n", fHasURIList, cbTotalSize));
522 if (fHasURIList)
523 {
524 /* Try parsing the data as URI list. */
525 rc = pCtx->mURI.lstURI.RootFromURIData(&pCtx->mData.vecData[0], pCtx->mData.vecData.size(), 0 /* uFlags */);
526 if (RT_SUCCESS(rc))
527 {
528 /* Reset processed bytes. */
529 pCtx->mData.cbProcessed = 0;
530
531 /*
532 * Assign new total size which also includes all file data to receive
533 * from the guest.
534 */
535 pCtx->mData.cbToProcess = cbTotalSize;
536
537 /* Update our process with the data we already received.
538 * Note: The total size will consist of the meta data (in vecData) and
539 * the actual accumulated file/directory data from the guest. */
540 rc = i_updateProcess(pCtx, (uint64_t)pCtx->mData.vecData.size());
541
542 LogFlowFunc(("URI data => cbProcessed=%RU64, cbToProcess=%RU64, rc=%Rrc\n",
543 pCtx->mData.cbProcessed, pCtx->mData.cbToProcess, rc));
544 }
545 }
546 }
547 }
548 }
549 catch (std::bad_alloc &)
550 {
551 rc = VERR_NO_MEMORY;
552 }
553
554 LogFlowFuncLeaveRC(rc);
555 return rc;
556}
557
558int GuestDnDSource::i_onReceiveDir(PRECVDATACTX pCtx, const char *pszPath, uint32_t cbPath, uint32_t fMode)
559{
560 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
561 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
562 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
563
564 LogFlowFunc(("pszPath=%s, cbPath=%RU32, fMode=0x%x\n", pszPath, cbPath, fMode));
565
566 int rc;
567 char *pszDir = RTPathJoinA(DnDDirDroppedFilesGetDirAbs(&pCtx->mURI.mDropDir), pszPath);
568 if (pszDir)
569 {
570 rc = RTDirCreateFullPath(pszDir, fMode);
571 if (RT_FAILURE(rc))
572 LogRel2(("DnD: Error creating guest directory '%s' on the host, rc=%Rrc\n", pszDir, rc));
573
574 RTStrFree(pszDir);
575 }
576 else
577 rc = VERR_NO_MEMORY;
578
579 LogFlowFuncLeaveRC(rc);
580 return rc;
581}
582
583int GuestDnDSource::i_onReceiveFileHdr(PRECVDATACTX pCtx, const char *pszPath, uint32_t cbPath,
584 uint64_t cbSize, uint32_t fMode, uint32_t fFlags)
585{
586 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
587 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
588 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
589 AssertReturn(fMode, VERR_INVALID_PARAMETER);
590 /* fFlags are optional. */
591
592 LogFlowFunc(("pszPath=%s, cbPath=%RU32, cbSize=%RU64, fMode=0x%x, fFlags=0x%x\n", pszPath, cbPath, cbSize, fMode, fFlags));
593
594 int rc = VINF_SUCCESS;
595
596 do
597 {
598 if ( pCtx->mURI.objURI.IsOpen()
599 && !pCtx->mURI.objURI.IsComplete())
600 {
601 LogFlowFunc(("Warning: Object '%s' not complete yet\n", pCtx->mURI.objURI.GetDestPath().c_str()));
602 rc = VERR_INVALID_PARAMETER;
603 break;
604 }
605
606 if (pCtx->mURI.objURI.IsOpen()) /* File already opened? */
607 {
608 LogFlowFunc(("Warning: Current opened object is '%s'\n", pCtx->mURI.objURI.GetDestPath().c_str()));
609 rc = VERR_WRONG_ORDER;
610 break;
611 }
612
613 char pszPathAbs[RTPATH_MAX];
614 rc = RTPathJoin(pszPathAbs, sizeof(pszPathAbs), DnDDirDroppedFilesGetDirAbs(&pCtx->mURI.mDropDir), pszPath);
615 if (RT_FAILURE(rc))
616 {
617 LogFlowFunc(("Warning: Rebasing current file failed with rc=%Rrc\n", rc));
618 break;
619 }
620
621 rc = DnDPathSanitize(pszPathAbs, sizeof(pszPathAbs));
622 if (RT_FAILURE(rc))
623 {
624 LogFlowFunc(("Warning: Rebasing current file failed with rc=%Rrc\n", rc));
625 break;
626 }
627
628 LogFunc(("Rebased to: %s\n", pszPathAbs));
629
630 /** @todo Add sparse file support based on fFlags? (Use Open(..., fFlags | SPARSE). */
631 rc = pCtx->mURI.objURI.OpenEx(pszPathAbs, DnDURIObject::File, DnDURIObject::Target,
632 RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE,
633 (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR);
634 if (RT_SUCCESS(rc))
635 {
636 /* Note: Protocol v1 does not send any file sizes, so always 0. */
637 if (mDataBase.mProtocolVersion >= 2)
638 rc = pCtx->mURI.objURI.SetSize(cbSize);
639
640 /** @todo Unescpae path before printing. */
641 LogRel2(("DnD: Transferring guest file to host: %s (%RU64 bytes, mode 0x%x)\n",
642 pCtx->mURI.objURI.GetDestPath().c_str(), pCtx->mURI.objURI.GetSize(), pCtx->mURI.objURI.GetMode()));
643
644 /** @todo Set progress object title to current file being transferred? */
645
646 if (!cbSize) /* 0-byte file? Close again. */
647 pCtx->mURI.objURI.Close();
648 }
649 else
650 {
651 LogRel2(("DnD: Error opening/creating guest file '%s' on host, rc=%Rrc\n",
652 pCtx->mURI.objURI.GetDestPath().c_str(), rc));
653 break;
654 }
655
656 } while (0);
657
658 LogFlowFuncLeaveRC(rc);
659 return rc;
660}
661
662int GuestDnDSource::i_onReceiveFileData(PRECVDATACTX pCtx, const void *pvData, uint32_t cbData)
663{
664 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
665 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
666 AssertReturn(cbData, VERR_INVALID_PARAMETER);
667
668 int rc = VINF_SUCCESS;
669
670 do
671 {
672 if (pCtx->mURI.objURI.IsComplete())
673 {
674 LogFlowFunc(("Warning: Object '%s' already completed\n", pCtx->mURI.objURI.GetDestPath().c_str()));
675 rc = VERR_WRONG_ORDER;
676 break;
677 }
678
679 if (!pCtx->mURI.objURI.IsOpen()) /* File opened on host? */
680 {
681 LogFlowFunc(("Warning: Object '%s' not opened\n", pCtx->mURI.objURI.GetDestPath().c_str()));
682 rc = VERR_WRONG_ORDER;
683 break;
684 }
685
686 uint32_t cbWritten;
687 rc = pCtx->mURI.objURI.Write(pvData, cbData, &cbWritten);
688 if (RT_SUCCESS(rc))
689 {
690 Assert(cbWritten <= cbData);
691 if (cbWritten < cbData)
692 {
693 /** @todo What to do when the host's disk is full? */
694 rc = VERR_DISK_FULL;
695 }
696
697 if (RT_SUCCESS(rc))
698 rc = i_updateProcess(pCtx, cbWritten);
699 }
700
701 if (RT_SUCCESS(rc))
702 {
703 if (pCtx->mURI.objURI.IsComplete())
704 {
705 /** @todo Sanitize path. */
706 LogRel2(("DnD: File transfer to host complete: %s\n", pCtx->mURI.objURI.GetDestPath().c_str()));
707 rc = VINF_EOF;
708
709 /* Prepare URI object for next use. */
710 pCtx->mURI.objURI.Reset();
711 }
712 }
713 else
714 {
715 /** @todo What to do when the host's disk is full? */
716 LogRel(("DnD: Error writing guest file to host to '%s': %Rrc\n", pCtx->mURI.objURI.GetDestPath().c_str(), rc));
717 }
718
719 } while (0);
720
721 LogFlowFuncLeaveRC(rc);
722 return rc;
723}
724#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
725
726int GuestDnDSource::i_receiveData(PRECVDATACTX pCtx, RTMSINTERVAL msTimeout)
727{
728 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
729
730 GuestDnD *pInst = GuestDnDInst();
731 if (!pInst)
732 return VERR_INVALID_POINTER;
733
734 GuestDnDResponse *pResp = pCtx->mpResp;
735 AssertPtr(pCtx->mpResp);
736
737 /* Is this context already in receiving state? */
738 if (ASMAtomicReadBool(&pCtx->mIsActive))
739 return VERR_WRONG_ORDER;
740
741 ASMAtomicWriteBool(&pCtx->mIsActive, true);
742
743 int rc = pCtx->mCallback.Reset();
744 if (RT_FAILURE(rc))
745 return rc;
746
747 /*
748 * Reset any old data.
749 */
750 pCtx->mData.Reset();
751 pCtx->mURI.Reset();
752
753 /* Set the format we are going to retrieve to have it around
754 * when retrieving the data later. */
755 pResp->reset();
756 pResp->setFormat(pCtx->mFormat);
757
758 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFormat.c_str(), pCtx->mFormat.length());
759 LogFlowFunc(("strFormat=%s, uAction=0x%x, fHasURIList=%RTbool\n", pCtx->mFormat.c_str(), pCtx->mAction, fHasURIList));
760 if (fHasURIList)
761 {
762 rc = i_receiveURIData(pCtx, msTimeout);
763 }
764 else
765 {
766 rc = i_receiveRawData(pCtx, msTimeout);
767 }
768
769 ASMAtomicWriteBool(&pCtx->mIsActive, false);
770
771 LogFlowFuncLeaveRC(rc);
772 return rc;
773}
774
775/* static */
776DECLCALLBACK(int) GuestDnDSource::i_receiveDataThread(RTTHREAD Thread, void *pvUser)
777{
778 LogFlowFunc(("pvUser=%p\n", pvUser));
779
780 RecvDataTask *pTask = (RecvDataTask *)pvUser;
781 AssertPtrReturn(pTask, VERR_INVALID_POINTER);
782
783 const ComObjPtr<GuestDnDSource> pSource(pTask->getSource());
784 Assert(!pSource.isNull());
785
786 int rc;
787
788 AutoCaller autoCaller(pSource);
789 if (SUCCEEDED(autoCaller.rc()))
790 {
791 rc = pSource->i_receiveData(pTask->getCtx(), RT_INDEFINITE_WAIT /* msTimeout */);
792 }
793 else
794 rc = VERR_COM_INVALID_OBJECT_STATE;
795
796 ASMAtomicWriteBool(&pSource->mDataBase.mfTransferIsPending, false);
797
798 if (pTask)
799 delete pTask;
800
801 LogFlowFunc(("pSource=%p returning rc=%Rrc\n", (GuestDnDSource *)pSource, rc));
802 return rc;
803}
804
805int GuestDnDSource::i_receiveRawData(PRECVDATACTX pCtx, RTMSINTERVAL msTimeout)
806{
807 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
808
809 int rc;
810
811 GuestDnDResponse *pResp = pCtx->mpResp;
812 AssertPtr(pCtx->mpResp);
813
814 GuestDnD *pInst = GuestDnDInst();
815 if (!pInst)
816 return VERR_INVALID_POINTER;
817
818#define REGISTER_CALLBACK(x) \
819 rc = pResp->setCallback(x, i_receiveRawDataCallback, pCtx); \
820 if (RT_FAILURE(rc)) \
821 return rc;
822
823#define UNREGISTER_CALLBACK(x) \
824 rc = pCtx->mpResp->setCallback(x, NULL); \
825 AssertRC(rc);
826
827 /*
828 * Register callbacks.
829 */
830 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
831 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
832
833 do
834 {
835 /*
836 * Receive the raw data.
837 */
838 GuestDnDMsg Msg;
839 Msg.setType(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED);
840 Msg.setNextPointer((void*)pCtx->mFormat.c_str(), (uint32_t)pCtx->mFormat.length() + 1);
841 Msg.setNextUInt32((uint32_t)pCtx->mFormat.length() + 1);
842 Msg.setNextUInt32(pCtx->mAction);
843
844 /* Make the initial call to the guest by telling that we initiated the "dropped" event on
845 * the host and therefore now waiting for the actual raw data. */
846 rc = pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
847 if (RT_SUCCESS(rc))
848 {
849 rc = waitForEvent(msTimeout, pCtx->mCallback, pCtx->mpResp);
850 if (RT_FAILURE(rc))
851 {
852 if (rc == VERR_CANCELLED)
853 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED, VINF_SUCCESS);
854 else if (rc != VERR_GSTDND_GUEST_ERROR) /* Guest-side error are already handled in the callback. */
855 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, rc,
856 GuestDnDSource::i_hostErrorToString(rc));
857 }
858 else
859 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_COMPLETE, VINF_SUCCESS);
860 }
861
862 } while (0);
863
864 /*
865 * Unregister callbacks.
866 */
867 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
868 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
869
870#undef REGISTER_CALLBACK
871#undef UNREGISTER_CALLBACK
872
873 if (rc == VERR_CANCELLED)
874 {
875 int rc2 = sendCancel();
876 AssertRC(rc2);
877 }
878
879 LogFlowFuncLeaveRC(rc);
880 return rc;
881}
882
883int GuestDnDSource::i_receiveURIData(PRECVDATACTX pCtx, RTMSINTERVAL msTimeout)
884{
885 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
886
887 int rc;
888
889 GuestDnDResponse *pResp = pCtx->mpResp;
890 AssertPtr(pCtx->mpResp);
891
892 GuestDnD *pInst = GuestDnDInst();
893 if (!pInst)
894 return VERR_INVALID_POINTER;
895
896#define REGISTER_CALLBACK(x) \
897 rc = pResp->setCallback(x, i_receiveURIDataCallback, pCtx); \
898 if (RT_FAILURE(rc)) \
899 return rc;
900
901#define UNREGISTER_CALLBACK(x) \
902 { \
903 int rc2 = pResp->setCallback(x, NULL); \
904 AssertRC(rc2); \
905 }
906
907 /*
908 * Register callbacks.
909 */
910 /* Guest callbacks. */
911 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
912 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
913 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DIR);
914 if (mDataBase.mProtocolVersion >= 2)
915 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR);
916 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA);
917
918 do
919 {
920 rc = DnDDirDroppedFilesCreateAndOpenTemp(&pCtx->mURI.mDropDir);
921 if (RT_FAILURE(rc))
922 break;
923 LogFlowFunc(("rc=%Rrc, strDropDir=%s\n", rc, DnDDirDroppedFilesGetDirAbs(&pCtx->mURI.mDropDir)));
924 if (RT_FAILURE(rc))
925 break;
926
927 /*
928 * Receive the URI list.
929 */
930 GuestDnDMsg Msg;
931 Msg.setType(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED);
932 Msg.setNextPointer((void*)pCtx->mFormat.c_str(), (uint32_t)pCtx->mFormat.length() + 1);
933 Msg.setNextUInt32((uint32_t)pCtx->mFormat.length() + 1);
934 Msg.setNextUInt32(pCtx->mAction);
935
936 /* Make the initial call to the guest by telling that we initiated the "dropped" event on
937 * the host and therefore now waiting for the actual URI data. */
938 rc = pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
939 if (RT_SUCCESS(rc))
940 {
941 rc = waitForEvent(msTimeout, pCtx->mCallback, pCtx->mpResp);
942 if (RT_FAILURE(rc))
943 {
944 if (rc == VERR_CANCELLED)
945 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED, VINF_SUCCESS);
946 else if (rc != VERR_GSTDND_GUEST_ERROR) /* Guest-side error are already handled in the callback. */
947 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, rc,
948 GuestDnDSource::i_hostErrorToString(rc));
949 }
950 else
951 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_COMPLETE, VINF_SUCCESS);
952 }
953
954 } while (0);
955
956 /*
957 * Unregister callbacks.
958 */
959 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
960 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
961 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DIR);
962 if (mDataBase.mProtocolVersion >= 2)
963 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR);
964 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA);
965
966#undef REGISTER_CALLBACK
967#undef UNREGISTER_CALLBACK
968
969 int rc2;
970
971 if (rc == VERR_CANCELLED)
972 {
973 rc2 = sendCancel();
974 AssertRC(rc2);
975 }
976
977 if (RT_FAILURE(rc))
978 {
979 rc2 = DnDDirDroppedFilesRollback(&pCtx->mURI.mDropDir); /** @todo Inform user on rollback failure? */
980 LogFlowFunc(("Rolling back ended with rc=%Rrc\n", rc2));
981 }
982
983 rc2 = DnDDirDroppedFilesClose(&pCtx->mURI.mDropDir, RT_FAILURE(rc) ? true : false /* fRemove */);
984 if (RT_SUCCESS(rc))
985 rc = rc2;
986
987 LogFlowFuncLeaveRC(rc);
988 return rc;
989}
990
991/* static */
992DECLCALLBACK(int) GuestDnDSource::i_receiveRawDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
993{
994 PRECVDATACTX pCtx = (PRECVDATACTX)pvUser;
995 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
996
997 GuestDnDSource *pThis = pCtx->mpSource;
998 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
999
1000 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
1001
1002 int rc = VINF_SUCCESS;
1003
1004 int rcCallback = VINF_SUCCESS; /* rc for the callback. */
1005 bool fNotify = false;
1006
1007 switch (uMsg)
1008 {
1009#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1010 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
1011 {
1012 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
1013 AssertPtr(pCBData);
1014 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1015 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1016
1017 rc = pThis->i_onReceiveData(pCtx, pCBData->pvData, pCBData->cbData, pCBData->cbTotalSize);
1018 break;
1019 }
1020 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
1021 {
1022 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
1023 AssertPtr(pCBData);
1024 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
1025 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1026
1027 pCtx->mpResp->reset();
1028
1029 if (RT_SUCCESS(pCBData->rc))
1030 pCBData->rc = VERR_GENERAL_FAILURE; /* Make sure some error is set. */
1031
1032 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc,
1033 GuestDnDSource::i_guestErrorToString(pCBData->rc));
1034 if (RT_SUCCESS(rc))
1035 rcCallback = VERR_GSTDND_GUEST_ERROR;
1036 break;
1037 }
1038#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1039 default:
1040 rc = VERR_NOT_SUPPORTED;
1041 break;
1042 }
1043
1044 if (RT_FAILURE(rc))
1045 {
1046 int rc2 = pCtx->mCallback.Notify(rc);
1047 AssertRC(rc2);
1048 }
1049
1050 LogFlowFuncLeaveRC(rc);
1051 return rc; /* Tell the guest. */
1052}
1053
1054/* static */
1055DECLCALLBACK(int) GuestDnDSource::i_receiveURIDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
1056{
1057 PRECVDATACTX pCtx = (PRECVDATACTX)pvUser;
1058 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1059
1060 GuestDnDSource *pThis = pCtx->mpSource;
1061 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1062
1063 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
1064
1065 int rc = VINF_SUCCESS;
1066
1067 int rcCallback = VINF_SUCCESS; /* rc for the callback. */
1068 bool fNotify = false;
1069
1070 switch (uMsg)
1071 {
1072#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1073 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
1074 {
1075 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
1076 AssertPtr(pCBData);
1077 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1078 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1079
1080 rc = pThis->i_onReceiveData(pCtx, pCBData->pvData, pCBData->cbData, pCBData->cbTotalSize);
1081 break;
1082 }
1083 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
1084 {
1085 DragAndDropSvc::PVBOXDNDCBSNDDIRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDIRDATA>(pvParms);
1086 AssertPtr(pCBData);
1087 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDIRDATA) == cbParms, VERR_INVALID_PARAMETER);
1088 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1089
1090 rc = pThis->i_onReceiveDir(pCtx, pCBData->pszPath, pCBData->cbPath, pCBData->fMode);
1091 break;
1092 }
1093 case DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR:
1094 {
1095 DragAndDropSvc::PVBOXDNDCBSNDFILEHDRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEHDRDATA>(pvParms);
1096 AssertPtr(pCBData);
1097 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEHDRDATA) == cbParms, VERR_INVALID_PARAMETER);
1098 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE_HDR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1099
1100 rc = pThis->i_onReceiveFileHdr(pCtx, pCBData->pszFilePath, pCBData->cbFilePath,
1101 pCBData->cbSize, pCBData->fMode, pCBData->fFlags);
1102 break;
1103 }
1104 case DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA:
1105 {
1106 DragAndDropSvc::PVBOXDNDCBSNDFILEDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEDATADATA>(pvParms);
1107 AssertPtr(pCBData);
1108 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1109 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1110
1111 if (pThis->mDataBase.mProtocolVersion <= 1)
1112 {
1113 /**
1114 * Notes for protocol v1 (< VBox 5.0):
1115 * - Every time this command is being sent it includes the file header,
1116 * so just process both calls here.
1117 * - There was no information whatsoever about the total file size; the old code only
1118 * appended data to the desired file. So just pass 0 as cbSize.
1119 */
1120 rc = pThis->i_onReceiveFileHdr(pCtx,
1121 pCBData->u.v1.pszFilePath, pCBData->u.v1.cbFilePath,
1122 0 /* cbSize */, pCBData->u.v1.fMode, 0 /* fFlags */);
1123 if (RT_SUCCESS(rc))
1124 rc = pThis->i_onReceiveFileData(pCtx, pCBData->pvData, pCBData->cbData);
1125 }
1126 else /* Protocol v2 and up. */
1127 rc = pThis->i_onReceiveFileData(pCtx, pCBData->pvData, pCBData->cbData);
1128 break;
1129 }
1130 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
1131 {
1132 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
1133 AssertPtr(pCBData);
1134 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
1135 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1136
1137 pCtx->mpResp->reset();
1138 if (RT_SUCCESS(pCBData->rc))
1139 pCBData->rc = VERR_GENERAL_FAILURE; /* Make sure some error is set. */
1140 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc);
1141 if (RT_SUCCESS(rc))
1142 rcCallback = pCBData->rc;
1143 break;
1144 }
1145#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1146 default:
1147 rc = VERR_NOT_SUPPORTED;
1148 break;
1149 }
1150
1151 if ( RT_FAILURE(rc)
1152 || RT_FAILURE(rcCallback))
1153 {
1154 fNotify = true;
1155 if (RT_SUCCESS(rcCallback))
1156 rcCallback = rc;
1157 }
1158
1159 if (RT_FAILURE(rc))
1160 {
1161 switch (rc)
1162 {
1163 case VERR_NO_DATA:
1164 LogRel2(("DnD: Transfer to host complete\n"));
1165 break;
1166
1167 case VERR_CANCELLED:
1168 LogRel2(("DnD: Transfer to host canceled\n"));
1169 break;
1170
1171 default:
1172 LogRel(("DnD: Error %Rrc occurred, aborting transfer to host\n", rc));
1173 break;
1174 }
1175
1176 /* Unregister this callback. */
1177 AssertPtr(pCtx->mpResp);
1178 int rc2 = pCtx->mpResp->setCallback(uMsg, NULL /* PFNGUESTDNDCALLBACK */);
1179 AssertRC(rc2);
1180 }
1181
1182 /* All URI data processed? */
1183 if (pCtx->mData.cbProcessed >= pCtx->mData.cbToProcess)
1184 {
1185 Assert(pCtx->mData.cbProcessed == pCtx->mData.cbToProcess);
1186 fNotify = true;
1187 }
1188
1189 LogFlowFunc(("cbProcessed=%RU64, cbToProcess=%RU64, fNotify=%RTbool, rcCallback=%Rrc, rc=%Rrc\n",
1190 pCtx->mData.cbProcessed, pCtx->mData.cbToProcess, fNotify, rcCallback, rc));
1191
1192 if (fNotify)
1193 {
1194 int rc2 = pCtx->mCallback.Notify(rcCallback);
1195 AssertRC(rc2);
1196 }
1197
1198 LogFlowFuncLeaveRC(rc);
1199 return rc; /* Tell the guest. */
1200}
1201
1202int GuestDnDSource::i_updateProcess(PRECVDATACTX pCtx, uint64_t cbDataAdd)
1203{
1204 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1205
1206 LogFlowFunc(("cbProcessed=%RU64 (+ %RU64 = %RU64), cbToProcess=%RU64\n",
1207 pCtx->mData.cbProcessed, cbDataAdd, pCtx->mData.cbProcessed + cbDataAdd, pCtx->mData.cbToProcess));
1208
1209 pCtx->mData.cbProcessed += cbDataAdd;
1210 Assert(pCtx->mData.cbProcessed <= pCtx->mData.cbToProcess);
1211
1212 int64_t cbTotal = pCtx->mData.cbToProcess;
1213 uint8_t uPercent = pCtx->mData.cbProcessed * 100 / (cbTotal ? cbTotal : 1);
1214
1215 int rc = pCtx->mpResp->setProgress(uPercent,
1216 uPercent >= 100
1217 ? DragAndDropSvc::DND_PROGRESS_COMPLETE
1218 : DragAndDropSvc::DND_PROGRESS_RUNNING);
1219 return rc;
1220}
1221
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