VirtualBox

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

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

DnD: Another bugfix for protocol v1.

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