VirtualBox

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

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

Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.7 KB
Line 
1/* $Id: GuestDnDSourceImpl.cpp 56655 2015-06-26 08:47:05Z vboxsync $ */
2/** @file
3 * VBox Console COM Class implementation - Guest drag and drop source.
4 */
5
6/*
7 * Copyright (C) 2014-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include "GuestImpl.h"
23#include "GuestDnDSourceImpl.h"
24#include "GuestDnDPrivate.h"
25#include "ConsoleImpl.h"
26
27#include "Global.h"
28#include "AutoCaller.h"
29
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(5000 /* Timeout in ms */) == 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_vecFmtSup, pResp->fmtReq(), 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 case VERR_TIMEOUT:
443 strError += Utf8StrFmt(tr("The guest was not able to retrieve the drag and drop data within time"));
444 break;
445
446 default:
447 strError += Utf8StrFmt(tr("Drag and drop error from guest (%Rrc)"), guestRc);
448 break;
449 }
450
451 return strError;
452}
453
454/* static */
455Utf8Str GuestDnDSource::i_hostErrorToString(int hostRc)
456{
457 Utf8Str strError;
458
459 switch (hostRc)
460 {
461 case VERR_ACCESS_DENIED:
462 strError += Utf8StrFmt(tr("For one or more host files or directories selected for transferring to the guest your host "
463 "user does not have the appropriate access rights for. Please make sure that all selected "
464 "elements can be accessed and that your host user has the appropriate rights."));
465 break;
466
467 case VERR_NOT_FOUND:
468 /* Should not happen due to file locking on the host, but anyway ... */
469 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the host were not"
470 "found on the host anymore. This can be the case if the host files were moved and/or"
471 "altered while the drag and drop operation was in progress."));
472 break;
473
474 case VERR_SHARING_VIOLATION:
475 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the guest were locked. "
476 "Please make sure that all selected elements can be accessed and that your host user has "
477 "the appropriate rights."));
478 break;
479
480 default:
481 strError += Utf8StrFmt(tr("Drag and drop error from host (%Rrc)"), hostRc);
482 break;
483 }
484
485 return strError;
486}
487
488#ifdef VBOX_WITH_DRAG_AND_DROP_GH
489int GuestDnDSource::i_onReceiveData(PRECVDATACTX pCtx, const void *pvData, uint32_t cbData, uint64_t cbTotalSize)
490{
491 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
492 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
493 AssertReturn(cbData, VERR_INVALID_PARAMETER);
494 AssertReturn(cbTotalSize, VERR_INVALID_PARAMETER);
495
496 LogFlowFunc(("cbData=%RU32, cbTotalSize=%RU64\n", cbData, cbTotalSize));
497
498 int rc = VINF_SUCCESS;
499
500 try
501 {
502 if ( cbData > cbTotalSize
503 || cbData > mData.mcbBlockSize)
504 {
505 LogFlowFunc(("Data sizes invalid: cbData=%RU32, cbTotalSize=%RU64\n", cbData, cbTotalSize));
506 rc = VERR_INVALID_PARAMETER;
507 }
508 else if (cbData < pCtx->mData.vecData.size())
509 {
510 AssertMsgFailed(("New size (%RU64) is smaller than current size (%zu)\n", cbTotalSize, pCtx->mData.vecData.size()));
511 rc = VERR_INVALID_PARAMETER;
512 }
513
514 if (RT_SUCCESS(rc))
515 {
516 pCtx->mData.vecData.insert(pCtx->mData.vecData.begin(), (BYTE *)pvData, (BYTE *)pvData + cbData);
517
518 LogFlowFunc(("vecDataSize=%zu, cbData=%RU32, cbTotalSize=%RU64\n", pCtx->mData.vecData.size(), cbData, cbTotalSize));
519
520 /* Data transfer complete? */
521 Assert(cbData <= pCtx->mData.vecData.size());
522 if (cbData == pCtx->mData.vecData.size())
523 {
524 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFormat.c_str(), pCtx->mFormat.length());
525 LogFlowFunc(("fHasURIList=%RTbool, cbTotalSize=%RU32\n", fHasURIList, cbTotalSize));
526 if (fHasURIList)
527 {
528 /* Try parsing the data as URI list. */
529 rc = pCtx->mURI.lstURI.RootFromURIData(&pCtx->mData.vecData[0], pCtx->mData.vecData.size(), 0 /* uFlags */);
530 if (RT_SUCCESS(rc))
531 {
532 /* Reset processed bytes. */
533 pCtx->mData.cbProcessed = 0;
534
535 /*
536 * Assign new total size which also includes all file data to receive
537 * from the guest.
538 */
539 pCtx->mData.cbToProcess = cbTotalSize;
540
541 /* Update our process with the data we already received.
542 * Note: The total size will consist of the meta data (in vecData) and
543 * the actual accumulated file/directory data from the guest. */
544 rc = i_updateProcess(pCtx, (uint64_t)pCtx->mData.vecData.size());
545
546 LogFlowFunc(("URI data => cbProcessed=%RU64, cbToProcess=%RU64, rc=%Rrc\n",
547 pCtx->mData.cbProcessed, pCtx->mData.cbToProcess, rc));
548 }
549 }
550 }
551 }
552 }
553 catch (std::bad_alloc &)
554 {
555 rc = VERR_NO_MEMORY;
556 }
557
558 LogFlowFuncLeaveRC(rc);
559 return rc;
560}
561
562int GuestDnDSource::i_onReceiveDir(PRECVDATACTX pCtx, const char *pszPath, uint32_t cbPath, uint32_t fMode)
563{
564 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
565 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
566 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
567
568 LogFlowFunc(("pszPath=%s, cbPath=%RU32, fMode=0x%x\n", pszPath, cbPath, fMode));
569
570 int rc;
571 char *pszDir = RTPathJoinA(DnDDirDroppedFilesGetDirAbs(&pCtx->mURI.mDropDir), pszPath);
572 if (pszDir)
573 {
574 rc = RTDirCreateFullPath(pszDir, fMode);
575 if (RT_FAILURE(rc))
576 LogRel2(("DnD: Error creating guest directory '%s' on the host, rc=%Rrc\n", pszDir, rc));
577
578 RTStrFree(pszDir);
579 }
580 else
581 rc = VERR_NO_MEMORY;
582
583 LogFlowFuncLeaveRC(rc);
584 return rc;
585}
586
587int GuestDnDSource::i_onReceiveFileHdr(PRECVDATACTX pCtx, const char *pszPath, uint32_t cbPath,
588 uint64_t cbSize, uint32_t fMode, uint32_t fFlags)
589{
590 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
591 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
592 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
593 AssertReturn(fMode, VERR_INVALID_PARAMETER);
594 /* fFlags are optional. */
595
596 LogFlowFunc(("pszPath=%s, cbPath=%RU32, cbSize=%RU64, fMode=0x%x, fFlags=0x%x\n", pszPath, cbPath, cbSize, fMode, fFlags));
597
598 int rc = VINF_SUCCESS;
599
600 do
601 {
602 if ( pCtx->mURI.objURI.IsOpen()
603 && !pCtx->mURI.objURI.IsComplete())
604 {
605 LogFlowFunc(("Warning: Object '%s' not complete yet\n", pCtx->mURI.objURI.GetDestPath().c_str()));
606 rc = VERR_INVALID_PARAMETER;
607 break;
608 }
609
610 if (pCtx->mURI.objURI.IsOpen()) /* File already opened? */
611 {
612 LogFlowFunc(("Warning: Current opened object is '%s'\n", pCtx->mURI.objURI.GetDestPath().c_str()));
613 rc = VERR_WRONG_ORDER;
614 break;
615 }
616
617 char pszPathAbs[RTPATH_MAX];
618 rc = RTPathJoin(pszPathAbs, sizeof(pszPathAbs), DnDDirDroppedFilesGetDirAbs(&pCtx->mURI.mDropDir), pszPath);
619 if (RT_FAILURE(rc))
620 {
621 LogFlowFunc(("Warning: Rebasing current file failed with rc=%Rrc\n", rc));
622 break;
623 }
624
625 rc = DnDPathSanitize(pszPathAbs, sizeof(pszPathAbs));
626 if (RT_FAILURE(rc))
627 {
628 LogFlowFunc(("Warning: Rebasing current file failed with rc=%Rrc\n", rc));
629 break;
630 }
631
632 LogFunc(("Rebased to: %s\n", pszPathAbs));
633
634 /** @todo Add sparse file support based on fFlags? (Use Open(..., fFlags | SPARSE). */
635 rc = pCtx->mURI.objURI.OpenEx(pszPathAbs, DnDURIObject::File, DnDURIObject::Target,
636 RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE,
637 (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR);
638 if (RT_SUCCESS(rc))
639 {
640 /* Note: Protocol v1 does not send any file sizes, so always 0. */
641 if (mDataBase.mProtocolVersion >= 2)
642 rc = pCtx->mURI.objURI.SetSize(cbSize);
643
644 /** @todo Unescpae path before printing. */
645 LogRel2(("DnD: Transferring guest file to host: %s (%RU64 bytes, mode 0x%x)\n",
646 pCtx->mURI.objURI.GetDestPath().c_str(), pCtx->mURI.objURI.GetSize(), pCtx->mURI.objURI.GetMode()));
647
648 /** @todo Set progress object title to current file being transferred? */
649
650 if (!cbSize) /* 0-byte file? Close again. */
651 pCtx->mURI.objURI.Close();
652 }
653 else
654 {
655 LogRel2(("DnD: Error opening/creating guest file '%s' on host, rc=%Rrc\n",
656 pCtx->mURI.objURI.GetDestPath().c_str(), rc));
657 break;
658 }
659
660 } while (0);
661
662 LogFlowFuncLeaveRC(rc);
663 return rc;
664}
665
666int GuestDnDSource::i_onReceiveFileData(PRECVDATACTX pCtx, const void *pvData, uint32_t cbData)
667{
668 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
669 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
670 AssertReturn(cbData, VERR_INVALID_PARAMETER);
671
672 int rc = VINF_SUCCESS;
673
674 do
675 {
676 if (pCtx->mURI.objURI.IsComplete())
677 {
678 LogFlowFunc(("Warning: Object '%s' already completed\n", pCtx->mURI.objURI.GetDestPath().c_str()));
679 rc = VERR_WRONG_ORDER;
680 break;
681 }
682
683 if (!pCtx->mURI.objURI.IsOpen()) /* File opened on host? */
684 {
685 LogFlowFunc(("Warning: Object '%s' not opened\n", pCtx->mURI.objURI.GetDestPath().c_str()));
686 rc = VERR_WRONG_ORDER;
687 break;
688 }
689
690 uint32_t cbWritten;
691 rc = pCtx->mURI.objURI.Write(pvData, cbData, &cbWritten);
692 if (RT_SUCCESS(rc))
693 {
694 Assert(cbWritten <= cbData);
695 if (cbWritten < cbData)
696 {
697 /** @todo What to do when the host's disk is full? */
698 rc = VERR_DISK_FULL;
699 }
700
701 if (RT_SUCCESS(rc))
702 rc = i_updateProcess(pCtx, cbWritten);
703 }
704
705 if (RT_SUCCESS(rc))
706 {
707 if (pCtx->mURI.objURI.IsComplete())
708 {
709 /** @todo Sanitize path. */
710 LogRel2(("DnD: File transfer to host complete: %s\n", pCtx->mURI.objURI.GetDestPath().c_str()));
711 rc = VINF_EOF;
712
713 /* Prepare URI object for next use. */
714 pCtx->mURI.objURI.Reset();
715 }
716 }
717 else
718 {
719 /** @todo What to do when the host's disk is full? */
720 LogRel(("DnD: Error writing guest file to host to '%s': %Rrc\n", pCtx->mURI.objURI.GetDestPath().c_str(), rc));
721 }
722
723 } while (0);
724
725 LogFlowFuncLeaveRC(rc);
726 return rc;
727}
728#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
729
730int GuestDnDSource::i_receiveData(PRECVDATACTX pCtx, RTMSINTERVAL msTimeout)
731{
732 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
733
734 GuestDnD *pInst = GuestDnDInst();
735 if (!pInst)
736 return VERR_INVALID_POINTER;
737
738 GuestDnDResponse *pResp = pCtx->mpResp;
739 AssertPtr(pCtx->mpResp);
740
741 /* Is this context already in receiving state? */
742 if (ASMAtomicReadBool(&pCtx->mIsActive))
743 return VERR_WRONG_ORDER;
744
745 ASMAtomicWriteBool(&pCtx->mIsActive, true);
746
747 int rc = pCtx->mCallback.Reset();
748 if (RT_FAILURE(rc))
749 return rc;
750
751 /*
752 * Reset any old data.
753 */
754 pCtx->mData.Reset();
755 pCtx->mURI.Reset();
756
757 /* Set the format we are going to retrieve to have it around
758 * when retrieving the data later. */
759 pResp->reset();
760 pResp->setFmtReq(pCtx->mFormat);
761
762 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFormat.c_str(), pCtx->mFormat.length());
763 LogFlowFunc(("strFormat=%s, uAction=0x%x, fHasURIList=%RTbool\n", pCtx->mFormat.c_str(), pCtx->mAction, fHasURIList));
764 if (fHasURIList)
765 {
766 rc = i_receiveURIData(pCtx, msTimeout);
767 }
768 else
769 {
770 rc = i_receiveRawData(pCtx, msTimeout);
771 }
772
773 ASMAtomicWriteBool(&pCtx->mIsActive, false);
774
775 LogFlowFuncLeaveRC(rc);
776 return rc;
777}
778
779/* static */
780DECLCALLBACK(int) GuestDnDSource::i_receiveDataThread(RTTHREAD Thread, void *pvUser)
781{
782 LogFlowFunc(("pvUser=%p\n", pvUser));
783
784 RecvDataTask *pTask = (RecvDataTask *)pvUser;
785 AssertPtrReturn(pTask, VERR_INVALID_POINTER);
786
787 const ComObjPtr<GuestDnDSource> pSource(pTask->getSource());
788 Assert(!pSource.isNull());
789
790 int rc;
791
792 AutoCaller autoCaller(pSource);
793 if (SUCCEEDED(autoCaller.rc()))
794 {
795 rc = pSource->i_receiveData(pTask->getCtx(), RT_INDEFINITE_WAIT /* msTimeout */);
796 }
797 else
798 rc = VERR_COM_INVALID_OBJECT_STATE;
799
800 ASMAtomicWriteBool(&pSource->mDataBase.mfTransferIsPending, false);
801
802 if (pTask)
803 delete pTask;
804
805 LogFlowFunc(("pSource=%p returning rc=%Rrc\n", (GuestDnDSource *)pSource, rc));
806 return rc;
807}
808
809int GuestDnDSource::i_receiveRawData(PRECVDATACTX pCtx, RTMSINTERVAL msTimeout)
810{
811 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
812
813 int rc;
814
815 GuestDnDResponse *pResp = pCtx->mpResp;
816 AssertPtr(pCtx->mpResp);
817
818 GuestDnD *pInst = GuestDnDInst();
819 if (!pInst)
820 return VERR_INVALID_POINTER;
821
822#define REGISTER_CALLBACK(x) \
823 rc = pResp->setCallback(x, i_receiveRawDataCallback, pCtx); \
824 if (RT_FAILURE(rc)) \
825 return rc;
826
827#define UNREGISTER_CALLBACK(x) \
828 rc = pCtx->mpResp->setCallback(x, NULL); \
829 AssertRC(rc);
830
831 /*
832 * Register callbacks.
833 */
834 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
835 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
836
837 do
838 {
839 /*
840 * Receive the raw data.
841 */
842 GuestDnDMsg Msg;
843 Msg.setType(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED);
844 Msg.setNextPointer((void*)pCtx->mFormat.c_str(), (uint32_t)pCtx->mFormat.length() + 1);
845 Msg.setNextUInt32((uint32_t)pCtx->mFormat.length() + 1);
846 Msg.setNextUInt32(pCtx->mAction);
847
848 /* Make the initial call to the guest by telling that we initiated the "dropped" event on
849 * the host and therefore now waiting for the actual raw data. */
850 rc = pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
851 if (RT_SUCCESS(rc))
852 {
853 rc = waitForEvent(msTimeout, pCtx->mCallback, pCtx->mpResp);
854 if (RT_SUCCESS(rc))
855 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_COMPLETE, VINF_SUCCESS);
856 }
857
858 } while (0);
859
860 /*
861 * Unregister callbacks.
862 */
863 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
864 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
865
866#undef REGISTER_CALLBACK
867#undef UNREGISTER_CALLBACK
868
869 if (RT_FAILURE(rc))
870 {
871 if (rc == VERR_CANCELLED)
872 {
873 int rc2 = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED, VINF_SUCCESS);
874 AssertRC(rc2);
875
876 rc2 = sendCancel();
877 AssertRC(rc2);
878 }
879 else if (rc != VERR_GSTDND_GUEST_ERROR) /* Guest-side error are already handled in the callback. */
880 {
881 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR,
882 rc, GuestDnDSource::i_hostErrorToString(rc));
883 }
884 }
885
886 LogFlowFuncLeaveRC(rc);
887 return rc;
888}
889
890int GuestDnDSource::i_receiveURIData(PRECVDATACTX pCtx, RTMSINTERVAL msTimeout)
891{
892 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
893
894 int rc;
895
896 GuestDnDResponse *pResp = pCtx->mpResp;
897 AssertPtr(pCtx->mpResp);
898
899 GuestDnD *pInst = GuestDnDInst();
900 if (!pInst)
901 return VERR_INVALID_POINTER;
902
903#define REGISTER_CALLBACK(x) \
904 rc = pResp->setCallback(x, i_receiveURIDataCallback, pCtx); \
905 if (RT_FAILURE(rc)) \
906 return rc;
907
908#define UNREGISTER_CALLBACK(x) \
909 { \
910 int rc2 = pResp->setCallback(x, NULL); \
911 AssertRC(rc2); \
912 }
913
914 /*
915 * Register callbacks.
916 */
917 /* Guest callbacks. */
918 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
919 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
920 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DIR);
921 if (mDataBase.mProtocolVersion >= 2)
922 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR);
923 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA);
924
925 do
926 {
927 rc = DnDDirDroppedFilesCreateAndOpenTemp(&pCtx->mURI.mDropDir);
928 if (RT_FAILURE(rc))
929 break;
930 LogFlowFunc(("rc=%Rrc, strDropDir=%s\n", rc, DnDDirDroppedFilesGetDirAbs(&pCtx->mURI.mDropDir)));
931 if (RT_FAILURE(rc))
932 break;
933
934 /*
935 * Receive the URI list.
936 */
937 GuestDnDMsg Msg;
938 Msg.setType(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED);
939 Msg.setNextPointer((void*)pCtx->mFormat.c_str(), (uint32_t)pCtx->mFormat.length() + 1);
940 Msg.setNextUInt32((uint32_t)pCtx->mFormat.length() + 1);
941 Msg.setNextUInt32(pCtx->mAction);
942
943 /* Make the initial call to the guest by telling that we initiated the "dropped" event on
944 * the host and therefore now waiting for the actual URI data. */
945 rc = pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
946 if (RT_SUCCESS(rc))
947 {
948 LogFlowFunc(("Waiting ...\n"));
949
950 rc = waitForEvent(msTimeout, pCtx->mCallback, pCtx->mpResp);
951 if (RT_SUCCESS(rc))
952 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_COMPLETE, VINF_SUCCESS);
953
954 LogFlowFunc(("Waiting ended with rc=%Rrc\n", rc));
955 }
956
957 } while (0);
958
959 /*
960 * Unregister callbacks.
961 */
962 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
963 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
964 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DIR);
965 if (mDataBase.mProtocolVersion >= 2)
966 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR);
967 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA);
968
969#undef REGISTER_CALLBACK
970#undef UNREGISTER_CALLBACK
971
972 int rc2;
973
974 if (RT_FAILURE(rc))
975 {
976 if (rc == VERR_CANCELLED)
977 {
978 rc2 = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED, VINF_SUCCESS);
979 AssertRC(rc2);
980
981 rc2 = sendCancel();
982 AssertRC(rc2);
983 }
984 else if (rc != VERR_GSTDND_GUEST_ERROR) /* Guest-side error are already handled in the callback. */
985 {
986 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR,
987 rc, GuestDnDSource::i_hostErrorToString(rc));
988 }
989 }
990
991 if (RT_FAILURE(rc))
992 {
993 rc2 = DnDDirDroppedFilesRollback(&pCtx->mURI.mDropDir); /** @todo Inform user on rollback failure? */
994 LogFlowFunc(("Rolling back ended with rc=%Rrc\n", rc2));
995 }
996
997 rc2 = DnDDirDroppedFilesClose(&pCtx->mURI.mDropDir, RT_FAILURE(rc) ? true : false /* fRemove */);
998 if (RT_SUCCESS(rc))
999 rc = rc2;
1000
1001 LogFlowFuncLeaveRC(rc);
1002 return rc;
1003}
1004
1005/* static */
1006DECLCALLBACK(int) GuestDnDSource::i_receiveRawDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
1007{
1008 PRECVDATACTX pCtx = (PRECVDATACTX)pvUser;
1009 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1010
1011 GuestDnDSource *pThis = pCtx->mpSource;
1012 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1013
1014 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
1015
1016 int rc = VINF_SUCCESS;
1017
1018 int rcCallback = VINF_SUCCESS; /* rc for the callback. */
1019 bool fNotify = false;
1020
1021 switch (uMsg)
1022 {
1023#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1024 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
1025 {
1026 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
1027 AssertPtr(pCBData);
1028 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1029 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1030
1031 rc = pThis->i_onReceiveData(pCtx, pCBData->pvData, pCBData->cbData, pCBData->cbTotalSize);
1032 break;
1033 }
1034 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
1035 {
1036 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
1037 AssertPtr(pCBData);
1038 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
1039 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1040
1041 pCtx->mpResp->reset();
1042
1043 if (RT_SUCCESS(pCBData->rc))
1044 pCBData->rc = VERR_GENERAL_FAILURE; /* Make sure some error is set. */
1045
1046 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc,
1047 GuestDnDSource::i_guestErrorToString(pCBData->rc));
1048 if (RT_SUCCESS(rc))
1049 rcCallback = VERR_GSTDND_GUEST_ERROR;
1050 break;
1051 }
1052#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1053 default:
1054 rc = VERR_NOT_SUPPORTED;
1055 break;
1056 }
1057
1058 if (RT_FAILURE(rc))
1059 {
1060 int rc2 = pCtx->mCallback.Notify(rc);
1061 AssertRC(rc2);
1062 }
1063
1064 LogFlowFuncLeaveRC(rc);
1065 return rc; /* Tell the guest. */
1066}
1067
1068/* static */
1069DECLCALLBACK(int) GuestDnDSource::i_receiveURIDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
1070{
1071 PRECVDATACTX pCtx = (PRECVDATACTX)pvUser;
1072 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1073
1074 GuestDnDSource *pThis = pCtx->mpSource;
1075 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1076
1077 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
1078
1079 int rc = VINF_SUCCESS;
1080
1081 int rcCallback = VINF_SUCCESS; /* rc for the callback. */
1082 bool fNotify = false;
1083
1084 switch (uMsg)
1085 {
1086#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1087 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
1088 {
1089 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
1090 AssertPtr(pCBData);
1091 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1092 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1093
1094 rc = pThis->i_onReceiveData(pCtx, pCBData->pvData, pCBData->cbData, pCBData->cbTotalSize);
1095 break;
1096 }
1097 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
1098 {
1099 DragAndDropSvc::PVBOXDNDCBSNDDIRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDIRDATA>(pvParms);
1100 AssertPtr(pCBData);
1101 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDIRDATA) == cbParms, VERR_INVALID_PARAMETER);
1102 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1103
1104 rc = pThis->i_onReceiveDir(pCtx, pCBData->pszPath, pCBData->cbPath, pCBData->fMode);
1105 break;
1106 }
1107 case DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR:
1108 {
1109 DragAndDropSvc::PVBOXDNDCBSNDFILEHDRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEHDRDATA>(pvParms);
1110 AssertPtr(pCBData);
1111 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEHDRDATA) == cbParms, VERR_INVALID_PARAMETER);
1112 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE_HDR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1113
1114 rc = pThis->i_onReceiveFileHdr(pCtx, pCBData->pszFilePath, pCBData->cbFilePath,
1115 pCBData->cbSize, pCBData->fMode, pCBData->fFlags);
1116 break;
1117 }
1118 case DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA:
1119 {
1120 DragAndDropSvc::PVBOXDNDCBSNDFILEDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEDATADATA>(pvParms);
1121 AssertPtr(pCBData);
1122 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1123 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1124
1125 if (pThis->mDataBase.mProtocolVersion <= 1)
1126 {
1127 /**
1128 * Notes for protocol v1 (< VBox 5.0):
1129 * - Every time this command is being sent it includes the file header,
1130 * so just process both calls here.
1131 * - There was no information whatsoever about the total file size; the old code only
1132 * appended data to the desired file. So just pass 0 as cbSize.
1133 */
1134 rc = pThis->i_onReceiveFileHdr(pCtx,
1135 pCBData->u.v1.pszFilePath, pCBData->u.v1.cbFilePath,
1136 0 /* cbSize */, pCBData->u.v1.fMode, 0 /* fFlags */);
1137 if (RT_SUCCESS(rc))
1138 rc = pThis->i_onReceiveFileData(pCtx, pCBData->pvData, pCBData->cbData);
1139 }
1140 else /* Protocol v2 and up. */
1141 rc = pThis->i_onReceiveFileData(pCtx, pCBData->pvData, pCBData->cbData);
1142 break;
1143 }
1144 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
1145 {
1146 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
1147 AssertPtr(pCBData);
1148 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
1149 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1150
1151 pCtx->mpResp->reset();
1152
1153 if (RT_SUCCESS(pCBData->rc))
1154 pCBData->rc = VERR_GENERAL_FAILURE; /* Make sure some error is set. */
1155
1156 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc,
1157 GuestDnDSource::i_guestErrorToString(pCBData->rc));
1158 if (RT_SUCCESS(rc))
1159 rcCallback = VERR_GSTDND_GUEST_ERROR;
1160 break;
1161 }
1162#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1163 default:
1164 rc = VERR_NOT_SUPPORTED;
1165 break;
1166 }
1167
1168 if ( RT_FAILURE(rc)
1169 || RT_FAILURE(rcCallback))
1170 {
1171 fNotify = true;
1172 if (RT_SUCCESS(rcCallback))
1173 rcCallback = rc;
1174 }
1175
1176 if (RT_FAILURE(rc))
1177 {
1178 switch (rc)
1179 {
1180 case VERR_NO_DATA:
1181 LogRel2(("DnD: Transfer to host complete\n"));
1182 break;
1183
1184 case VERR_CANCELLED:
1185 LogRel2(("DnD: Transfer to host canceled\n"));
1186 break;
1187
1188 default:
1189 LogRel(("DnD: Error %Rrc occurred, aborting transfer to host\n", rc));
1190 break;
1191 }
1192
1193 /* Unregister this callback. */
1194 AssertPtr(pCtx->mpResp);
1195 int rc2 = pCtx->mpResp->setCallback(uMsg, NULL /* PFNGUESTDNDCALLBACK */);
1196 AssertRC(rc2);
1197 }
1198
1199 /* All URI data processed? */
1200 if (pCtx->mData.cbProcessed >= pCtx->mData.cbToProcess)
1201 {
1202 Assert(pCtx->mData.cbProcessed == pCtx->mData.cbToProcess);
1203 fNotify = true;
1204 }
1205
1206 LogFlowFunc(("cbProcessed=%RU64, cbToProcess=%RU64, fNotify=%RTbool, rcCallback=%Rrc, rc=%Rrc\n",
1207 pCtx->mData.cbProcessed, pCtx->mData.cbToProcess, fNotify, rcCallback, rc));
1208
1209 if (fNotify)
1210 {
1211 int rc2 = pCtx->mCallback.Notify(rcCallback);
1212 AssertRC(rc2);
1213 }
1214
1215 LogFlowFuncLeaveRC(rc);
1216 return rc; /* Tell the guest. */
1217}
1218
1219int GuestDnDSource::i_updateProcess(PRECVDATACTX pCtx, uint64_t cbDataAdd)
1220{
1221 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1222
1223 LogFlowFunc(("cbProcessed=%RU64 (+ %RU64 = %RU64), cbToProcess=%RU64\n",
1224 pCtx->mData.cbProcessed, cbDataAdd, pCtx->mData.cbProcessed + cbDataAdd, pCtx->mData.cbToProcess));
1225
1226 pCtx->mData.cbProcessed += cbDataAdd;
1227 Assert(pCtx->mData.cbProcessed <= pCtx->mData.cbToProcess);
1228
1229 int64_t cbTotal = pCtx->mData.cbToProcess;
1230 uint8_t uPercent = pCtx->mData.cbProcessed * 100 / (cbTotal ? cbTotal : 1);
1231
1232 int rc = pCtx->mpResp->setProgress(uPercent,
1233 uPercent >= 100
1234 ? DragAndDropSvc::DND_PROGRESS_COMPLETE
1235 : DragAndDropSvc::DND_PROGRESS_RUNNING);
1236 return rc;
1237}
1238
Note: See TracBrowser for help on using the repository browser.

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