VirtualBox

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

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

DnD: Bugfixes, cleanup, spelling.

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