VirtualBox

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

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

build fix

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