VirtualBox

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

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

DnD: Use a separate context for URI object when transferring, misc cleanups.

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