VirtualBox

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

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

DnD: Fixed hang when reporting host errors such as inaccessible (locked) files.

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