VirtualBox

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

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

DnD/Main: GuestDnDSourceImpl.cpp: Compare incoming and previously announced sizes, logging.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.0 KB
Line 
1/* $Id: GuestDnDSourceImpl.cpp 56907 2015-07-09 18:05:19Z 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 AssertMsgFailed(("Object '%s' not complete yet\n", pCtx->mURI.objURI.GetDestPath().c_str()));
607 rc = VERR_WRONG_ORDER;
608 break;
609 }
610
611 if (pCtx->mURI.objURI.IsOpen()) /* File already opened? */
612 {
613 AssertMsgFailed(("Current opened object is '%s', close this first\n", pCtx->mURI.objURI.GetDestPath().c_str()));
614 rc = VERR_WRONG_ORDER;
615 break;
616 }
617
618 if (cbSize > pCtx->mData.cbToProcess)
619 {
620 AssertMsgFailed(("File size (%RU64) exceeds total size to transfer (%RU64)\n", cbSize, pCtx->mData.cbToProcess));
621 rc = VERR_INVALID_PARAMETER;
622 break;
623 }
624
625 char pszPathAbs[RTPATH_MAX];
626 rc = RTPathJoin(pszPathAbs, sizeof(pszPathAbs), DnDDirDroppedFilesGetDirAbs(&pCtx->mURI.mDropDir), pszPath);
627 if (RT_FAILURE(rc))
628 {
629 LogFlowFunc(("Warning: Rebasing current file failed with rc=%Rrc\n", rc));
630 break;
631 }
632
633 rc = DnDPathSanitize(pszPathAbs, sizeof(pszPathAbs));
634 if (RT_FAILURE(rc))
635 {
636 LogFlowFunc(("Warning: Rebasing current file failed with rc=%Rrc\n", rc));
637 break;
638 }
639
640 LogFunc(("Rebased to: %s\n", pszPathAbs));
641
642 /** @todo Add sparse file support based on fFlags? (Use Open(..., fFlags | SPARSE). */
643 rc = pCtx->mURI.objURI.OpenEx(pszPathAbs, DnDURIObject::File, DnDURIObject::Target,
644 RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE,
645 (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR);
646 if (RT_SUCCESS(rc))
647 {
648 /* Note: Protocol v1 does not send any file sizes, so always 0. */
649 if (mDataBase.mProtocolVersion >= 2)
650 rc = pCtx->mURI.objURI.SetSize(cbSize);
651
652 /** @todo Unescpae path before printing. */
653 LogRel2(("DnD: Transferring guest file to host: %s (%RU64 bytes, mode 0x%x)\n",
654 pCtx->mURI.objURI.GetDestPath().c_str(), pCtx->mURI.objURI.GetSize(), pCtx->mURI.objURI.GetMode()));
655
656 /** @todo Set progress object title to current file being transferred? */
657
658 if (!cbSize) /* 0-byte file? Close again. */
659 pCtx->mURI.objURI.Close();
660 }
661 else
662 {
663 LogRel2(("DnD: Error opening/creating guest file '%s' on host, rc=%Rrc\n",
664 pCtx->mURI.objURI.GetDestPath().c_str(), rc));
665 break;
666 }
667
668 } while (0);
669
670 LogFlowFuncLeaveRC(rc);
671 return rc;
672}
673
674int GuestDnDSource::i_onReceiveFileData(PRECVDATACTX pCtx, const void *pvData, uint32_t cbData)
675{
676 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
677 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
678 AssertReturn(cbData, VERR_INVALID_PARAMETER);
679
680 int rc = VINF_SUCCESS;
681
682 do
683 {
684 if (pCtx->mURI.objURI.IsComplete())
685 {
686 LogFlowFunc(("Warning: Object '%s' already completed\n", pCtx->mURI.objURI.GetDestPath().c_str()));
687 rc = VERR_WRONG_ORDER;
688 break;
689 }
690
691 if (!pCtx->mURI.objURI.IsOpen()) /* File opened on host? */
692 {
693 LogFlowFunc(("Warning: Object '%s' not opened\n", pCtx->mURI.objURI.GetDestPath().c_str()));
694 rc = VERR_WRONG_ORDER;
695 break;
696 }
697
698 uint32_t cbWritten;
699 rc = pCtx->mURI.objURI.Write(pvData, cbData, &cbWritten);
700 if (RT_SUCCESS(rc))
701 {
702 Assert(cbWritten <= cbData);
703 if (cbWritten < cbData)
704 {
705 /** @todo What to do when the host's disk is full? */
706 rc = VERR_DISK_FULL;
707 }
708
709 if (RT_SUCCESS(rc))
710 rc = i_updateProcess(pCtx, cbWritten);
711 }
712
713 if (RT_SUCCESS(rc))
714 {
715 if (pCtx->mURI.objURI.IsComplete())
716 {
717 /** @todo Sanitize path. */
718 LogRel2(("DnD: File transfer to host complete: %s\n", pCtx->mURI.objURI.GetDestPath().c_str()));
719 rc = VINF_EOF;
720
721 /* Prepare URI object for next use. */
722 pCtx->mURI.objURI.Reset();
723 }
724 }
725 else
726 {
727 /** @todo What to do when the host's disk is full? */
728 LogRel(("DnD: Error writing guest file to host to '%s': %Rrc\n", pCtx->mURI.objURI.GetDestPath().c_str(), rc));
729 }
730
731 } while (0);
732
733 LogFlowFuncLeaveRC(rc);
734 return rc;
735}
736#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
737
738int GuestDnDSource::i_receiveData(PRECVDATACTX pCtx, RTMSINTERVAL msTimeout)
739{
740 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
741
742 GuestDnD *pInst = GuestDnDInst();
743 if (!pInst)
744 return VERR_INVALID_POINTER;
745
746 GuestDnDResponse *pResp = pCtx->mpResp;
747 AssertPtr(pCtx->mpResp);
748
749 /* Is this context already in receiving state? */
750 if (ASMAtomicReadBool(&pCtx->mIsActive))
751 return VERR_WRONG_ORDER;
752
753 ASMAtomicWriteBool(&pCtx->mIsActive, true);
754
755 int rc = pCtx->mCallback.Reset();
756 if (RT_FAILURE(rc))
757 return rc;
758
759 /*
760 * Reset any old data.
761 */
762 pCtx->mData.Reset();
763 pCtx->mURI.Reset();
764
765 /* Set the format we are going to retrieve to have it around
766 * when retrieving the data later. */
767 pResp->reset();
768 pResp->setFmtReq(pCtx->mFormat);
769
770 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFormat.c_str(), pCtx->mFormat.length());
771 LogFlowFunc(("strFormat=%s, uAction=0x%x, fHasURIList=%RTbool\n", pCtx->mFormat.c_str(), pCtx->mAction, fHasURIList));
772 if (fHasURIList)
773 {
774 rc = i_receiveURIData(pCtx, msTimeout);
775 }
776 else
777 {
778 rc = i_receiveRawData(pCtx, msTimeout);
779 }
780
781 ASMAtomicWriteBool(&pCtx->mIsActive, false);
782
783 LogFlowFuncLeaveRC(rc);
784 return rc;
785}
786
787/* static */
788DECLCALLBACK(int) GuestDnDSource::i_receiveDataThread(RTTHREAD Thread, void *pvUser)
789{
790 LogFlowFunc(("pvUser=%p\n", pvUser));
791
792 RecvDataTask *pTask = (RecvDataTask *)pvUser;
793 AssertPtrReturn(pTask, VERR_INVALID_POINTER);
794
795 const ComObjPtr<GuestDnDSource> pSource(pTask->getSource());
796 Assert(!pSource.isNull());
797
798 int rc;
799
800 AutoCaller autoCaller(pSource);
801 if (SUCCEEDED(autoCaller.rc()))
802 {
803 rc = pSource->i_receiveData(pTask->getCtx(), RT_INDEFINITE_WAIT /* msTimeout */);
804 }
805 else
806 rc = VERR_COM_INVALID_OBJECT_STATE;
807
808 ASMAtomicWriteBool(&pSource->mDataBase.mfTransferIsPending, false);
809
810 if (pTask)
811 delete pTask;
812
813 LogFlowFunc(("pSource=%p returning rc=%Rrc\n", (GuestDnDSource *)pSource, rc));
814 return rc;
815}
816
817int GuestDnDSource::i_receiveRawData(PRECVDATACTX pCtx, RTMSINTERVAL msTimeout)
818{
819 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
820
821 int rc;
822
823 GuestDnDResponse *pResp = pCtx->mpResp;
824 AssertPtr(pCtx->mpResp);
825
826 GuestDnD *pInst = GuestDnDInst();
827 if (!pInst)
828 return VERR_INVALID_POINTER;
829
830#define REGISTER_CALLBACK(x) \
831 rc = pResp->setCallback(x, i_receiveRawDataCallback, pCtx); \
832 if (RT_FAILURE(rc)) \
833 return rc;
834
835#define UNREGISTER_CALLBACK(x) \
836 rc = pCtx->mpResp->setCallback(x, NULL); \
837 AssertRC(rc);
838
839 /*
840 * Register callbacks.
841 */
842 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
843 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
844
845 do
846 {
847 /*
848 * Receive the raw data.
849 */
850 GuestDnDMsg Msg;
851 Msg.setType(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED);
852 Msg.setNextPointer((void*)pCtx->mFormat.c_str(), (uint32_t)pCtx->mFormat.length() + 1);
853 Msg.setNextUInt32((uint32_t)pCtx->mFormat.length() + 1);
854 Msg.setNextUInt32(pCtx->mAction);
855
856 /* Make the initial call to the guest by telling that we initiated the "dropped" event on
857 * the host and therefore now waiting for the actual raw data. */
858 rc = pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
859 if (RT_SUCCESS(rc))
860 {
861 rc = waitForEvent(msTimeout, pCtx->mCallback, pCtx->mpResp);
862 if (RT_SUCCESS(rc))
863 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_COMPLETE, VINF_SUCCESS);
864 }
865
866 } while (0);
867
868 /*
869 * Unregister callbacks.
870 */
871 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
872 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
873
874#undef REGISTER_CALLBACK
875#undef UNREGISTER_CALLBACK
876
877 if (RT_FAILURE(rc))
878 {
879 if (rc == VERR_CANCELLED)
880 {
881 int rc2 = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED, VINF_SUCCESS);
882 AssertRC(rc2);
883
884 rc2 = sendCancel();
885 AssertRC(rc2);
886 }
887 else if (rc != VERR_GSTDND_GUEST_ERROR) /* Guest-side error are already handled in the callback. */
888 {
889 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR,
890 rc, GuestDnDSource::i_hostErrorToString(rc));
891 }
892 }
893
894 LogFlowFuncLeaveRC(rc);
895 return rc;
896}
897
898int GuestDnDSource::i_receiveURIData(PRECVDATACTX pCtx, RTMSINTERVAL msTimeout)
899{
900 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
901
902 int rc;
903
904 GuestDnDResponse *pResp = pCtx->mpResp;
905 AssertPtr(pCtx->mpResp);
906
907 GuestDnD *pInst = GuestDnDInst();
908 if (!pInst)
909 return VERR_INVALID_POINTER;
910
911#define REGISTER_CALLBACK(x) \
912 rc = pResp->setCallback(x, i_receiveURIDataCallback, pCtx); \
913 if (RT_FAILURE(rc)) \
914 return rc;
915
916#define UNREGISTER_CALLBACK(x) \
917 { \
918 int rc2 = pResp->setCallback(x, NULL); \
919 AssertRC(rc2); \
920 }
921
922 /*
923 * Register callbacks.
924 */
925 /* Guest callbacks. */
926 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
927 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
928 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DIR);
929 if (mDataBase.mProtocolVersion >= 2)
930 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR);
931 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA);
932
933 do
934 {
935 rc = DnDDirDroppedFilesCreateAndOpenTemp(&pCtx->mURI.mDropDir);
936 if (RT_FAILURE(rc))
937 break;
938 LogFlowFunc(("rc=%Rrc, strDropDir=%s\n", rc, DnDDirDroppedFilesGetDirAbs(&pCtx->mURI.mDropDir)));
939 if (RT_FAILURE(rc))
940 break;
941
942 /*
943 * Receive the URI list.
944 */
945 GuestDnDMsg Msg;
946 Msg.setType(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED);
947 Msg.setNextPointer((void*)pCtx->mFormat.c_str(), (uint32_t)pCtx->mFormat.length() + 1);
948 Msg.setNextUInt32((uint32_t)pCtx->mFormat.length() + 1);
949 Msg.setNextUInt32(pCtx->mAction);
950
951 /* Make the initial call to the guest by telling that we initiated the "dropped" event on
952 * the host and therefore now waiting for the actual URI data. */
953 rc = pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
954 if (RT_SUCCESS(rc))
955 {
956 LogFlowFunc(("Waiting ...\n"));
957
958 rc = waitForEvent(msTimeout, pCtx->mCallback, pCtx->mpResp);
959 if (RT_SUCCESS(rc))
960 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_COMPLETE, VINF_SUCCESS);
961
962 LogFlowFunc(("Waiting ended with rc=%Rrc\n", rc));
963 }
964
965 } while (0);
966
967 /*
968 * Unregister callbacks.
969 */
970 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
971 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
972 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DIR);
973 if (mDataBase.mProtocolVersion >= 2)
974 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR);
975 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA);
976
977#undef REGISTER_CALLBACK
978#undef UNREGISTER_CALLBACK
979
980 int rc2;
981
982 if (RT_FAILURE(rc))
983 {
984 if (rc == VERR_CANCELLED)
985 {
986 rc2 = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED, VINF_SUCCESS);
987 AssertRC(rc2);
988
989 rc2 = sendCancel();
990 AssertRC(rc2);
991 }
992 else if (rc != VERR_GSTDND_GUEST_ERROR) /* Guest-side error are already handled in the callback. */
993 {
994 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR,
995 rc, GuestDnDSource::i_hostErrorToString(rc));
996 }
997 }
998
999 if (RT_FAILURE(rc))
1000 {
1001 rc2 = DnDDirDroppedFilesRollback(&pCtx->mURI.mDropDir); /** @todo Inform user on rollback failure? */
1002 LogFlowFunc(("Rolling back ended with rc=%Rrc\n", rc2));
1003 }
1004
1005 rc2 = DnDDirDroppedFilesClose(&pCtx->mURI.mDropDir, RT_FAILURE(rc) ? true : false /* fRemove */);
1006 if (RT_SUCCESS(rc))
1007 rc = rc2;
1008
1009 LogFlowFuncLeaveRC(rc);
1010 return rc;
1011}
1012
1013/* static */
1014DECLCALLBACK(int) GuestDnDSource::i_receiveRawDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
1015{
1016 PRECVDATACTX pCtx = (PRECVDATACTX)pvUser;
1017 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1018
1019 GuestDnDSource *pThis = pCtx->mpSource;
1020 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1021
1022 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
1023
1024 int rc = VINF_SUCCESS;
1025
1026 int rcCallback = VINF_SUCCESS; /* rc for the callback. */
1027 bool fNotify = false;
1028
1029 switch (uMsg)
1030 {
1031#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1032 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
1033 {
1034 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
1035 AssertPtr(pCBData);
1036 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1037 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1038
1039 rc = pThis->i_onReceiveData(pCtx, pCBData->pvData, pCBData->cbData, pCBData->cbTotalSize);
1040 break;
1041 }
1042 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
1043 {
1044 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
1045 AssertPtr(pCBData);
1046 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
1047 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1048
1049 pCtx->mpResp->reset();
1050
1051 if (RT_SUCCESS(pCBData->rc))
1052 pCBData->rc = VERR_GENERAL_FAILURE; /* Make sure some error is set. */
1053
1054 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc,
1055 GuestDnDSource::i_guestErrorToString(pCBData->rc));
1056 if (RT_SUCCESS(rc))
1057 rcCallback = VERR_GSTDND_GUEST_ERROR;
1058 break;
1059 }
1060#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1061 default:
1062 rc = VERR_NOT_SUPPORTED;
1063 break;
1064 }
1065
1066 if (RT_FAILURE(rc))
1067 {
1068 int rc2 = pCtx->mCallback.Notify(rc);
1069 AssertRC(rc2);
1070 }
1071
1072 LogFlowFuncLeaveRC(rc);
1073 return rc; /* Tell the guest. */
1074}
1075
1076/* static */
1077DECLCALLBACK(int) GuestDnDSource::i_receiveURIDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
1078{
1079 PRECVDATACTX pCtx = (PRECVDATACTX)pvUser;
1080 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1081
1082 GuestDnDSource *pThis = pCtx->mpSource;
1083 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1084
1085 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
1086
1087 int rc = VINF_SUCCESS;
1088
1089 int rcCallback = VINF_SUCCESS; /* rc for the callback. */
1090 bool fNotify = false;
1091
1092 switch (uMsg)
1093 {
1094#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1095 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
1096 {
1097 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
1098 AssertPtr(pCBData);
1099 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1100 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1101
1102 rc = pThis->i_onReceiveData(pCtx, pCBData->pvData, pCBData->cbData, pCBData->cbTotalSize);
1103 break;
1104 }
1105 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
1106 {
1107 DragAndDropSvc::PVBOXDNDCBSNDDIRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDIRDATA>(pvParms);
1108 AssertPtr(pCBData);
1109 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDIRDATA) == cbParms, VERR_INVALID_PARAMETER);
1110 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1111
1112 rc = pThis->i_onReceiveDir(pCtx, pCBData->pszPath, pCBData->cbPath, pCBData->fMode);
1113 break;
1114 }
1115 case DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR:
1116 {
1117 DragAndDropSvc::PVBOXDNDCBSNDFILEHDRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEHDRDATA>(pvParms);
1118 AssertPtr(pCBData);
1119 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEHDRDATA) == cbParms, VERR_INVALID_PARAMETER);
1120 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE_HDR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1121
1122 rc = pThis->i_onReceiveFileHdr(pCtx, pCBData->pszFilePath, pCBData->cbFilePath,
1123 pCBData->cbSize, pCBData->fMode, pCBData->fFlags);
1124 break;
1125 }
1126 case DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA:
1127 {
1128 DragAndDropSvc::PVBOXDNDCBSNDFILEDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEDATADATA>(pvParms);
1129 AssertPtr(pCBData);
1130 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1131 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1132
1133 if (pThis->mDataBase.mProtocolVersion <= 1)
1134 {
1135 /**
1136 * Notes for protocol v1 (< VBox 5.0):
1137 * - Every time this command is being sent it includes the file header,
1138 * so just process both calls here.
1139 * - There was no information whatsoever about the total file size; the old code only
1140 * appended data to the desired file. So just pass 0 as cbSize.
1141 */
1142 rc = pThis->i_onReceiveFileHdr(pCtx,
1143 pCBData->u.v1.pszFilePath, pCBData->u.v1.cbFilePath,
1144 0 /* cbSize */, pCBData->u.v1.fMode, 0 /* fFlags */);
1145 if (RT_SUCCESS(rc))
1146 rc = pThis->i_onReceiveFileData(pCtx, pCBData->pvData, pCBData->cbData);
1147 }
1148 else /* Protocol v2 and up. */
1149 rc = pThis->i_onReceiveFileData(pCtx, pCBData->pvData, pCBData->cbData);
1150 break;
1151 }
1152 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
1153 {
1154 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
1155 AssertPtr(pCBData);
1156 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
1157 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1158
1159 pCtx->mpResp->reset();
1160
1161 if (RT_SUCCESS(pCBData->rc))
1162 pCBData->rc = VERR_GENERAL_FAILURE; /* Make sure some error is set. */
1163
1164 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc,
1165 GuestDnDSource::i_guestErrorToString(pCBData->rc));
1166 if (RT_SUCCESS(rc))
1167 rcCallback = VERR_GSTDND_GUEST_ERROR;
1168 break;
1169 }
1170#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1171 default:
1172 rc = VERR_NOT_SUPPORTED;
1173 break;
1174 }
1175
1176 if ( RT_FAILURE(rc)
1177 || RT_FAILURE(rcCallback))
1178 {
1179 fNotify = true;
1180 if (RT_SUCCESS(rcCallback))
1181 rcCallback = rc;
1182 }
1183
1184 if (RT_FAILURE(rc))
1185 {
1186 switch (rc)
1187 {
1188 case VERR_NO_DATA:
1189 LogRel2(("DnD: Transfer to host complete\n"));
1190 break;
1191
1192 case VERR_CANCELLED:
1193 LogRel2(("DnD: Transfer to host canceled\n"));
1194 break;
1195
1196 default:
1197 LogRel(("DnD: Error %Rrc occurred, aborting transfer to host\n", rc));
1198 break;
1199 }
1200
1201 /* Unregister this callback. */
1202 AssertPtr(pCtx->mpResp);
1203 int rc2 = pCtx->mpResp->setCallback(uMsg, NULL /* PFNGUESTDNDCALLBACK */);
1204 AssertRC(rc2);
1205 }
1206
1207 /* All URI data processed? */
1208 if (pCtx->mData.cbProcessed >= pCtx->mData.cbToProcess)
1209 {
1210 Assert(pCtx->mData.cbProcessed == pCtx->mData.cbToProcess);
1211 fNotify = true;
1212 }
1213
1214 LogFlowFunc(("cbProcessed=%RU64, cbToProcess=%RU64, fNotify=%RTbool, rcCallback=%Rrc, rc=%Rrc\n",
1215 pCtx->mData.cbProcessed, pCtx->mData.cbToProcess, fNotify, rcCallback, rc));
1216
1217 if (fNotify)
1218 {
1219 int rc2 = pCtx->mCallback.Notify(rcCallback);
1220 AssertRC(rc2);
1221 }
1222
1223 LogFlowFuncLeaveRC(rc);
1224 return rc; /* Tell the guest. */
1225}
1226
1227int GuestDnDSource::i_updateProcess(PRECVDATACTX pCtx, uint64_t cbDataAdd)
1228{
1229 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1230
1231 LogFlowFunc(("cbProcessed=%RU64 (+ %RU64 = %RU64), cbToProcess=%RU64\n",
1232 pCtx->mData.cbProcessed, cbDataAdd, pCtx->mData.cbProcessed + cbDataAdd, pCtx->mData.cbToProcess));
1233
1234 pCtx->mData.cbProcessed += cbDataAdd;
1235 Assert(pCtx->mData.cbProcessed <= pCtx->mData.cbToProcess);
1236
1237 int64_t cbTotal = pCtx->mData.cbToProcess;
1238 uint8_t uPercent = pCtx->mData.cbProcessed * 100 / (cbTotal ? cbTotal : 1);
1239
1240 int rc = pCtx->mpResp->setProgress(uPercent,
1241 uPercent >= 100
1242 ? DragAndDropSvc::DND_PROGRESS_COMPLETE
1243 : DragAndDropSvc::DND_PROGRESS_RUNNING);
1244 return rc;
1245}
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