VirtualBox

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

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

*: scm cleanup run.

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