VirtualBox

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

Last change on this file since 58059 was 57776, checked in by vboxsync, 9 years ago

DnD: Renamed DNDDIRDROPPEDFILES to DnDDroppedFiles and restructured it for being a class.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 45.3 KB
Line 
1/* $Id: GuestDnDSourceImpl.cpp 57776 2015-09-16 09:40:54Z 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 const char *pszDroppedFilesDir = pCtx->mURI.droppedFiles.GetDirAbs();
422 Utf8Str strURIs = pCtx->mURI.lstURI.RootToString(RTCString(pszDroppedFilesDir));
423 cbData = strURIs.length();
424
425 LogFlowFunc(("Found %zu root URIs (%zu bytes)\n", pCtx->mURI.lstURI.RootCount(), cbData));
426
427 aData.resize(cbData + 1 /* Include termination */);
428 memcpy(&aData.front(), strURIs.c_str(), cbData);
429 }
430 else
431 {
432 cbData = pCtx->mData.vecData.size();
433
434 /* Copy the data into a safe array of bytes. */
435 aData.resize(cbData);
436 memcpy(&aData.front(), &pCtx->mData.vecData[0], cbData);
437 }
438 }
439 catch (std::bad_alloc &)
440 {
441 hr = E_OUTOFMEMORY;
442 }
443
444 LogFlowFunc(("Returning cbData=%zu, hr=%Rhrc\n", cbData, hr));
445 return hr;
446#endif /* VBOX_WITH_DRAG_AND_DROP */
447}
448
449// implementation of internal methods.
450/////////////////////////////////////////////////////////////////////////////
451
452/* static */
453Utf8Str GuestDnDSource::i_guestErrorToString(int guestRc)
454{
455 Utf8Str strError;
456
457 switch (guestRc)
458 {
459 case VERR_ACCESS_DENIED:
460 strError += Utf8StrFmt(tr("For one or more guest files or directories selected for transferring to the host your guest "
461 "user does not have the appropriate access rights for. Please make sure that all selected "
462 "elements can be accessed and that your guest user has the appropriate rights"));
463 break;
464
465 case VERR_NOT_FOUND:
466 /* Should not happen due to file locking on the guest, but anyway ... */
467 strError += Utf8StrFmt(tr("One or more guest files or directories selected for transferring to the host were not"
468 "found on the guest anymore. This can be the case if the guest 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 guest files or directories selected for transferring to the host were locked. "
474 "Please make sure that all selected elements can be accessed and that your guest user has "
475 "the appropriate rights"));
476 break;
477
478 case VERR_TIMEOUT:
479 strError += Utf8StrFmt(tr("The guest was not able to retrieve the drag and drop data within time"));
480 break;
481
482 default:
483 strError += Utf8StrFmt(tr("Drag and drop error from guest (%Rrc)"), guestRc);
484 break;
485 }
486
487 return strError;
488}
489
490/* static */
491Utf8Str GuestDnDSource::i_hostErrorToString(int hostRc)
492{
493 Utf8Str strError;
494
495 switch (hostRc)
496 {
497 case VERR_ACCESS_DENIED:
498 strError += Utf8StrFmt(tr("For one or more host files or directories selected for transferring to the guest your host "
499 "user does not have the appropriate access rights for. Please make sure that all selected "
500 "elements can be accessed and that your host user has the appropriate rights."));
501 break;
502
503 case VERR_NOT_FOUND:
504 /* Should not happen due to file locking on the host, but anyway ... */
505 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the host were not"
506 "found on the host anymore. This can be the case if the host files were moved and/or"
507 "altered while the drag and drop operation was in progress."));
508 break;
509
510 case VERR_SHARING_VIOLATION:
511 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the guest were locked. "
512 "Please make sure that all selected elements can be accessed and that your host user has "
513 "the appropriate rights."));
514 break;
515
516 default:
517 strError += Utf8StrFmt(tr("Drag and drop error from host (%Rrc)"), hostRc);
518 break;
519 }
520
521 return strError;
522}
523
524#ifdef VBOX_WITH_DRAG_AND_DROP_GH
525int GuestDnDSource::i_onReceiveData(PRECVDATACTX pCtx, const void *pvData, uint32_t cbData, uint64_t cbTotalSize)
526{
527 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
528 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
529 AssertReturn(cbData, VERR_INVALID_PARAMETER);
530 AssertReturn(cbTotalSize, VERR_INVALID_PARAMETER);
531
532 LogFlowFunc(("cbData=%RU32, cbTotalSize=%RU64\n", cbData, cbTotalSize));
533
534 int rc = VINF_SUCCESS;
535
536 try
537 {
538 if ( cbData > cbTotalSize
539 || cbData > mData.mcbBlockSize)
540 {
541 LogFlowFunc(("Data sizes invalid: cbData=%RU32, cbTotalSize=%RU64\n", cbData, cbTotalSize));
542 rc = VERR_INVALID_PARAMETER;
543 }
544 else if (cbData < pCtx->mData.vecData.size())
545 {
546 AssertMsgFailed(("New size (%RU64) is smaller than current size (%zu)\n", cbTotalSize, pCtx->mData.vecData.size()));
547 rc = VERR_INVALID_PARAMETER;
548 }
549
550 if (RT_SUCCESS(rc))
551 {
552 pCtx->mData.vecData.insert(pCtx->mData.vecData.begin(), (BYTE *)pvData, (BYTE *)pvData + cbData);
553
554 LogFlowFunc(("vecDataSize=%zu, cbData=%RU32, cbTotalSize=%RU64\n", pCtx->mData.vecData.size(), cbData, cbTotalSize));
555
556 /* Data transfer complete? */
557 Assert(cbData <= pCtx->mData.vecData.size());
558 if (cbData == pCtx->mData.vecData.size())
559 {
560 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFmtRecv.c_str(), pCtx->mFmtRecv.length());
561 LogFlowFunc(("fHasURIList=%RTbool, cbTotalSize=%RU32\n", fHasURIList, cbTotalSize));
562 if (fHasURIList)
563 {
564 /* Try parsing the data as URI list. */
565 rc = pCtx->mURI.lstURI.RootFromURIData(&pCtx->mData.vecData[0], pCtx->mData.vecData.size(), 0 /* uFlags */);
566 if (RT_SUCCESS(rc))
567 {
568 /* Reset processed bytes. */
569 pCtx->mData.cbProcessed = 0;
570
571 /*
572 * Assign new total size which also includes all file data to receive
573 * from the guest.
574 */
575 pCtx->mData.cbToProcess = cbTotalSize;
576
577 /* Update our process with the data we already received.
578 * Note: The total size will consist of the meta data (in vecData) and
579 * the actual accumulated file/directory data from the guest. */
580 rc = i_updateProcess(pCtx, (uint64_t)pCtx->mData.vecData.size());
581
582 LogFlowFunc(("URI data => cbProcessed=%RU64, cbToProcess=%RU64, rc=%Rrc\n",
583 pCtx->mData.cbProcessed, pCtx->mData.cbToProcess, rc));
584 }
585 }
586 }
587 }
588 }
589 catch (std::bad_alloc &)
590 {
591 rc = VERR_NO_MEMORY;
592 }
593
594 LogFlowFuncLeaveRC(rc);
595 return rc;
596}
597
598int GuestDnDSource::i_onReceiveDir(PRECVDATACTX pCtx, const char *pszPath, uint32_t cbPath, uint32_t fMode)
599{
600 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
601 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
602 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
603
604 LogFlowFunc(("pszPath=%s, cbPath=%RU32, fMode=0x%x\n", pszPath, cbPath, fMode));
605
606 int rc;
607
608 const char *pszDroppedFilesDir = pCtx->mURI.droppedFiles.GetDirAbs();
609 char *pszDir = RTPathJoinA(pszDroppedFilesDir, pszPath);
610 if (pszDir)
611 {
612 rc = RTDirCreateFullPath(pszDir, fMode);
613 if (RT_FAILURE(rc))
614 LogRel2(("DnD: Error creating guest directory '%s' on the host, rc=%Rrc\n", pszDir, rc));
615
616 RTStrFree(pszDir);
617 }
618 else
619 rc = VERR_NO_MEMORY;
620
621 LogFlowFuncLeaveRC(rc);
622 return rc;
623}
624
625int GuestDnDSource::i_onReceiveFileHdr(PRECVDATACTX pCtx, GuestDnDURIObjCtx *pObjCtx, const char *pszPath, uint32_t cbPath,
626 uint64_t cbSize, uint32_t fMode, uint32_t fFlags)
627{
628 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
629 AssertPtrReturn(pObjCtx, VERR_INVALID_POINTER);
630 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
631 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
632 AssertReturn(fMode, VERR_INVALID_PARAMETER);
633 /* fFlags are optional. */
634
635 LogFlowFunc(("pszPath=%s, cbPath=%RU32, cbSize=%RU64, fMode=0x%x, fFlags=0x%x\n", pszPath, cbPath, cbSize, fMode, fFlags));
636
637 int rc = VINF_SUCCESS;
638
639 do
640 {
641 DnDURIObject *pObj = pObjCtx->pObjURI;
642
643 if ( pObj
644 && pObj->IsOpen()
645 && !pObj->IsComplete())
646 {
647 AssertMsgFailed(("Object '%s' not complete yet\n", pObj->GetDestPath().c_str()));
648 rc = VERR_WRONG_ORDER;
649 break;
650 }
651
652 if ( pObj
653 && pObj->IsOpen()) /* File already opened? */
654 {
655 AssertMsgFailed(("Current opened object is '%s', close this first\n", pObj->GetDestPath().c_str()));
656 rc = VERR_WRONG_ORDER;
657 break;
658 }
659
660 if (cbSize > pCtx->mData.cbToProcess)
661 {
662 AssertMsgFailed(("File size (%RU64) exceeds total size to transfer (%RU64)\n", cbSize, pCtx->mData.cbToProcess));
663 rc = VERR_INVALID_PARAMETER;
664 break;
665 }
666
667 const char *pszDroppedFilesDir = pCtx->mURI.droppedFiles.GetDirAbs();
668
669 char pszPathAbs[RTPATH_MAX];
670 rc = RTPathJoin(pszPathAbs, sizeof(pszPathAbs), pszDroppedFilesDir, pszPath);
671 if (RT_FAILURE(rc))
672 {
673 LogFlowFunc(("Warning: Rebasing current file failed with rc=%Rrc\n", rc));
674 break;
675 }
676
677 rc = DnDPathSanitize(pszPathAbs, sizeof(pszPathAbs));
678 if (RT_FAILURE(rc))
679 {
680 LogFlowFunc(("Warning: Rebasing current file failed with rc=%Rrc\n", rc));
681 break;
682 }
683
684 LogFunc(("Rebased to: %s\n", pszPathAbs));
685
686 if ( pObj
687 && pObjCtx->fAllocated)
688 {
689 delete pObj;
690 pObj = NULL;
691 }
692
693 try
694 {
695 pObj = new DnDURIObject();
696
697 pObjCtx->pObjURI = pObj;
698 pObjCtx->fAllocated = true;
699 }
700 catch (std::bad_alloc &)
701 {
702 rc = VERR_NO_MEMORY;
703 }
704
705 if (RT_SUCCESS(rc))
706 {
707 /** @todo Add sparse file support based on fFlags? (Use Open(..., fFlags | SPARSE). */
708 rc = pObj->OpenEx(pszPathAbs, DnDURIObject::File, DnDURIObject::Target,
709 RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE,
710 (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR);
711 }
712
713 if (RT_SUCCESS(rc))
714 {
715 /* Note: Protocol v1 does not send any file sizes, so always 0. */
716 if (mDataBase.mProtocolVersion >= 2)
717 rc = pObj->SetSize(cbSize);
718
719 /** @todo Unescpae path before printing. */
720 LogRel2(("DnD: Transferring guest file to host: %s (%RU64 bytes, mode 0x%x)\n",
721 pObj->GetDestPath().c_str(), pObj->GetSize(), pObj->GetMode()));
722
723 /** @todo Set progress object title to current file being transferred? */
724
725 if (!cbSize) /* 0-byte file? Close again. */
726 pObj->Close();
727 }
728
729 if (RT_FAILURE(rc))
730 {
731 LogRel2(("DnD: Error opening/creating guest file '%s' on host, rc=%Rrc\n",
732 pObj->GetDestPath().c_str(), rc));
733 break;
734 }
735
736 } while (0);
737
738 LogFlowFuncLeaveRC(rc);
739 return rc;
740}
741
742int GuestDnDSource::i_onReceiveFileData(PRECVDATACTX pCtx, GuestDnDURIObjCtx *pObjCtx, const void *pvData, uint32_t cbData)
743{
744 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
745 AssertPtrReturn(pObjCtx, VERR_INVALID_POINTER);
746 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
747 AssertReturn(cbData, VERR_INVALID_PARAMETER);
748
749 int rc = VINF_SUCCESS;
750
751 do
752 {
753 DnDURIObject *pObj = pObjCtx->pObjURI;
754 if (!pObj)
755 {
756 rc = VERR_INVALID_PARAMETER;
757 break;
758 }
759
760 if (pObj->IsComplete())
761 {
762 LogFlowFunc(("Warning: Object '%s' already completed\n", pObj->GetDestPath().c_str()));
763 rc = VERR_WRONG_ORDER;
764 break;
765 }
766
767 if (!pObj->IsOpen()) /* File opened on host? */
768 {
769 LogFlowFunc(("Warning: Object '%s' not opened\n", pObj->GetDestPath().c_str()));
770 rc = VERR_WRONG_ORDER;
771 break;
772 }
773
774 uint32_t cbWritten;
775 rc = pObj->Write(pvData, cbData, &cbWritten);
776 if (RT_SUCCESS(rc))
777 {
778 Assert(cbWritten <= cbData);
779 if (cbWritten < cbData)
780 {
781 /** @todo What to do when the host's disk is full? */
782 rc = VERR_DISK_FULL;
783 }
784
785 if (RT_SUCCESS(rc))
786 rc = i_updateProcess(pCtx, cbWritten);
787 }
788
789 if (RT_SUCCESS(rc))
790 {
791 if (pObj->IsComplete())
792 {
793 /** @todo Sanitize path. */
794 LogRel2(("DnD: File transfer to host complete: %s\n", pObj->GetDestPath().c_str()));
795 rc = VINF_EOF;
796
797 /* Deletion needed? */
798 if (pObjCtx->fAllocated)
799 {
800 delete pObj;
801 pObj = NULL;
802
803 pObjCtx->fAllocated = false;
804 }
805 }
806 }
807 else
808 {
809 /** @todo What to do when the host's disk is full? */
810 LogRel(("DnD: Error writing guest file to host to '%s': %Rrc\n", pObj->GetDestPath().c_str(), rc));
811 }
812
813 } while (0);
814
815 LogFlowFuncLeaveRC(rc);
816 return rc;
817}
818#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
819
820int GuestDnDSource::i_receiveData(PRECVDATACTX pCtx, RTMSINTERVAL msTimeout)
821{
822 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
823
824 GuestDnD *pInst = GuestDnDInst();
825 if (!pInst)
826 return VERR_INVALID_POINTER;
827
828 GuestDnDResponse *pResp = pCtx->mpResp;
829 AssertPtr(pCtx->mpResp);
830
831 /* Is this context already in receiving state? */
832 if (ASMAtomicReadBool(&pCtx->mIsActive))
833 return VERR_WRONG_ORDER;
834
835 ASMAtomicWriteBool(&pCtx->mIsActive, true);
836
837 int rc = pCtx->mCallback.Reset();
838 if (RT_FAILURE(rc))
839 return rc;
840
841 /*
842 * Reset any old data.
843 */
844 pCtx->mData.Reset();
845 pCtx->mURI.Reset();
846 pResp->reset();
847
848 /*
849 * Do we need to receive a different format than initially requested?
850 *
851 * For example, receiving a file link as "text/plain" requires still to receive
852 * the file from the guest as "text/uri-list" first, then pointing to
853 * the file path on the host in the "text/plain" data returned.
854 */
855
856 /* Plain text needed? */
857 if (pCtx->mFmtReq.equalsIgnoreCase("text/plain"))
858 {
859 /* Did the guest offer a file? Receive a file instead. */
860 if (GuestDnD::isFormatInFormatList("text/uri-list", pCtx->mFmtOffered))
861 pCtx->mFmtRecv = "text/uri-list";
862
863 /** @todo Add more conversions here. */
864 }
865
866 if (pCtx->mFmtRecv.isEmpty())
867 pCtx->mFmtRecv = pCtx->mFmtReq;
868
869 if (!pCtx->mFmtRecv.equals(pCtx->mFmtReq))
870 LogRel3(("DnD: Requested data in format '%s', receiving in intermediate format '%s' now\n",
871 pCtx->mFmtReq.c_str(), pCtx->mFmtRecv.c_str()));
872
873 /*
874 * Call the appropriate receive handler based on the data format to handle.
875 */
876 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFmtRecv.c_str(), pCtx->mFmtRecv.length());
877 LogFlowFunc(("strFormatReq=%s, strFormatRecv=%s, uAction=0x%x, fHasURIList=%RTbool\n",
878 pCtx->mFmtReq.c_str(), pCtx->mFmtRecv.c_str(), pCtx->mAction, fHasURIList));
879
880 if (fHasURIList)
881 {
882 rc = i_receiveURIData(pCtx, msTimeout);
883 }
884 else
885 {
886 rc = i_receiveRawData(pCtx, msTimeout);
887 }
888
889 ASMAtomicWriteBool(&pCtx->mIsActive, false);
890
891 LogFlowFuncLeaveRC(rc);
892 return rc;
893}
894
895/* static */
896DECLCALLBACK(int) GuestDnDSource::i_receiveDataThread(RTTHREAD Thread, void *pvUser)
897{
898 LogFlowFunc(("pvUser=%p\n", pvUser));
899
900 RecvDataTask *pTask = (RecvDataTask *)pvUser;
901 AssertPtrReturn(pTask, VERR_INVALID_POINTER);
902
903 const ComObjPtr<GuestDnDSource> pSource(pTask->getSource());
904 Assert(!pSource.isNull());
905
906 int rc;
907
908 AutoCaller autoCaller(pSource);
909 if (SUCCEEDED(autoCaller.rc()))
910 {
911 rc = pSource->i_receiveData(pTask->getCtx(), RT_INDEFINITE_WAIT /* msTimeout */);
912 }
913 else
914 rc = VERR_COM_INVALID_OBJECT_STATE;
915
916 ASMAtomicWriteBool(&pSource->mDataBase.mfTransferIsPending, false);
917
918 if (pTask)
919 delete pTask;
920
921 LogFlowFunc(("pSource=%p returning rc=%Rrc\n", (GuestDnDSource *)pSource, rc));
922 return rc;
923}
924
925int GuestDnDSource::i_receiveRawData(PRECVDATACTX pCtx, RTMSINTERVAL msTimeout)
926{
927 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
928
929 int rc;
930
931 GuestDnDResponse *pResp = pCtx->mpResp;
932 AssertPtr(pCtx->mpResp);
933
934 GuestDnD *pInst = GuestDnDInst();
935 if (!pInst)
936 return VERR_INVALID_POINTER;
937
938#define REGISTER_CALLBACK(x) \
939 rc = pResp->setCallback(x, i_receiveRawDataCallback, pCtx); \
940 if (RT_FAILURE(rc)) \
941 return rc;
942
943#define UNREGISTER_CALLBACK(x) \
944 rc = pCtx->mpResp->setCallback(x, NULL); \
945 AssertRC(rc);
946
947 /*
948 * Register callbacks.
949 */
950 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
951 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
952
953 do
954 {
955 /*
956 * Receive the raw data.
957 */
958 GuestDnDMsg Msg;
959 Msg.setType(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED);
960 Msg.setNextPointer((void*)pCtx->mFmtRecv.c_str(), (uint32_t)pCtx->mFmtRecv.length() + 1);
961 Msg.setNextUInt32((uint32_t)pCtx->mFmtRecv.length() + 1);
962 Msg.setNextUInt32(pCtx->mAction);
963
964 /* Make the initial call to the guest by telling that we initiated the "dropped" event on
965 * the host and therefore now waiting for the actual raw data. */
966 rc = pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
967 if (RT_SUCCESS(rc))
968 {
969 rc = waitForEvent(msTimeout, pCtx->mCallback, pCtx->mpResp);
970 if (RT_SUCCESS(rc))
971 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_COMPLETE, VINF_SUCCESS);
972 }
973
974 } while (0);
975
976 /*
977 * Unregister callbacks.
978 */
979 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
980 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
981
982#undef REGISTER_CALLBACK
983#undef UNREGISTER_CALLBACK
984
985 if (RT_FAILURE(rc))
986 {
987 if (rc == VERR_CANCELLED)
988 {
989 int rc2 = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED, VINF_SUCCESS);
990 AssertRC(rc2);
991
992 rc2 = sendCancel();
993 AssertRC(rc2);
994 }
995 else if (rc != VERR_GSTDND_GUEST_ERROR) /* Guest-side error are already handled in the callback. */
996 {
997 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR,
998 rc, GuestDnDSource::i_hostErrorToString(rc));
999 }
1000 }
1001
1002 LogFlowFuncLeaveRC(rc);
1003 return rc;
1004}
1005
1006int GuestDnDSource::i_receiveURIData(PRECVDATACTX pCtx, RTMSINTERVAL msTimeout)
1007{
1008 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1009
1010 int rc;
1011
1012 GuestDnDResponse *pResp = pCtx->mpResp;
1013 AssertPtr(pCtx->mpResp);
1014
1015 GuestDnD *pInst = GuestDnDInst();
1016 if (!pInst)
1017 return VERR_INVALID_POINTER;
1018
1019#define REGISTER_CALLBACK(x) \
1020 rc = pResp->setCallback(x, i_receiveURIDataCallback, pCtx); \
1021 if (RT_FAILURE(rc)) \
1022 return rc;
1023
1024#define UNREGISTER_CALLBACK(x) \
1025 { \
1026 int rc2 = pResp->setCallback(x, NULL); \
1027 AssertRC(rc2); \
1028 }
1029
1030 /*
1031 * Register callbacks.
1032 */
1033 /* Guest callbacks. */
1034 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
1035 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
1036 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DIR);
1037 if (mDataBase.mProtocolVersion >= 2)
1038 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR);
1039 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA);
1040
1041 DnDDroppedFiles &droppedFiles = pCtx->mURI.droppedFiles;
1042
1043 do
1044 {
1045 rc = droppedFiles.OpenTemp(0 /* fFlags */);
1046 if (RT_FAILURE(rc))
1047 break;
1048 LogFlowFunc(("rc=%Rrc, strDropDir=%s\n", rc, droppedFiles.GetDirAbs()));
1049 if (RT_FAILURE(rc))
1050 break;
1051
1052 /*
1053 * Receive the URI list.
1054 */
1055 GuestDnDMsg Msg;
1056 Msg.setType(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED);
1057 Msg.setNextPointer((void*)pCtx->mFmtRecv.c_str(), (uint32_t)pCtx->mFmtRecv.length() + 1);
1058 Msg.setNextUInt32((uint32_t)pCtx->mFmtRecv.length() + 1);
1059 Msg.setNextUInt32(pCtx->mAction);
1060
1061 /* Make the initial call to the guest by telling that we initiated the "dropped" event on
1062 * the host and therefore now waiting for the actual URI data. */
1063 rc = pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
1064 if (RT_SUCCESS(rc))
1065 {
1066 LogFlowFunc(("Waiting ...\n"));
1067
1068 rc = waitForEvent(msTimeout, pCtx->mCallback, pCtx->mpResp);
1069 if (RT_SUCCESS(rc))
1070 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_COMPLETE, VINF_SUCCESS);
1071
1072 LogFlowFunc(("Waiting ended with rc=%Rrc\n", rc));
1073 }
1074
1075 } while (0);
1076
1077 /*
1078 * Unregister callbacks.
1079 */
1080 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
1081 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
1082 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DIR);
1083 if (mDataBase.mProtocolVersion >= 2)
1084 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR);
1085 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA);
1086
1087#undef REGISTER_CALLBACK
1088#undef UNREGISTER_CALLBACK
1089
1090 int rc2;
1091
1092 if (RT_FAILURE(rc))
1093 {
1094 if (rc == VERR_CANCELLED)
1095 {
1096 rc2 = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED, VINF_SUCCESS);
1097 AssertRC(rc2);
1098
1099 rc2 = sendCancel();
1100 AssertRC(rc2);
1101 }
1102 else if (rc != VERR_GSTDND_GUEST_ERROR) /* Guest-side error are already handled in the callback. */
1103 {
1104 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR,
1105 rc, GuestDnDSource::i_hostErrorToString(rc));
1106 }
1107 }
1108
1109 if (RT_FAILURE(rc))
1110 {
1111 rc2 = droppedFiles.Rollback(); /** @todo Inform user on rollback failure? */
1112 LogFlowFunc(("Rolling back ended with rc=%Rrc\n", rc2));
1113 }
1114
1115 rc2 = droppedFiles.Reset(RT_FAILURE(rc) ? true : false /* fRemoveDropDir */);
1116 if (RT_SUCCESS(rc))
1117 rc = rc2;
1118
1119 LogFlowFuncLeaveRC(rc);
1120 return rc;
1121}
1122
1123/* static */
1124DECLCALLBACK(int) GuestDnDSource::i_receiveRawDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
1125{
1126 PRECVDATACTX pCtx = (PRECVDATACTX)pvUser;
1127 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1128
1129 GuestDnDSource *pThis = pCtx->mpSource;
1130 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1131
1132 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
1133
1134 int rc = VINF_SUCCESS;
1135
1136 int rcCallback = VINF_SUCCESS; /* rc for the callback. */
1137 bool fNotify = false;
1138
1139 switch (uMsg)
1140 {
1141#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1142 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
1143 {
1144 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
1145 AssertPtr(pCBData);
1146 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1147 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1148
1149 rc = pThis->i_onReceiveData(pCtx, pCBData->pvData, pCBData->cbData, pCBData->cbTotalSize);
1150 break;
1151 }
1152 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
1153 {
1154 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
1155 AssertPtr(pCBData);
1156 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
1157 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1158
1159 pCtx->mpResp->reset();
1160
1161 if (RT_SUCCESS(pCBData->rc))
1162 pCBData->rc = VERR_GENERAL_FAILURE; /* Make sure some error is set. */
1163
1164 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc,
1165 GuestDnDSource::i_guestErrorToString(pCBData->rc));
1166 if (RT_SUCCESS(rc))
1167 rcCallback = VERR_GSTDND_GUEST_ERROR;
1168 break;
1169 }
1170#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1171 default:
1172 rc = VERR_NOT_SUPPORTED;
1173 break;
1174 }
1175
1176 if (RT_FAILURE(rc))
1177 {
1178 int rc2 = pCtx->mCallback.Notify(rc);
1179 AssertRC(rc2);
1180 }
1181
1182 LogFlowFuncLeaveRC(rc);
1183 return rc; /* Tell the guest. */
1184}
1185
1186/* static */
1187DECLCALLBACK(int) GuestDnDSource::i_receiveURIDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
1188{
1189 PRECVDATACTX pCtx = (PRECVDATACTX)pvUser;
1190 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1191
1192 GuestDnDSource *pThis = pCtx->mpSource;
1193 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1194
1195 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
1196
1197 int rc = VINF_SUCCESS;
1198
1199 int rcCallback = VINF_SUCCESS; /* rc for the callback. */
1200 bool fNotify = false;
1201
1202 switch (uMsg)
1203 {
1204#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1205 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
1206 {
1207 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
1208 AssertPtr(pCBData);
1209 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1210 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1211
1212 rc = pThis->i_onReceiveData(pCtx, pCBData->pvData, pCBData->cbData, pCBData->cbTotalSize);
1213 break;
1214 }
1215 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
1216 {
1217 DragAndDropSvc::PVBOXDNDCBSNDDIRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDIRDATA>(pvParms);
1218 AssertPtr(pCBData);
1219 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDIRDATA) == cbParms, VERR_INVALID_PARAMETER);
1220 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1221
1222 rc = pThis->i_onReceiveDir(pCtx, pCBData->pszPath, pCBData->cbPath, pCBData->fMode);
1223 break;
1224 }
1225 case DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR:
1226 {
1227 DragAndDropSvc::PVBOXDNDCBSNDFILEHDRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEHDRDATA>(pvParms);
1228 AssertPtr(pCBData);
1229 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEHDRDATA) == cbParms, VERR_INVALID_PARAMETER);
1230 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE_HDR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1231
1232 rc = pThis->i_onReceiveFileHdr(pCtx, &pCtx->mURI.objCtx, pCBData->pszFilePath, pCBData->cbFilePath,
1233 pCBData->cbSize, pCBData->fMode, pCBData->fFlags);
1234 break;
1235 }
1236 case DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA:
1237 {
1238 DragAndDropSvc::PVBOXDNDCBSNDFILEDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEDATADATA>(pvParms);
1239 AssertPtr(pCBData);
1240 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1241 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1242
1243 if (pThis->mDataBase.mProtocolVersion <= 1)
1244 {
1245 /**
1246 * Notes for protocol v1 (< VBox 5.0):
1247 * - Every time this command is being sent it includes the file header,
1248 * so just process both calls here.
1249 * - There was no information whatsoever about the total file size; the old code only
1250 * appended data to the desired file. So just pass 0 as cbSize.
1251 */
1252 rc = pThis->i_onReceiveFileHdr(pCtx, &pCtx->mURI.objCtx,
1253 pCBData->u.v1.pszFilePath, pCBData->u.v1.cbFilePath,
1254 0 /* cbSize */, pCBData->u.v1.fMode, 0 /* fFlags */);
1255 if (RT_SUCCESS(rc))
1256 rc = pThis->i_onReceiveFileData(pCtx, &pCtx->mURI.objCtx, pCBData->pvData, pCBData->cbData);
1257 }
1258 else /* Protocol v2 and up. */
1259 rc = pThis->i_onReceiveFileData(pCtx, &pCtx->mURI.objCtx, pCBData->pvData, pCBData->cbData);
1260 break;
1261 }
1262 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
1263 {
1264 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
1265 AssertPtr(pCBData);
1266 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
1267 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1268
1269 pCtx->mpResp->reset();
1270
1271 if (RT_SUCCESS(pCBData->rc))
1272 pCBData->rc = VERR_GENERAL_FAILURE; /* Make sure some error is set. */
1273
1274 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc,
1275 GuestDnDSource::i_guestErrorToString(pCBData->rc));
1276 if (RT_SUCCESS(rc))
1277 rcCallback = VERR_GSTDND_GUEST_ERROR;
1278 break;
1279 }
1280#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1281 default:
1282 rc = VERR_NOT_SUPPORTED;
1283 break;
1284 }
1285
1286 if ( RT_FAILURE(rc)
1287 || RT_FAILURE(rcCallback))
1288 {
1289 fNotify = true;
1290 if (RT_SUCCESS(rcCallback))
1291 rcCallback = rc;
1292 }
1293
1294 if (RT_FAILURE(rc))
1295 {
1296 switch (rc)
1297 {
1298 case VERR_NO_DATA:
1299 LogRel2(("DnD: Transfer to host complete\n"));
1300 break;
1301
1302 case VERR_CANCELLED:
1303 LogRel2(("DnD: Transfer to host canceled\n"));
1304 break;
1305
1306 default:
1307 LogRel(("DnD: Error %Rrc occurred, aborting transfer to host\n", rc));
1308 break;
1309 }
1310
1311 /* Unregister this callback. */
1312 AssertPtr(pCtx->mpResp);
1313 int rc2 = pCtx->mpResp->setCallback(uMsg, NULL /* PFNGUESTDNDCALLBACK */);
1314 AssertRC(rc2);
1315 }
1316
1317 /* All URI data processed? */
1318 if (pCtx->mData.cbProcessed >= pCtx->mData.cbToProcess)
1319 {
1320 Assert(pCtx->mData.cbProcessed == pCtx->mData.cbToProcess);
1321 fNotify = true;
1322 }
1323
1324 LogFlowFunc(("cbProcessed=%RU64, cbToProcess=%RU64, fNotify=%RTbool, rcCallback=%Rrc, rc=%Rrc\n",
1325 pCtx->mData.cbProcessed, pCtx->mData.cbToProcess, fNotify, rcCallback, rc));
1326
1327 if (fNotify)
1328 {
1329 int rc2 = pCtx->mCallback.Notify(rcCallback);
1330 AssertRC(rc2);
1331 }
1332
1333 LogFlowFuncLeaveRC(rc);
1334 return rc; /* Tell the guest. */
1335}
1336
1337int GuestDnDSource::i_updateProcess(PRECVDATACTX pCtx, uint64_t cbDataAdd)
1338{
1339 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1340
1341 LogFlowFunc(("cbProcessed=%RU64 (+ %RU64 = %RU64), cbToProcess=%RU64\n",
1342 pCtx->mData.cbProcessed, cbDataAdd, pCtx->mData.cbProcessed + cbDataAdd, pCtx->mData.cbToProcess));
1343
1344 pCtx->mData.cbProcessed += cbDataAdd;
1345 Assert(pCtx->mData.cbProcessed <= pCtx->mData.cbToProcess);
1346
1347 int64_t cbTotal = pCtx->mData.cbToProcess;
1348 uint8_t uPercent = pCtx->mData.cbProcessed * 100 / (cbTotal ? cbTotal : 1);
1349
1350 int rc = pCtx->mpResp->setProgress(uPercent,
1351 uPercent >= 100
1352 ? DragAndDropSvc::DND_PROGRESS_COMPLETE
1353 : DragAndDropSvc::DND_PROGRESS_RUNNING);
1354 return rc;
1355}
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