VirtualBox

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

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

Nit.

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