VirtualBox

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

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

DnD: ErrorInfo handling, formatting, validation.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.4 KB
Line 
1/* $Id: GuestDnDSourceImpl.cpp 55539 2015-04-30 09:48:34Z 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, std::vector<com::Utf8Str> &aFormats,
232 std::vector<DnDAction_T> &aAllowedActions, DnDAction_T *aDefaultAction)
233{
234#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
235 ReturnComNotImplemented();
236#else /* VBOX_WITH_DRAG_AND_DROP */
237
238 AutoCaller autoCaller(this);
239 if (FAILED(autoCaller.rc())) return autoCaller.rc();
240
241 /* Determine guest DnD protocol to use. */
242 GuestDnDBase::getProtocolVersion(&mDataBase.mProtocolVersion);
243
244 /* Default is ignoring the action. */
245 DnDAction_T defaultAction = DnDAction_Ignore;
246
247 HRESULT hr = S_OK;
248
249 GuestDnDMsg Msg;
250 Msg.setType(DragAndDropSvc::HOST_DND_GH_REQ_PENDING);
251 Msg.setNextUInt32(uScreenId);
252
253 int rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
254 if (RT_SUCCESS(rc))
255 {
256 bool fFetchResult = true;
257 GuestDnDResponse *pResp = GuestDnDInst()->response();
258 if (pResp)
259 {
260 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
261 fFetchResult = false;
262
263 if (isDnDIgnoreAction(pResp->defAction()))
264 fFetchResult = false;
265
266 /* Fetch the default action to use. */
267 if (fFetchResult)
268 {
269 defaultAction = GuestDnD::toMainAction(pResp->defAction());
270
271 GuestDnD::toFormatVector(m_strFormats, pResp->format(), aFormats);
272 GuestDnD::toMainActions(pResp->allActions(), aAllowedActions);
273 }
274 }
275
276 if (aDefaultAction)
277 *aDefaultAction = defaultAction;
278 }
279
280 if (RT_FAILURE(rc))
281 hr = setError(VBOX_E_IPRT_ERROR,
282 tr("Error retrieving drag and drop pending status (%Rrc)\n"), rc);
283
284 LogFlowFunc(("hr=%Rhrc, defaultAction=0x%x\n", hr, defaultAction));
285 return hr;
286#endif /* VBOX_WITH_DRAG_AND_DROP */
287}
288
289HRESULT GuestDnDSource::drop(const com::Utf8Str &aFormat, DnDAction_T aAction, ComPtr<IProgress> &aProgress)
290{
291#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
292 ReturnComNotImplemented();
293#else /* VBOX_WITH_DRAG_AND_DROP */
294
295 /* Input validation. */
296 if (RT_UNLIKELY((aFormat.c_str()) == NULL || *(aFormat.c_str()) == '\0'))
297 return setError(E_INVALIDARG, tr("No drop format specified"));
298
299 AutoCaller autoCaller(this);
300 if (FAILED(autoCaller.rc())) return autoCaller.rc();
301
302 uint32_t uAction = GuestDnD::toHGCMAction(aAction);
303 /* If there is no usable action, ignore this request. */
304 if (isDnDIgnoreAction(uAction))
305 return S_OK;
306
307 /* Note: At the moment we only support one transfer at a time. */
308 if (ASMAtomicReadBool(&mData.mfDropIsPending))
309 return setError(E_INVALIDARG, tr("Another drop operation already is in progress"));
310
311 ASMAtomicWriteBool(&mData.mfDropIsPending, true);
312
313 /* Dito. */
314 GuestDnDResponse *pResp = GuestDnDInst()->response();
315 AssertPtr(pResp);
316
317 HRESULT hr = pResp->resetProgress(m_pGuest);
318 if (FAILED(hr))
319 return hr;
320
321 try
322 {
323 mData.mRecvCtx.mIsActive = false;
324 mData.mRecvCtx.mpSource = this;
325 mData.mRecvCtx.mpResp = pResp;
326 mData.mRecvCtx.mFormat = aFormat;
327
328 RecvDataTask *pTask = new RecvDataTask(this, &mData.mRecvCtx);
329 AssertReturn(pTask->isOk(), pTask->getRC());
330
331 RTTHREAD recvThread;
332 int rc = RTThreadCreate(&recvThread, GuestDnDSource::i_receiveDataThread,
333 (void *)pTask, 0, RTTHREADTYPE_MAIN_WORKER, 0, "dndSrcRcvData");
334 if (RT_SUCCESS(rc))
335 {
336 hr = pResp->queryProgressTo(aProgress.asOutParam());
337 ComAssertComRC(hr);
338
339 /* Note: pTask is now owned by the worker thread. */
340 }
341 else
342 hr = setError(VBOX_E_IPRT_ERROR, tr("Starting thread failed (%Rrc)"), rc);
343 }
344 catch(std::bad_alloc &)
345 {
346 hr = setError(E_OUTOFMEMORY);
347 }
348
349 /* Note: mData.mfDropIsPending will be set to false again by i_receiveDataThread. */
350
351 LogFlowFunc(("Returning hr=%Rhrc\n", hr));
352 return hr;
353#endif /* VBOX_WITH_DRAG_AND_DROP */
354}
355
356HRESULT GuestDnDSource::receiveData(std::vector<BYTE> &aData)
357{
358#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
359 ReturnComNotImplemented();
360#else /* VBOX_WITH_DRAG_AND_DROP */
361
362 /* Input validation. */
363
364 AutoCaller autoCaller(this);
365 if (FAILED(autoCaller.rc())) return autoCaller.rc();
366
367 if (ASMAtomicReadBool(&mData.mfDropIsPending))
368 return setError(E_INVALIDARG, tr("Current drop operation still running"));
369
370 PRECVDATACTX pCtx = &mData.mRecvCtx;
371
372 if (pCtx->mData.vecData.empty())
373 {
374 aData.resize(0);
375 return S_OK;
376 }
377
378 HRESULT hr = S_OK;
379 size_t cbData;
380
381 try
382 {
383 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFormat.c_str(), pCtx->mFormat.length());
384 if (fHasURIList)
385 {
386 Utf8Str strURIs = pCtx->mURI.lstURI.RootToString(pCtx->mURI.strDropDir);
387 cbData = strURIs.length();
388
389 LogFlowFunc(("Found %zu root URIs (%zu bytes)\n", pCtx->mURI.lstURI.RootCount(), cbData));
390
391 aData.resize(cbData + 1 /* Include termination */);
392 memcpy(&aData.front(), strURIs.c_str(), cbData);
393 }
394 else
395 {
396 cbData = pCtx->mData.vecData.size();
397
398 /* Copy the data into a safe array of bytes. */
399 aData.resize(cbData);
400 memcpy(&aData.front(), &pCtx->mData.vecData[0], cbData);
401 }
402 }
403 catch (std::bad_alloc &)
404 {
405 hr = E_OUTOFMEMORY;
406 }
407
408 LogFlowFunc(("Returning cbData=%zu, hr=%Rhrc\n", cbData, hr));
409 return hr;
410#endif /* VBOX_WITH_DRAG_AND_DROP */
411}
412
413// implementation of internal methods.
414/////////////////////////////////////////////////////////////////////////////
415
416#ifdef VBOX_WITH_DRAG_AND_DROP_GH
417int GuestDnDSource::i_onReceiveData(PRECVDATACTX pCtx, const void *pvData, uint32_t cbData, uint64_t cbTotalSize)
418{
419 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
420 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
421 AssertReturn(cbData, VERR_INVALID_PARAMETER);
422 AssertReturn(cbTotalSize, VERR_INVALID_PARAMETER);
423
424 LogFlowFunc(("cbData=%RU32, cbTotalSize=%RU64\n", cbData, cbTotalSize));
425
426 int rc = VINF_SUCCESS;
427
428 try
429 {
430 if ( cbData > cbTotalSize
431 || cbData > mData.mcbBlockSize)
432 {
433 LogFlowFunc(("Data sizes invalid: cbData=%RU32, cbTotalSize=%RU64\n", cbData, cbTotalSize));
434 rc = VERR_INVALID_PARAMETER;
435 }
436 else if (cbData < pCtx->mData.vecData.size())
437 {
438 AssertMsgFailed(("New size (%RU64) is smaller than current size (%zu)\n", cbTotalSize, pCtx->mData.vecData.size()));
439 rc = VERR_INVALID_PARAMETER;
440 }
441
442 if (RT_SUCCESS(rc))
443 {
444 pCtx->mData.vecData.insert(pCtx->mData.vecData.begin(), (BYTE *)pvData, (BYTE *)pvData + cbData);
445
446 LogFlowFunc(("vecDataSize=%zu, cbData=%RU32, cbTotalSize=%RU64\n", pCtx->mData.vecData.size(), cbData, cbTotalSize));
447
448 /* Data transfer complete? */
449 Assert(cbData <= pCtx->mData.vecData.size());
450 if (cbData == pCtx->mData.vecData.size())
451 {
452 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFormat.c_str(), pCtx->mFormat.length());
453 LogFlowFunc(("fHasURIList=%RTbool, cbTotalSize=%RU32\n", fHasURIList, cbTotalSize));
454 if (fHasURIList)
455 {
456 /* Try parsing the data as URI list. */
457 rc = pCtx->mURI.lstURI.RootFromURIData(&pCtx->mData.vecData[0], pCtx->mData.vecData.size(), 0 /* uFlags */);
458 if (RT_SUCCESS(rc))
459 {
460 pCtx->mData.cbProcessed = 0;
461
462 /*
463 * Assign new total size which also includes all file data to receive
464 * from the guest.
465 */
466 pCtx->mData.cbToProcess = cbTotalSize;
467
468 LogFlowFunc(("URI data => cbToProcess=%RU64\n", pCtx->mData.cbToProcess));
469 }
470 }
471 }
472 }
473 }
474 catch (std::bad_alloc &)
475 {
476 rc = VERR_NO_MEMORY;
477 }
478
479 LogFlowFuncLeaveRC(rc);
480 return rc;
481}
482
483int GuestDnDSource::i_onReceiveDir(PRECVDATACTX pCtx, const char *pszPath, uint32_t cbPath, uint32_t fMode)
484{
485 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
486 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
487 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
488
489 LogFlowFunc(("pszPath=%s, cbPath=%RU32, fMode=0x%x\n", pszPath, cbPath, fMode));
490
491 int rc;
492 char *pszDir = RTPathJoinA(pCtx->mURI.strDropDir.c_str(), pszPath);
493 if (pszDir)
494 {
495 rc = RTDirCreateFullPath(pszDir, fMode);
496 if (RT_FAILURE(rc))
497 LogRel2(("DnD: Error creating guest directory \"%s\" on the host, rc=%Rrc\n", pszDir, rc));
498
499 RTStrFree(pszDir);
500 }
501 else
502 rc = VERR_NO_MEMORY;
503
504 if (RT_SUCCESS(rc))
505 {
506 if (mDataBase.mProtocolVersion <= 2)
507 {
508 /*
509 * BUG: Protocol v1 does *not* send root directory names in URI format,
510 * however, if this is a root URI directory (which came with the initial
511 * GUEST_DND_GH_SND_DATA message(s)) the total data announced was for
512 * root directory names which came in URI format, as an URI list.
513 *
514 * So construct an URI path locally to keep the accounting right.
515 */
516 char *pszPathURI = RTUriCreate("file" /* pszScheme */, "/" /* pszAuthority */,
517 pszPath /* pszPath */,
518 NULL /* pszQuery */, NULL /* pszFragment */);
519 if (pszPathURI)
520 {
521 bool fHasPath = RTPathHasPath(pszPath); /* Use original data received. */
522 if (!fHasPath) /* Root path? */
523 {
524 cbPath = strlen(pszPathURI);
525 cbPath += 3; /* Include "\r" + "\n" + termination -- see above. */
526
527 rc = i_updateProcess(pCtx, cbPath);
528 }
529
530 LogFlowFunc(("URI pszPathURI=%s, fHasPath=%RTbool, cbPath=%RU32\n", pszPathURI, fHasPath, cbPath));
531 RTStrFree(pszPathURI);
532 }
533 else
534 rc = VERR_NO_MEMORY;
535 }
536 }
537
538 LogFlowFuncLeaveRC(rc);
539 return rc;
540}
541
542int GuestDnDSource::i_onReceiveFileHdr(PRECVDATACTX pCtx, const char *pszPath, uint32_t cbPath,
543 uint64_t cbSize, uint32_t fMode, uint32_t fFlags)
544{
545 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
546 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
547 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
548 AssertReturn(fMode, VERR_INVALID_PARAMETER);
549 /* fFlags are optional. */
550
551 LogFlowFunc(("pszPath=%s, cbPath=%RU32, cbSize=%RU64, fMode=0x%x, fFlags=0x%x\n", pszPath, cbPath, cbSize, fMode, fFlags));
552
553 int rc = VINF_SUCCESS;
554
555 do
556 {
557 if (!pCtx->mURI.objURI.IsComplete())
558 {
559 LogFlowFunc(("Warning: Object \"%s\" not complete yet\n", pCtx->mURI.objURI.GetDestPath().c_str()));
560 rc = VERR_INVALID_PARAMETER;
561 break;
562 }
563
564 if (pCtx->mURI.objURI.IsOpen()) /* File already opened? */
565 {
566 LogFlowFunc(("Warning: Current opened object is \"%s\"\n", pCtx->mURI.objURI.GetDestPath().c_str()));
567 rc = VERR_WRONG_ORDER;
568 break;
569 }
570
571 char pszPathAbs[RTPATH_MAX];
572 rc = RTPathJoin(pszPathAbs, sizeof(pszPathAbs), pCtx->mURI.strDropDir.c_str(), pszPath);
573 if (RT_FAILURE(rc))
574 {
575 LogFlowFunc(("Warning: Rebasing current file failed with rc=%Rrc\n", rc));
576 break;
577 }
578
579 rc = DnDPathSanitize(pszPathAbs, sizeof(pszPathAbs));
580 if (RT_FAILURE(rc))
581 {
582 LogFlowFunc(("Warning: Rebasing current file failed with rc=%Rrc\n", rc));
583 break;
584 }
585
586 LogFunc(("Rebased to: %s\n", pszPathAbs));
587
588 /** @todo Add sparse file support based on fFlags? (Use Open(..., fFlags | SPARSE). */
589 /** @todo Add fMode to opening flags. */
590 rc = pCtx->mURI.objURI.OpenEx(pszPathAbs, DnDURIObject::File, DnDURIObject::Target,
591 RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
592 if (RT_SUCCESS(rc))
593 {
594 /** @todo Unescpae path before printing. */
595 LogRel2(("DnD: Transferring file to host: %s\n", pCtx->mURI.objURI.GetDestPath().c_str()));
596
597 /* Note: Protocol v1 does not send any file sizes, so always 0. */
598 if (mDataBase.mProtocolVersion >= 2)
599 rc = pCtx->mURI.objURI.SetSize(cbSize);
600
601 if (!cbSize) /* 0-byte file? Close again. */
602 pCtx->mURI.objURI.Close();
603 }
604 else
605 {
606 LogRel2(("DnD: Error opening/creating guest file \"%s\" on host, rc=%Rrc\n",
607 pCtx->mURI.objURI.GetDestPath().c_str(), rc));
608 break;
609 }
610
611 } while (0);
612
613 LogFlowFuncLeaveRC(rc);
614 return rc;
615}
616
617int GuestDnDSource::i_onReceiveFileData(PRECVDATACTX pCtx, const void *pvData, uint32_t cbData)
618{
619 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
620 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
621 AssertReturn(cbData, VERR_INVALID_PARAMETER);
622
623 int rc = VINF_SUCCESS;
624
625 do
626 {
627 if (pCtx->mURI.objURI.IsComplete())
628 {
629 LogFlowFunc(("Warning: Object \"%s\" already completed\n", pCtx->mURI.objURI.GetDestPath().c_str()));
630 rc = VERR_WRONG_ORDER;
631 break;
632 }
633
634 if (!pCtx->mURI.objURI.IsOpen()) /* File opened on host? */
635 {
636 LogFlowFunc(("Warning: Object \"%s\" not opened\n", pCtx->mURI.objURI.GetDestPath().c_str()));
637 rc = VERR_WRONG_ORDER;
638 break;
639 }
640
641 uint32_t cbWritten;
642 rc = pCtx->mURI.objURI.Write(pvData, cbData, &cbWritten);
643 if (RT_SUCCESS(rc))
644 {
645 Assert(cbWritten <= cbData);
646 if (cbWritten < cbData)
647 {
648 /** @todo What to do when the host's disk is full? */
649 rc = VERR_DISK_FULL;
650 }
651
652 if (RT_SUCCESS(rc))
653 rc = i_updateProcess(pCtx, cbWritten);
654 }
655
656 if (RT_SUCCESS(rc))
657 {
658 if (pCtx->mURI.objURI.IsComplete())
659 {
660 /** @todo Sanitize path. */
661 LogRel2(("DnD: File transfer to host complete: %s\n", pCtx->mURI.objURI.GetDestPath().c_str()));
662 rc = VINF_EOF;
663
664 /* Prepare URI object for next use. */
665 pCtx->mURI.objURI.Reset();
666 }
667 }
668 else
669 LogRel(("DnD: Error: Can't write guest file to host to \"%s\": %Rrc\n", pCtx->mURI.objURI.GetDestPath().c_str(), rc));
670
671 } while (0);
672
673 LogFlowFuncLeaveRC(rc);
674 return rc;
675}
676#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
677
678int GuestDnDSource::i_receiveData(PRECVDATACTX pCtx, RTMSINTERVAL msTimeout)
679{
680 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
681
682 GuestDnD *pInst = GuestDnDInst();
683 if (!pInst)
684 return VERR_INVALID_POINTER;
685
686 GuestDnDResponse *pResp = pCtx->mpResp;
687 AssertPtr(pCtx->mpResp);
688
689 /* Is this context already in receiving state? */
690 if (ASMAtomicReadBool(&pCtx->mIsActive))
691 return VERR_WRONG_ORDER;
692
693 ASMAtomicWriteBool(&pCtx->mIsActive, true);
694
695 int rc = pCtx->mCallback.Reset();
696 if (RT_FAILURE(rc))
697 return rc;
698
699 /*
700 * Reset any old data.
701 */
702 pCtx->mData.vecData.clear();
703 pCtx->mData.cbToProcess = 0;
704 pCtx->mData.cbProcessed = 0;
705
706 pResp->reset();
707 pResp->resetProgress(m_pGuest);
708
709 /* Set the format we are going to retrieve to have it around
710 * when retrieving the data later. */
711 pResp->setFormat(pCtx->mFormat);
712
713 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFormat.c_str(), pCtx->mFormat.length());
714 LogFlowFunc(("strFormat=%s, uAction=0x%x, fHasURIList=%RTbool\n", pCtx->mFormat.c_str(), pCtx->mAction, fHasURIList));
715 if (fHasURIList)
716 {
717 rc = i_receiveURIData(pCtx, msTimeout);
718 }
719 else
720 {
721 rc = i_receiveRawData(pCtx, msTimeout);
722 }
723
724 ASMAtomicWriteBool(&pCtx->mIsActive, false);
725
726 LogFlowFuncLeaveRC(rc);
727 return rc;
728}
729
730/* static */
731DECLCALLBACK(int) GuestDnDSource::i_receiveDataThread(RTTHREAD Thread, void *pvUser)
732{
733 LogFlowFunc(("pvUser=%p\n", pvUser));
734
735 RecvDataTask *pTask = (RecvDataTask *)pvUser;
736 AssertPtrReturn(pTask, VERR_INVALID_POINTER);
737
738 const ComObjPtr<GuestDnDSource> pSource(pTask->getSource());
739 Assert(!pSource.isNull());
740
741 int rc;
742
743 AutoCaller autoCaller(pSource);
744 if (SUCCEEDED(autoCaller.rc()))
745 {
746 rc = pSource->i_receiveData(pTask->getCtx(), RT_INDEFINITE_WAIT);
747 }
748 else
749 rc = VERR_COM_INVALID_OBJECT_STATE;
750
751 LogFlowFunc(("pSource=%p returning rc=%Rrc\n", (GuestDnDSource *)pSource, rc));
752
753 if (pTask)
754 delete pTask;
755
756 ASMAtomicWriteBool(&pSource->mData.mfDropIsPending, false);
757
758 return rc;
759}
760
761int GuestDnDSource::i_receiveRawData(PRECVDATACTX pCtx, RTMSINTERVAL msTimeout)
762{
763 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
764
765 int rc;
766
767 GuestDnDResponse *pResp = pCtx->mpResp;
768 AssertPtr(pCtx->mpResp);
769
770 GuestDnD *pInst = GuestDnDInst();
771 if (!pInst)
772 return VERR_INVALID_POINTER;
773
774#define REGISTER_CALLBACK(x) \
775 rc = pResp->setCallback(x, i_receiveRawDataCallback, pCtx); \
776 if (RT_FAILURE(rc)) \
777 return rc;
778
779#define UNREGISTER_CALLBACK(x) \
780 rc = pCtx->mpResp->setCallback(x, NULL); \
781 AssertRC(rc);
782
783 /*
784 * Register callbacks.
785 */
786 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
787 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
788
789 do
790 {
791 /*
792 * Receive the raw data.
793 */
794 GuestDnDMsg Msg;
795 Msg.setType(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED);
796 Msg.setNextPointer((void*)pCtx->mFormat.c_str(), (uint32_t)pCtx->mFormat.length() + 1);
797 Msg.setNextUInt32((uint32_t)pCtx->mFormat.length() + 1);
798 Msg.setNextUInt32(pCtx->mAction);
799
800 /* Make the initial call to the guest by telling that we initiated the "dropped" event on
801 * the host and therefore now waiting for the actual raw data. */
802 rc = pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
803 if (RT_SUCCESS(rc))
804 {
805 /*
806 * Wait until our callback i_receiveRawDataCallback triggered the
807 * wait event.
808 */
809 LogFlowFunc(("Waiting for raw data callback (%RU32ms timeout) ...\n", msTimeout));
810 rc = pCtx->mCallback.Wait(msTimeout);
811 LogFlowFunc(("Raw callback done, rc=%Rrc\n", rc));
812 }
813
814 } while (0);
815
816 /*
817 * Unregister callbacks.
818 */
819 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
820 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
821
822#undef REGISTER_CALLBACK
823#undef UNREGISTER_CALLBACK
824
825 LogFlowFuncLeaveRC(rc);
826 return rc;
827}
828
829int GuestDnDSource::i_receiveURIData(PRECVDATACTX pCtx, RTMSINTERVAL msTimeout)
830{
831 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
832
833 int rc;
834
835 GuestDnDResponse *pResp = pCtx->mpResp;
836 AssertPtr(pCtx->mpResp);
837
838 GuestDnD *pInst = GuestDnDInst();
839 if (!pInst)
840 return VERR_INVALID_POINTER;
841
842#define REGISTER_CALLBACK(x) \
843 rc = pResp->setCallback(x, i_receiveURIDataCallback, pCtx); \
844 if (RT_FAILURE(rc)) \
845 return rc;
846
847#define UNREGISTER_CALLBACK(x) \
848 rc = pResp->setCallback(x, NULL); \
849 AssertRC(rc);
850
851 /*
852 * Register callbacks.
853 */
854 /* Guest callbacks. */
855 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
856 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
857 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DIR);
858 if (mDataBase.mProtocolVersion >= 2)
859 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR);
860 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA);
861
862 do
863 {
864 char szDropDir[RTPATH_MAX];
865 rc = DnDDirCreateDroppedFiles(szDropDir, sizeof(szDropDir));
866 LogFlowFunc(("rc=%Rrc, szDropDir=%s\n", rc, szDropDir));
867 if (RT_FAILURE(rc))
868 break;
869
870 pCtx->mURI.strDropDir = szDropDir; /** @todo Keep directory handle open? */
871
872 /*
873 * Receive the URI list.
874 */
875 GuestDnDMsg Msg;
876 Msg.setType(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED);
877 Msg.setNextPointer((void*)pCtx->mFormat.c_str(), (uint32_t)pCtx->mFormat.length() + 1);
878 Msg.setNextUInt32((uint32_t)pCtx->mFormat.length() + 1);
879 Msg.setNextUInt32(pCtx->mAction);
880
881 /* Make the initial call to the guest by telling that we initiated the "dropped" event on
882 * the host and therefore now waiting for the actual URI actual data. */
883 rc = pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
884 if (RT_SUCCESS(rc))
885 {
886 /*
887 * Wait until our callback i_receiveURIDataCallback triggered the
888 * wait event.
889 */
890 LogFlowFunc(("Waiting for URI callback (%RU32ms timeout) ...\n", msTimeout));
891 rc = pCtx->mCallback.Wait(msTimeout);
892 LogFlowFunc(("URI callback done, rc=%Rrc\n", rc));
893 }
894
895 } while (0);
896
897 /*
898 * Unregister callbacks.
899 */
900 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
901 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
902 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DIR);
903 if (mDataBase.mProtocolVersion >= 2)
904 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR);
905 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA);
906
907#undef REGISTER_CALLBACK
908#undef UNREGISTER_CALLBACK
909
910 if (RT_FAILURE(rc))
911 {
912 LogFlowFunc(("Rolling back ...\n"));
913
914 /* Rollback by removing any stuff created. */
915 for (size_t i = 0; i < pCtx->mURI.lstFiles.size(); ++i)
916 RTFileDelete(pCtx->mURI.lstFiles.at(i).c_str());
917 for (size_t i = 0; i < pCtx->mURI.lstDirs.size(); ++i)
918 RTDirRemove(pCtx->mURI.lstDirs.at(i).c_str());
919 }
920
921 /* Try removing (hopefully) empty drop directory in any case. */
922 if (pCtx->mURI.strDropDir.isNotEmpty())
923 RTDirRemove(pCtx->mURI.strDropDir.c_str());
924
925 LogFlowFuncLeaveRC(rc);
926 return rc;
927}
928
929/* static */
930DECLCALLBACK(int) GuestDnDSource::i_receiveRawDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
931{
932 PRECVDATACTX pCtx = (PRECVDATACTX)pvUser;
933 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
934
935 GuestDnDSource *pThis = pCtx->mpSource;
936 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
937
938 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
939
940 int rc = VINF_SUCCESS;
941 bool fNotify = false;
942
943 switch (uMsg)
944 {
945#ifdef VBOX_WITH_DRAG_AND_DROP_GH
946 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
947 {
948 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
949 AssertPtr(pCBData);
950 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
951 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
952
953 rc = pThis->i_onReceiveData(pCtx, pCBData->pvData, pCBData->cbData, pCBData->cbTotalSize);
954 break;
955 }
956 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
957 {
958 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
959 AssertPtr(pCBData);
960 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
961 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
962
963 pCtx->mpResp->reset();
964 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc);
965 if (RT_SUCCESS(rc))
966 rc = pCBData->rc;
967 break;
968 }
969#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
970 default:
971 rc = VERR_NOT_SUPPORTED;
972 break;
973 }
974
975 if (RT_FAILURE(rc))
976 fNotify = true;
977
978 if (fNotify)
979 {
980 int rc2 = pCtx->mCallback.Notify(rc);
981 AssertRC(rc2);
982 }
983
984 LogFlowFuncLeaveRC(rc);
985 return rc; /* Tell the guest. */
986}
987
988/* static */
989DECLCALLBACK(int) GuestDnDSource::i_receiveURIDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
990{
991 PRECVDATACTX pCtx = (PRECVDATACTX)pvUser;
992 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
993
994 GuestDnDSource *pThis = pCtx->mpSource;
995 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
996
997 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
998
999 int rc = VINF_SUCCESS;
1000 bool fNotify = false;
1001
1002 switch (uMsg)
1003 {
1004#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1005 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
1006 {
1007 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
1008 AssertPtr(pCBData);
1009 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1010 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1011
1012 rc = pThis->i_onReceiveData(pCtx, pCBData->pvData, pCBData->cbData, pCBData->cbTotalSize);
1013 break;
1014 }
1015 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
1016 {
1017 DragAndDropSvc::PVBOXDNDCBSNDDIRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDIRDATA>(pvParms);
1018 AssertPtr(pCBData);
1019 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDIRDATA) == cbParms, VERR_INVALID_PARAMETER);
1020 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1021
1022 rc = pThis->i_onReceiveDir(pCtx, pCBData->pszPath, pCBData->cbPath, pCBData->fMode);
1023 break;
1024 }
1025 case DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR:
1026 {
1027 DragAndDropSvc::PVBOXDNDCBSNDFILEHDRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEHDRDATA>(pvParms);
1028 AssertPtr(pCBData);
1029 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEHDRDATA) == cbParms, VERR_INVALID_PARAMETER);
1030 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE_HDR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1031
1032 rc = pThis->i_onReceiveFileHdr(pCtx, pCBData->pszFilePath, pCBData->cbFilePath,
1033 pCBData->cbSize, pCBData->fMode, pCBData->fFlags);
1034 break;
1035 }
1036 case DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA:
1037 {
1038 DragAndDropSvc::PVBOXDNDCBSNDFILEDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEDATADATA>(pvParms);
1039 AssertPtr(pCBData);
1040 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1041 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1042
1043 if (pThis->mDataBase.mProtocolVersion <= 1)
1044 {
1045 /**
1046 * Notes for protocol v1 (< VBox 5.0):
1047 * - Every time this command is being sent it includes the file header,
1048 * so just process both calls here.
1049 * - There was no information whatsoever about the total file size; the old code only
1050 * appended data to the desired file. So just pass 0 as cbSize.
1051 */
1052 rc = pThis->i_onReceiveFileHdr(pCtx,
1053 pCBData->u.v1.pszFilePath, pCBData->u.v1.cbFilePath,
1054 0 /* cbSize */, pCBData->u.v1.fMode, 0 /* fFlags */);
1055 if (RT_SUCCESS(rc))
1056 rc = pThis->i_onReceiveFileData(pCtx, pCBData->pvData, pCBData->cbData);
1057 }
1058 else /* Protocol v2 and up. */
1059 rc = pThis->i_onReceiveFileData(pCtx, pCBData->pvData, pCBData->cbData);
1060 break;
1061 }
1062 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
1063 {
1064 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
1065 AssertPtr(pCBData);
1066 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
1067 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1068
1069 pCtx->mpResp->reset();
1070 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc);
1071 if (RT_SUCCESS(rc))
1072 rc = pCBData->rc;
1073 break;
1074 }
1075#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1076 default:
1077 rc = VERR_NOT_SUPPORTED;
1078 break;
1079 }
1080
1081 if (RT_FAILURE(rc))
1082 fNotify = true;
1083
1084 /* All URI data processed? */
1085 if (pCtx->mData.cbProcessed >= pCtx->mData.cbToProcess)
1086 {
1087 Assert(pCtx->mData.cbProcessed == pCtx->mData.cbToProcess);
1088 fNotify = true;
1089 }
1090
1091 LogFlowFunc(("cbProcessed=%RU64, cbToProcess=%RU64, fNotify=%RTbool\n",
1092 pCtx->mData.cbProcessed, pCtx->mData.cbToProcess, fNotify));
1093
1094 if (fNotify)
1095 {
1096 int rc2 = pCtx->mCallback.Notify(rc);
1097 AssertRC(rc2);
1098 }
1099
1100 LogFlowFuncLeaveRC(rc);
1101 return rc; /* Tell the guest. */
1102}
1103
1104int GuestDnDSource::i_updateProcess(PRECVDATACTX pCtx, uint32_t cbDataAdd)
1105{
1106 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1107
1108 pCtx->mData.cbProcessed += cbDataAdd;
1109 Assert(pCtx->mData.cbProcessed <= pCtx->mData.cbToProcess);
1110
1111 int64_t cbTotal = pCtx->mData.cbToProcess;
1112 uint8_t uPercent = pCtx->mData.cbProcessed * 100 / (cbTotal ? cbTotal : 1);
1113
1114 int rc = pCtx->mpResp->setProgress(uPercent,
1115 uPercent >= 100
1116 ? DragAndDropSvc::DND_PROGRESS_COMPLETE
1117 : DragAndDropSvc::DND_PROGRESS_RUNNING);
1118 return rc;
1119}
1120
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