Changeset 55422 in vbox for trunk/src/VBox/Main/src-client
- Timestamp:
- Apr 24, 2015 1:52:33 PM (10 years ago)
- Location:
- trunk/src/VBox/Main/src-client
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/src-client/GuestDnDPrivate.cpp
r55244 r55422 6 6 7 7 /* 8 * Copyright (C) 2011-201 4Oracle Corporation8 * Copyright (C) 2011-2015 Oracle Corporation 9 9 * 10 10 * This file is part of VirtualBox Open Source Edition (OSE), as … … 37 37 # include <VBox/GuestHost/DragAndDrop.h> 38 38 # include <VBox/HostServices/DragAndDropSvc.h> 39 # include <VBox/version.h> 39 40 40 41 # ifdef LOG_GROUP … … 167 168 , m_cbDataCurrent(0) 168 169 , m_cbDataTotal(0) 169 , m_hFile(NIL_RTFILE)170 170 , m_parent(pGuest) 171 171 { 172 172 int rc = RTSemEventCreate(&m_EventSem); 173 AssertRC(rc); 173 if (RT_FAILURE(rc)) 174 throw rc; 174 175 } 175 176 … … 242 243 } 243 244 244 int GuestDnDResponse::notifyAboutGuestResponse(void) 245 int GuestDnDResponse::notifyAboutGuestResponse(void) const 245 246 { 246 247 return RTSemEventSignal(m_EventSem); … … 262 263 m_pvData = NULL; 263 264 } 264 m_cbData = 0; 265 266 m_cbData = 0; 265 267 m_cbDataCurrent = 0; 266 m_cbDataTotal = 0; 267 268 if (m_hFile != NIL_RTFILE) 269 { 270 RTFileClose(m_hFile); 271 m_hFile = NIL_RTFILE; 272 } 273 m_strFile = ""; 268 m_cbDataTotal = 0; 274 269 } 275 270 … … 280 275 if (SUCCEEDED(rc)) 281 276 { 282 rc = m_progress->init(static_cast<IGuest *>(pParent),277 rc = m_progress->init(static_cast<IGuest *>(pParent), 283 278 Bstr(pParent->tr("Dropping data")).raw(), 284 FALSE /* fCancelable */);279 TRUE /* aCancelable */); 285 280 } 286 281 return rc; 287 282 } 288 283 284 bool GuestDnDResponse::isProgressCanceled(void) const 285 { 286 BOOL fCanceled; 287 if (!m_progress.isNull()) 288 { 289 HRESULT hr = m_progress->COMGETTER(Canceled)(&fCanceled); 290 AssertComRC(hr); 291 } 292 else fCanceled = TRUE; 293 294 return RT_BOOL(fCanceled); 295 } 296 297 int GuestDnDResponse::setCallback(uint32_t uMsg, PFNGUESTDNDCALLBACK pfnCallback, void *pvUser /* = NULL */) 298 { 299 GuestDnDCallbackMap::const_iterator it = m_mapCallbacks.find(uMsg); 300 301 /* Add. */ 302 if (pfnCallback) 303 { 304 if (it == m_mapCallbacks.end()) 305 { 306 m_mapCallbacks[uMsg] = GuestDnDCallback(pfnCallback, uMsg, pvUser); 307 return VINF_SUCCESS; 308 } 309 310 AssertMsgFailed(("Callback for message %RU32 already registered\n", uMsg)); 311 return VERR_ALREADY_EXISTS; 312 } 313 314 /* Remove. */ 315 if (it != m_mapCallbacks.end()) 316 m_mapCallbacks.erase(it); 317 318 return VINF_SUCCESS; 319 } 320 289 321 int GuestDnDResponse::setProgress(unsigned uPercentage, 290 uint32_t uStat e, int rcOp /* = VINF_SUCCESS */)291 { 292 LogFlowFunc(("u Percentage=%RU32, uState=%RU32, rcOp=%Rrc\n",293 u Percentage, uState, rcOp));322 uint32_t uStatus, int rcOp /* = VINF_SUCCESS */) 323 { 324 LogFlowFunc(("uStatus=%RU32, uPercentage=%RU32, rcOp=%Rrc\n", 325 uStatus, uPercentage, rcOp)); 294 326 295 327 int vrc = VINF_SUCCESS; … … 298 330 BOOL fCompleted; 299 331 HRESULT hr = m_progress->COMGETTER(Completed)(&fCompleted); 332 AssertComRC(hr); 333 334 BOOL fCanceled; 335 hr = m_progress->COMGETTER(Canceled)(&fCanceled); 336 AssertComRC(hr); 337 338 LogFlowFunc(("fCompleted=%RTbool, fCanceled=%RTbool\n", fCompleted, fCanceled)); 339 300 340 if (!fCompleted) 301 341 { 302 if (uState == DragAndDropSvc::DND_PROGRESS_ERROR)342 switch (uStatus) 303 343 { 304 hr = m_progress->i_notifyComplete(VBOX_E_IPRT_ERROR, 305 COM_IIDOF(IGuest), 306 m_parent->getComponentName(), 307 GuestDnDResponse::errorToString(m_parent, rcOp).c_str()); 308 reset(); 309 } 310 else if (uState == DragAndDropSvc::DND_PROGRESS_CANCELLED) 311 { 312 hr = m_progress->Cancel(); 313 if (SUCCEEDED(hr)) 314 vrc = VERR_CANCELLED; 315 316 reset(); 317 } 318 else /* uState == DragAndDropSvc::DND_PROGRESS_RUNNING */ 319 { 320 hr = m_progress->SetCurrentOperationProgress(uPercentage); 321 AssertComRC(hr); 322 if ( uState == DragAndDropSvc::DND_PROGRESS_COMPLETE 323 || uPercentage >= 100) 344 case DragAndDropSvc::DND_PROGRESS_ERROR: 345 { 346 hr = m_progress->i_notifyComplete(VBOX_E_IPRT_ERROR, 347 COM_IIDOF(IGuest), 348 m_parent->getComponentName(), 349 GuestDnDResponse::errorToString(m_parent, rcOp).c_str()); 350 reset(); 351 break; 352 } 353 354 case DragAndDropSvc::DND_PROGRESS_CANCELLED: 355 { 324 356 hr = m_progress->i_notifyComplete(S_OK); 357 AssertComRC(hr); 358 359 reset(); 360 break; 361 } 362 363 case DragAndDropSvc::DND_PROGRESS_RUNNING: 364 case DragAndDropSvc::DND_PROGRESS_COMPLETE: 365 { 366 if (!fCanceled) 367 { 368 hr = m_progress->SetCurrentOperationProgress(uPercentage); 369 AssertComRC(hr); 370 if ( uStatus == DragAndDropSvc::DND_PROGRESS_COMPLETE 371 || uPercentage >= 100) 372 { 373 hr = m_progress->i_notifyComplete(S_OK); 374 AssertComRC(hr); 375 } 376 } 377 break; 378 } 379 380 default: 381 break; 325 382 } 326 383 } 327 384 } 328 385 386 LogFlowFuncLeaveRC(vrc); 329 387 return vrc; 330 388 } … … 348 406 /** @todo Don't use anonymous enums (uint32_t). */ 349 407 uint32_t uStatus = DragAndDropSvc::DND_PROGRESS_RUNNING; 350 if (m_cbDataCurrent >= m_cbDataTotal) 351 uStatus = DragAndDropSvc::DND_PROGRESS_COMPLETE; 352 353 #ifdef DEBUG_andy 354 LogFlowFunc(("Updating transfer status (%zu/%zu), status=%ld\n", 355 m_cbDataCurrent, m_cbDataTotal, uStatus)); 356 #else 408 Assert(m_cbDataCurrent <= m_cbDataTotal); 409 if (m_cbDataCurrent >= m_cbDataTotal) uStatus = DragAndDropSvc::DND_PROGRESS_COMPLETE; 410 411 LogFlowFunc(("Updating transfer status (%zu/%zu), status=%ld\n", m_cbDataCurrent, m_cbDataTotal, uStatus)); 357 412 AssertMsg(m_cbDataCurrent <= m_cbDataTotal, 358 413 ("More data transferred (%zu) than initially announced (%zu), cbDataAdd=%zu\n", 359 m_cbDataCurrent, m_cbDataTotal, cbDataAdd));360 #endif 414 m_cbDataCurrent, m_cbDataTotal, cbDataAdd)); 415 361 416 int rc = setProgress(cPercentage, uStatus); 362 417 … … 364 419 * guest should first clean up stuff itself and than really confirm 365 420 * the cancel request by an extra message. */ 366 if (rc == VERR_CANCELLED) 367 rc = setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED); 421 if (rc == VERR_CANCELLED) rc = setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED); 368 422 369 423 LogFlowFuncLeaveRC(rc); … … 371 425 } 372 426 427 int GuestDnDResponse::onDispatch(uint32_t u32Function, void *pvParms, uint32_t cbParms) 428 { 429 LogFlowFunc(("u32Function=%RU32, pvParms=%p, cbParms=%RU32\n", u32Function, pvParms, cbParms)); 430 431 int rc = VERR_WRONG_ORDER; /* Play safe. */ 432 bool fTryCallbacks = false; 433 434 switch (u32Function) 435 { 436 case DragAndDropSvc::GUEST_DND_HG_ACK_OP: 437 { 438 DragAndDropSvc::PVBOXDNDCBHGACKOPDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGACKOPDATA>(pvParms); 439 AssertPtr(pCBData); 440 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGACKOPDATA) == cbParms, VERR_INVALID_PARAMETER); 441 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER); 442 443 setDefAction(pCBData->uAction); 444 rc = notifyAboutGuestResponse(); 445 break; 446 } 447 448 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA: 449 { 450 DragAndDropSvc::PVBOXDNDCBHGREQDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGREQDATADATA>(pvParms); 451 AssertPtr(pCBData); 452 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGREQDATADATA) == cbParms, VERR_INVALID_PARAMETER); 453 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER); 454 455 setFormat(pCBData->pszFormat); 456 rc = notifyAboutGuestResponse(); 457 break; 458 } 459 460 case DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS: 461 { 462 DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA pCBData = 463 reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA>(pvParms); 464 AssertPtr(pCBData); 465 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGEVTPROGRESSDATA) == cbParms, VERR_INVALID_PARAMETER); 466 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_EVT_PROGRESS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER); 467 468 rc = setProgress(pCBData->uPercentage, pCBData->uStatus, pCBData->rc); 469 if (RT_SUCCESS(rc)) 470 rc = notifyAboutGuestResponse(); 471 break; 472 } 473 #ifdef VBOX_WITH_DRAG_AND_DROP_GH 474 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING: 475 { 476 DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA pCBData = 477 reinterpret_cast<DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA>(pvParms); 478 AssertPtr(pCBData); 479 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA) == cbParms, VERR_INVALID_PARAMETER); 480 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER); 481 482 setFormat (pCBData->pszFormat); 483 setDefAction (pCBData->uDefAction); 484 setAllActions(pCBData->uAllActions); 485 486 rc = notifyAboutGuestResponse(); 487 break; 488 } 489 #endif /* VBOX_WITH_DRAG_AND_DROP_GH */ 490 default: 491 /* * Try if the event is covered by a registered callback. */ 492 fTryCallbacks = true; 493 break; 494 } 495 496 /* 497 * Try the host's installed callbacks (if any). 498 */ 499 if (fTryCallbacks) 500 { 501 GuestDnDCallbackMap::const_iterator it = m_mapCallbacks.find(u32Function); 502 if (it != m_mapCallbacks.end()) 503 { 504 AssertPtr(it->second.pfnCallback); 505 rc = it->second.pfnCallback(u32Function, pvParms, cbParms, it->second.pvUser); 506 } 507 else 508 rc = VERR_NO_DATA; /* Tell the guest. */ 509 } 510 511 LogFlowFunc(("Returning rc=%Rrc\n", rc)); 512 return rc; 513 } 514 373 515 HRESULT GuestDnDResponse::queryProgressTo(IProgress **ppProgress) 374 516 { … … 376 518 } 377 519 378 int GuestDnDResponse::waitForGuestResponse(RTMSINTERVAL msTimeout /*= 500 */) 520 int GuestDnDResponse::waitForGuestResponse(RTMSINTERVAL msTimeout /*= 500 */) const 379 521 { 380 522 int rc = RTSemEventWait(m_EventSem, msTimeout); … … 382 524 LogFlowFunc(("msTimeout=%RU32, rc=%Rrc\n", msTimeout, rc)); 383 525 #endif 384 return rc;385 }386 387 int GuestDnDResponse::writeToFile(const char *pszPath, size_t cbPath,388 void *pvData, size_t cbData, uint32_t fMode)389 {390 /** @todo Support locking more than one file at a time! We391 * might want to have a table in DnDGuestImpl which392 * keeps those file pointers around, or extend the393 * actual protocol for explicit open calls.394 *395 * For now we only keep one file open at a time, so if396 * a client does alternating writes to different files397 * this function will close the old and re-open the new398 * file on every call. */399 int rc;400 if ( m_hFile == NIL_RTFILE401 || m_strFile != pszPath)402 {403 char *pszFile = RTPathJoinA(m_strDropDir.c_str(), pszPath);404 if (pszFile)405 {406 RTFILE hFile;407 /** @todo Respect fMode! */408 rc = RTFileOpen(&hFile, pszFile,409 RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE410 | RTFILE_O_WRITE | RTFILE_O_APPEND);411 if (RT_SUCCESS(rc))412 {413 LogFlowFunc(("Opening \"%s\" (fMode=0x%x) for writing ...\n",414 pszFile, fMode));415 416 m_hFile = hFile;417 m_strFile = pszPath;418 }419 420 RTStrFree(pszFile);421 }422 else423 rc = VERR_NO_MEMORY;424 }425 else426 rc = VINF_SUCCESS;427 428 if (RT_SUCCESS(rc))429 {430 rc = RTFileWrite(m_hFile, pvData, cbData,431 NULL /* No partial writes */);432 433 if (RT_SUCCESS(rc))434 rc = dataSetStatus(cbData);435 }436 437 526 return rc; 438 527 } … … 507 596 return VERR_COM_OBJECT_NOT_FOUND; 508 597 509 int rc = pVMMDev->hgcmHostCall("VBoxDragAndDropSvc", u32Function, 510 cParms, paParms); 511 LogFlowFunc(("uMsg=%RU32, cParms=%RU32, rc=%Rrc\n", 512 u32Function, cParms, rc)); 598 int rc = pVMMDev->hgcmHostCall("VBoxDragAndDropSvc", u32Function, cParms, paParms); 599 LogFlowFunc(("uMsg=%RU32, cParms=%RU32, rc=%Rrc\n", u32Function, cParms, rc)); 513 600 return rc; 514 601 } 602 603 /* static */ 604 DECLCALLBACK(int) GuestDnD::notifyDnDDispatcher(void *pvExtension, uint32_t u32Function, 605 void *pvParms, uint32_t cbParms) 606 { 607 LogFlowFunc(("pvExtension=%p, u32Function=%RU32, pvParms=%p, cbParms=%RU32\n", 608 pvExtension, u32Function, pvParms, cbParms)); 609 610 GuestDnD *pGuestDnD = reinterpret_cast<GuestDnD*>(pvExtension); 611 AssertPtrReturn(pGuestDnD, VERR_INVALID_POINTER); 612 613 /** @todo In case we need to handle multiple guest DnD responses at a time this 614 * would be the place to lookup and dispatch to those. For the moment we 615 * only have one response -- simple. */ 616 GuestDnDResponse *pResp = pGuestDnD->m_pResponse; 617 if (pResp) 618 return pResp->onDispatch(u32Function, pvParms, cbParms); 619 620 return VERR_NOT_SUPPORTED; 621 } 622 515 623 516 624 /* static */ … … 637 745 } 638 746 639 /* static */640 DECLCALLBACK(int) GuestDnD::notifyDnDDispatcher(void *pvExtension, uint32_t u32Function,641 void *pvParms, uint32_t cbParms)642 {643 LogFlowFunc(("pvExtension=%p, u32Function=%RU32, pvParms=%p, cbParms=%RU32\n",644 pvExtension, u32Function, pvParms, cbParms));645 646 GuestDnD *pGuestDnD = reinterpret_cast<GuestDnD*>(pvExtension);647 AssertPtrReturn(pGuestDnD, VERR_INVALID_POINTER);648 649 GuestDnDResponse *pResp = pGuestDnD->m_pResponse;650 AssertPtrReturn(pResp, VERR_INVALID_POINTER);651 652 int rc;653 switch (u32Function)654 {655 case DragAndDropSvc::GUEST_DND_HG_ACK_OP:656 {657 DragAndDropSvc::PVBOXDNDCBHGACKOPDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGACKOPDATA>(pvParms);658 AssertPtr(pCBData);659 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGACKOPDATA) == cbParms, VERR_INVALID_PARAMETER);660 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);661 662 pResp->setDefAction(pCBData->uAction);663 664 rc = pResp->notifyAboutGuestResponse();665 break;666 }667 668 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:669 {670 DragAndDropSvc::PVBOXDNDCBHGREQDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGREQDATADATA>(pvParms);671 AssertPtr(pCBData);672 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGREQDATADATA) == cbParms, VERR_INVALID_PARAMETER);673 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);674 675 pResp->setFormat(pCBData->pszFormat);676 677 rc = pResp->notifyAboutGuestResponse();678 break;679 }680 681 case DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS:682 {683 DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA pCBData =684 reinterpret_cast <DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA>(pvParms);685 AssertPtr(pCBData);686 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGEVTPROGRESSDATA) == cbParms, VERR_INVALID_PARAMETER);687 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_EVT_PROGRESS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);688 689 rc = pResp->setProgress(pCBData->uPercentage, pCBData->uState, pCBData->rc);690 break;691 }692 693 # ifdef VBOX_WITH_DRAG_AND_DROP_GH694 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:695 {696 DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA pCBData =697 reinterpret_cast <DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA>(pvParms);698 AssertPtr(pCBData);699 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA) == cbParms, VERR_INVALID_PARAMETER);700 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);701 702 pResp->setFormat(pCBData->pszFormat);703 pResp->setDefAction(pCBData->uDefAction);704 pResp->setAllActions(pCBData->uAllActions);705 706 rc = pResp->notifyAboutGuestResponse();707 break;708 }709 710 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:711 {712 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);713 AssertPtr(pCBData);714 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);715 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);716 717 rc = pGuestDnD->onGHSendData(pResp, pCBData->pvData, pCBData->cbData,718 pCBData->cbTotalSize);719 break;720 }721 722 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:723 {724 DragAndDropSvc::PVBOXDNDCBSNDDIRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDIRDATA>(pvParms);725 AssertPtr(pCBData);726 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDIRDATA) == cbParms, VERR_INVALID_PARAMETER);727 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);728 729 rc = pGuestDnD->onGHSendDir(pResp, pCBData->pszPath, pCBData->cbPath, pCBData->fMode);730 break;731 }732 733 case DragAndDropSvc::GUEST_DND_GH_SND_FILE:734 {735 DragAndDropSvc::PVBOXDNDCBSNDFILEDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEDATA>(pvParms);736 AssertPtr(pCBData);737 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEDATA) == cbParms, VERR_INVALID_PARAMETER);738 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);739 740 rc = pGuestDnD->onGHSendFile(pResp, pCBData->pszFilePath, pCBData->cbFilePath,741 pCBData->pvData, pCBData->cbData, pCBData->fMode);742 break;743 }744 745 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:746 {747 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);748 AssertPtr(pCBData);749 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);750 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);751 752 /* Cleanup. */753 pResp->reset();754 rc = pResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc);755 break;756 }757 # endif /* VBOX_WITH_DRAG_AND_DROP_GH */758 default:759 rc = VERR_NOT_SUPPORTED; /* Tell the guest. */760 break;761 }762 763 LogFlowFunc(("Returning rc=%Rrc\n", rc));764 return rc;765 }766 767 # ifdef VBOX_WITH_DRAG_AND_DROP_GH768 int GuestDnD::onGHSendData(GuestDnDResponse *pResp,769 const void *pvData, size_t cbData, size_t cbTotalSize)770 {771 AssertPtrReturn(pResp, VERR_INVALID_POINTER);772 AssertPtrReturn(pvData, VERR_INVALID_POINTER);773 AssertReturn(cbData, VERR_INVALID_PARAMETER);774 AssertReturn(cbTotalSize, VERR_INVALID_PARAMETER);775 776 int rc = pResp->dataAdd(pvData, cbData, NULL /* Current size */);777 if (RT_SUCCESS(rc))778 rc = pResp->dataSetStatus(cbData, cbTotalSize);779 780 LogFlowFuncLeaveRC(rc);781 return rc;782 }783 784 int GuestDnD::onGHSendDir(GuestDnDResponse *pResp,785 const char *pszPath, size_t cbPath, uint32_t fMode)786 {787 AssertPtrReturn(pResp, VERR_INVALID_POINTER);788 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);789 AssertReturn(cbPath, VERR_INVALID_PARAMETER);790 791 LogFlowFunc(("pszPath=%s, cbPath=%zu, fMode=0x%x\n",792 pszPath, cbPath, fMode));793 794 int rc;795 char *pszDir = RTPathJoinA(pResp->dropDir().c_str(), pszPath);796 if (pszDir)797 {798 rc = RTDirCreateFullPath(pszDir, fMode);799 RTStrFree(pszDir);800 }801 else802 rc = VERR_NO_MEMORY;803 804 if (RT_SUCCESS(rc))805 rc = pResp->dataSetStatus(cbPath);806 807 LogFlowFuncLeaveRC(rc);808 return rc;809 }810 811 int GuestDnD::onGHSendFile(GuestDnDResponse *pResp,812 const char *pszPath, size_t cbPath,813 void *pvData, size_t cbData, uint32_t fMode)814 {815 AssertPtrReturn(pResp, VERR_INVALID_POINTER);816 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);817 AssertReturn(cbPath, VERR_INVALID_PARAMETER);818 819 LogFlowFunc(("pszPath=%s, cbPath=%zu, fMode=0x%x\n",820 pszPath, cbPath, fMode));821 822 int rc = pResp->writeToFile(pszPath, cbPath,823 pvData, cbData, fMode);824 LogFlowFuncLeaveRC(rc);825 return rc;826 }827 # endif /* VBOX_WITH_DRAG_AND_DROP_GH */828 829 747 /////////////////////////////////////////////////////////////////////////////// 830 748 … … 834 752 } 835 753 836 HRESULT GuestDnDBase::i sFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported)754 HRESULT GuestDnDBase::i_isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported) 837 755 { 838 756 *aSupported = std::find(m_strFormats.begin(), … … 842 760 } 843 761 844 HRESULT GuestDnDBase:: getFormats(std::vector<com::Utf8Str> &aFormats)762 HRESULT GuestDnDBase::i_getFormats(std::vector<com::Utf8Str> &aFormats) 845 763 { 846 764 aFormats = m_strFormats; … … 849 767 } 850 768 851 HRESULT GuestDnDBase:: addFormats(const std::vector<com::Utf8Str> &aFormats)769 HRESULT GuestDnDBase::i_addFormats(const std::vector<com::Utf8Str> &aFormats) 852 770 { 853 771 for (size_t i = 0; i < aFormats.size(); ++i) … … 864 782 } 865 783 866 HRESULT GuestDnDBase:: removeFormats(const std::vector<com::Utf8Str> &aFormats)784 HRESULT GuestDnDBase::i_removeFormats(const std::vector<com::Utf8Str> &aFormats) 867 785 { 868 786 for (size_t i = 0; i < aFormats.size(); ++i) … … 878 796 } 879 797 798 HRESULT GuestDnDBase::i_getProtocolVersion(ULONG *puVersion) 799 { 800 int rc = getProtocolVersion((uint32_t *)puVersion); 801 return RT_SUCCESS(rc) ? S_OK : E_FAIL; 802 } 803 804 int GuestDnDBase::getProtocolVersion(uint32_t *puVersion) 805 { 806 AssertPtrReturn(puVersion, VERR_INVALID_POINTER); 807 808 int rc; 809 810 uint32_t uVer, uVerAdditions = 0; 811 if ( m_pGuest 812 && (uVerAdditions = m_pGuest->i_getAdditionsVersion()) > 0) 813 { 814 uint32_t uVBoxMajor = VBOX_FULL_VERSION_GET_MAJOR(uVerAdditions); 815 uint32_t uVBoxMinor = VBOX_FULL_VERSION_GET_MINOR(uVerAdditions); 816 817 #if 0 /*def DEBUG_andy*/ 818 /* Hardcode the to-used protocol version; nice for testing side effects. */ 819 uVer = 2; 820 #else 821 uVer = ( uVBoxMajor >= 5) 822 ? 2 /* VBox 5.0 and up: Protocol version 2. */ 823 : 1; /* VBox <= 4.3: Protocol version 1. */ 824 /* Build revision is ignored. */ 825 #endif 826 827 LogFlowThisFunc(("uVerAdditions=%RU32 (%RU32.%RU32)\n", uVerAdditions, uVBoxMajor, uVBoxMinor)); 828 rc = VINF_SUCCESS; 829 } 830 else 831 { 832 uVer = 1; /* Fallback. */ 833 rc = VERR_NOT_FOUND; 834 } 835 836 LogFlowThisFunc(("uVer=%RU32, uVerAdditions=%RU32, rc=%Rrc\n", uVer, uVerAdditions, rc)); 837 838 *puVersion = uVer; 839 return rc; 840 } 841 842 #if 0 843 /** 844 * Returns back information (message type + parameter count) of the current message in 845 * the local outgoing message queue. 846 * 847 * @return IPRT status code. 848 * @param pThis 849 * @param puMsg 850 * @param pcParms 851 * @param paParms 852 */ 853 /* static */ 854 DECLCALLBACK(int) GuestDnDBase::i_getNextMsgCallback(GuestDnDBase *pThis, uint32_t *puMsg, 855 uint32_t *pcParms, PVBOXHGCMSVCPARM paParms) 856 { 857 AssertPtrReturn(pThis, VERR_INVALID_POINTER); 858 AssertPtrReturn(puMsg, VERR_INVALID_POINTER); 859 AssertPtrReturn(pcParms, VERR_INVALID_POINTER); 860 AssertPtrReturn(paParms, VERR_INVALID_POINTER); 861 862 if (pThis->mData.m_lstOutgoing.empty()) 863 return VERR_NO_DATA; 864 865 GuestDnDMsg *pMsg = pThis->mData.m_lstOutgoing.front(); 866 AssertPtr(pMsg); 867 868 *puMsg = pMsg->uMsg; 869 *pcParms = pMsg->cParms; 870 871 return VINF_SUCCESS; 872 } 873 #endif 880 874 #endif /* VBOX_WITH_DRAG_AND_DROP */ 881 875 -
trunk/src/VBox/Main/src-client/GuestDnDSourceImpl.cpp
r55180 r55422 5 5 6 6 /* 7 * Copyright (C) 2014 Oracle Corporation7 * Copyright (C) 2014-2015 Oracle Corporation 8 8 * 9 9 * This file is part of VirtualBox Open Source Edition (OSE), as … … 27 27 #include "AutoCaller.h" 28 28 29 #include <memory> /* For unique_ptr, seee #7179. */ 30 31 #include <iprt/dir.h> 32 #include <iprt/file.h> 33 #include <iprt/path.h> 34 29 35 #include <iprt/cpp/utils.h> /* For unconst(). */ 30 36 … … 39 45 #include <VBox/log.h> 40 46 47 /** 48 * Base class for a source task. 49 */ 50 class GuestDnDSourceTask 51 { 52 public: 53 54 GuestDnDSourceTask(GuestDnDSource *pSource) 55 : mSource(pSource), 56 mRC(VINF_SUCCESS) { } 57 58 virtual ~GuestDnDSourceTask(void) { } 59 60 int getRC(void) const { return mRC; } 61 bool isOk(void) const { return RT_SUCCESS(mRC); } 62 const ComObjPtr<GuestDnDSource> &getSource(void) const { return mSource; } 63 64 protected: 65 66 const ComObjPtr<GuestDnDSource> mSource; 67 int mRC; 68 }; 69 70 /** 71 * Task structure for receiving data from a source using 72 * a worker thread. 73 */ 74 class RecvDataTask : public GuestDnDSourceTask 75 { 76 public: 77 78 RecvDataTask(GuestDnDSource *pSource, PRECVDATACTX pCtx) 79 : GuestDnDSourceTask(pSource) 80 , mpCtx(pCtx) { } 81 82 virtual ~RecvDataTask(void) 83 { 84 if (mpCtx) 85 { 86 delete mpCtx; 87 mpCtx = NULL; 88 } 89 } 90 91 PRECVDATACTX getCtx(void) { return mpCtx; } 92 93 protected: 94 95 /** Pointer to receive data context. */ 96 PRECVDATACTX mpCtx; 97 }; 41 98 42 99 // constructor / destructor … … 95 152 ///////////////////////////////////////////////////////////////////////////// 96 153 97 HRESULT GuestDnDSource::isFormatSupported(const com::Utf8Str &aFormat, 98 BOOL *aSupported) 154 HRESULT GuestDnDSource::isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported) 99 155 { 100 156 #if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH) … … 107 163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 108 164 109 return GuestDnDBase::i sFormatSupported(aFormat, aSupported);165 return GuestDnDBase::i_isFormatSupported(aFormat, aSupported); 110 166 #endif /* VBOX_WITH_DRAG_AND_DROP */ 111 167 } … … 122 178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 123 179 124 return GuestDnDBase:: getFormats(aFormats);180 return GuestDnDBase::i_getFormats(aFormats); 125 181 #endif /* VBOX_WITH_DRAG_AND_DROP */ 126 182 } … … 137 193 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 138 194 139 return GuestDnDBase:: addFormats(aFormats);195 return GuestDnDBase::i_addFormats(aFormats); 140 196 #endif /* VBOX_WITH_DRAG_AND_DROP */ 141 197 } … … 152 208 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 153 209 154 return GuestDnDBase::removeFormats(aFormats); 210 return GuestDnDBase::i_removeFormats(aFormats); 211 #endif /* VBOX_WITH_DRAG_AND_DROP */ 212 } 213 214 HRESULT GuestDnDSource::getProtocolVersion(ULONG *aProtocolVersion) 215 { 216 #if !defined(VBOX_WITH_DRAG_AND_DROP) 217 ReturnComNotImplemented(); 218 #else /* VBOX_WITH_DRAG_AND_DROP */ 219 220 AutoCaller autoCaller(this); 221 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 222 223 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 224 225 return GuestDnDBase::i_getProtocolVersion(aProtocolVersion); 155 226 #endif /* VBOX_WITH_DRAG_AND_DROP */ 156 227 } … … 171 242 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 172 243 244 /* Determine guest DnD protocol to use. */ 245 GuestDnDBase::getProtocolVersion(&mData.mProtocolVersion); 246 173 247 /* Default is ignoring the action. */ 174 248 DnDAction_T defaultAction = DnDAction_Ignore; … … 180 254 paParms[i++].setUInt32(uScreenId); 181 255 182 int rc = GuestDnDInst()->hostCall(DragAndDropSvc::HOST_DND_GH_REQ_PENDING, 183 i, paParms); 256 int rc = GuestDnDInst()->hostCall(DragAndDropSvc::HOST_DND_GH_REQ_PENDING, i, paParms); 184 257 if (RT_SUCCESS(rc)) 185 258 { … … 210 283 if (RT_FAILURE(rc)) 211 284 hr = setError(VBOX_E_IPRT_ERROR, 212 tr("Unable to retrieve pending status (%Rrc)\n"), rc);285 tr("Unable to retrieve drag'n drop pending status (%Rrc)\n"), rc); 213 286 214 287 LogFlowFunc(("hr=%Rhrc, defaultAction=0x%x\n", hr, defaultAction)); … … 238 311 HRESULT hr = S_OK; 239 312 240 const char *pcszFormat = aFormat.c_str(); 241 bool fNeedsDropDir = DnDMIMENeedsDropDir(pcszFormat, strlen(pcszFormat)); 242 LogFlowFunc(("strFormat=%s, uAction=0x%x, fNeedsDropDir=%RTbool\n", 243 pcszFormat, uAction, fNeedsDropDir)); 244 313 /* Note: At the moment we only support one response at a time. */ 245 314 GuestDnDResponse *pResp = GuestDnDInst()->response(); 246 315 if (pResp) 247 316 { 248 /* Reset any old data. */249 pResp->reset();250 317 pResp->resetProgress(m_pGuest); 251 318 252 /* Set the format we are going to retrieve to have it around 253 * when retrieving the data later. */ 254 pResp->setFormat(aFormat); 255 256 if (fNeedsDropDir) 257 { 258 char szDropDir[RTPATH_MAX]; 259 int rc = DnDDirCreateDroppedFiles(szDropDir, sizeof(szDropDir)); 260 LogFlowFunc(("rc=%Rrc, szDropDir=%s\n", rc, szDropDir)); 261 if (RT_FAILURE(rc)) 262 return setError(VBOX_E_IPRT_ERROR, 263 tr("Unable to create the temporary drag and drop directory \"%s\" (%Rrc)\n"), 264 szDropDir, rc); 265 266 pResp->setDropDir(szDropDir); 267 } 268 269 VBOXHGCMSVCPARM paParms[4]; 270 int i = 0; 271 paParms[i++].setPointer((void*)aFormat.c_str(), (uint32_t)aFormat.length() + 1); 272 paParms[i++].setUInt32((uint32_t)aFormat.length() + 1); 273 paParms[i++].setUInt32(uAction); 274 275 int rc = GuestDnDInst()->hostCall(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED, 276 i, paParms); 277 if (RT_SUCCESS(rc)) 278 { 279 /* Query the progress object to the caller. */ 280 pResp->queryProgressTo(aProgress.asOutParam()); 281 } 282 else 283 hr = setError(VBOX_E_IPRT_ERROR, 284 tr("Error signalling to drop data (%Rrc)\n"), rc); 285 } 319 int rc; 320 321 try 322 { 323 PRECVDATACTX pRecvCtx = new RECVDATACTX; 324 RT_BZERO(pRecvCtx, sizeof(RECVDATACTX)); 325 326 pRecvCtx->mpSource = this; 327 pRecvCtx->mpResp = pResp; 328 pRecvCtx->mFormat = aFormat; 329 330 std::unique_ptr <RecvDataTask> pTask(new RecvDataTask(this, pRecvCtx)); 331 AssertReturn(pTask->isOk(), pTask->getRC()); 332 333 rc = RTThreadCreate(NULL, GuestDnDSource::i_receiveDataThread, 334 (void *)pTask.get(), 0, RTTHREADTYPE_MAIN_WORKER, 0, "dndSrcRcvData"); 335 if (RT_SUCCESS(rc)) 336 { 337 hr = pResp->queryProgressTo(aProgress.asOutParam()); 338 ComAssertComRC(hr); 339 340 /* pTask is now owned by i_receiveDataThread(), so release it. */ 341 pTask.release(); 342 } 343 else if (pRecvCtx) 344 delete pRecvCtx; 345 } 346 catch(std::bad_alloc &) 347 { 348 rc = VERR_NO_MEMORY; 349 } 350 351 /*if (RT_FAILURE(vrc)) @todo SetError(...) */ 352 } 353 /** @todo SetError(...) */ 286 354 287 355 LogFlowFunc(("Returning hr=%Rhrc\n", hr)); … … 313 381 314 382 Utf8Str strFormat = pResp->format(); 315 LogFlowFunc(("strFormat=%s, cbData=%zu, pvData=0x%p\n", 316 strFormat.c_str(), cbData, pvData)); 383 LogFlowFunc(("strFormat=%s, cbData=%zu, pvData=0x%p\n", strFormat.c_str(), cbData, pvData)); 317 384 318 385 try … … 329 396 size_t cbURIs = strURIs.length(); 330 397 331 LogFlowFunc(("Found %zu root URIs (%zu bytes)\n", 332 lstURI.RootCount(), cbURIs)); 398 LogFlowFunc(("Found %zu root URIs (%zu bytes)\n", lstURI.RootCount(), cbURIs)); 333 399 334 400 aData.resize(cbURIs + 1 /* Include termination */); … … 362 428 } 363 429 430 // implementation of internal methods. 431 ///////////////////////////////////////////////////////////////////////////// 432 433 #ifdef VBOX_WITH_DRAG_AND_DROP_GH 434 int GuestDnDSource::i_onReceiveData(PRECVDATACTX pCtx, const void *pvData, uint32_t cbData, uint64_t cbTotalSize) 435 { 436 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 437 AssertPtrReturn(pvData, VERR_INVALID_POINTER); 438 AssertReturn(cbData, VERR_INVALID_PARAMETER); 439 AssertReturn(cbTotalSize, VERR_INVALID_PARAMETER); 440 441 LogFlowFunc(("cbData=%RU32, cbTotalSize=%RU64\n", cbData, cbTotalSize)); 442 443 int rc = VINF_SUCCESS; 444 445 try 446 { 447 if ( cbData > cbTotalSize 448 || cbData > _64K) /** @todo Make this configurable? */ 449 { 450 LogFlowFunc(("Data sizes invalid: cbData=%RU32, cbTotalSize=%RU64\n", cbData, cbTotalSize)); 451 rc = VERR_INVALID_PARAMETER; 452 } 453 else if (cbData < pCtx->mData.size()) 454 { 455 AssertMsgFailed(("New size (%RU64) is smaller than current size (%zu)\n", cbTotalSize, pCtx->mData.size())); 456 rc = VERR_INVALID_PARAMETER; 457 } 458 459 if (RT_SUCCESS(rc)) 460 { 461 pCtx->mData.insert(pCtx->mData.begin(), (BYTE *)pvData, (BYTE *)pvData + cbData); 462 463 LogFlowFunc(("mDataSize=%zu\n", pCtx->mData.size())); 464 465 /* Data transfer complete? */ 466 Assert(cbData <= pCtx->mData.size()); 467 if (cbData == pCtx->mData.size()) 468 { 469 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFormat.c_str(), pCtx->mFormat.length()); 470 if (fHasURIList) 471 { 472 LogFlowFunc(("Parsing URI data ...\n")); 473 474 /* Try parsing the data as URI list. */ 475 rc = pCtx->mURI.lstURI.RootFromURIData(&pCtx->mData[0], pCtx->mData.size(), 0 /* uFlags */); 476 if (RT_SUCCESS(rc)) 477 { 478 pCtx->mURI.cbProcessed = 0; 479 pCtx->mURI.cbToProcess = cbTotalSize; 480 } 481 } 482 } 483 } 484 } 485 catch (std::bad_alloc &) 486 { 487 rc = VERR_NO_MEMORY; 488 } 489 490 LogFlowFuncLeaveRC(rc); 491 return rc; 492 } 493 494 int GuestDnDSource::i_onReceiveDir(PRECVDATACTX pCtx, const char *pszPath, uint32_t cbPath, uint32_t fMode) 495 { 496 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 497 AssertPtrReturn(pszPath, VERR_INVALID_POINTER); 498 AssertReturn(cbPath, VERR_INVALID_PARAMETER); 499 500 LogFlowFunc(("pszPath=%s, cbPath=%zu, fMode=0x%x\n", pszPath, cbPath, fMode)); 501 502 int rc; 503 char *pszDir = RTPathJoinA(pCtx->mURI.strDropDir.c_str(), pszPath); 504 if (pszDir) 505 { 506 rc = RTDirCreateFullPath(pszDir, fMode); 507 RTStrFree(pszDir); 508 } 509 else 510 rc = VERR_NO_MEMORY; 511 512 LogFlowFuncLeaveRC(rc); 513 return rc; 514 } 515 516 int GuestDnDSource::i_onReceiveFileHdr(PRECVDATACTX pCtx, const char *pszPath, uint32_t cbPath, 517 uint64_t cbSize, uint32_t fMode, uint32_t fFlags) 518 { 519 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 520 AssertPtrReturn(pszPath, VERR_INVALID_POINTER); 521 AssertReturn(cbPath, VERR_INVALID_PARAMETER); 522 AssertReturn(fMode, VERR_INVALID_PARAMETER); 523 /* fFlags are optional. */ 524 525 LogFlowFunc(("pszPath=%s, cbPath=%RU32, cbSize=%RU64, fMode=0x%x, fFlags=0x%x\n", pszPath, cbPath, cbSize, fMode, fFlags)); 526 527 int rc = VINF_SUCCESS; 528 529 do 530 { 531 if (!pCtx->mURI.objURI.IsComplete()) 532 { 533 LogFlowFunc(("Warning: Object \"%s\" not complete yet\n", pCtx->mURI.objURI.GetDestPath().c_str())); 534 rc = VERR_INVALID_PARAMETER; 535 break; 536 } 537 538 if (pCtx->mURI.objURI.IsOpen()) /* File already opened? */ 539 { 540 LogFlowFunc(("Warning: Current opened object is \"%s\"\n", pCtx->mURI.objURI.GetDestPath().c_str())); 541 rc = VERR_WRONG_ORDER; 542 break; 543 } 544 545 char pszPathAbs[RTPATH_MAX]; 546 rc = RTPathJoin(pszPathAbs, sizeof(pszPathAbs), pCtx->mURI.strDropDir.c_str(), pszPath); 547 if (RT_FAILURE(rc)) 548 { 549 LogFlowFunc(("Warning: Rebasing current file failed with rc=%Rrc\n", rc)); 550 break; 551 } 552 553 rc = DnDPathSanitize(pszPathAbs, sizeof(pszPathAbs)); 554 if (RT_FAILURE(rc)) 555 { 556 LogFlowFunc(("Warning: Rebasing current file failed with rc=%Rrc\n", rc)); 557 break; 558 } 559 560 LogFunc(("Rebased to: %s\n", pszPathAbs)); 561 562 /** @todo Add sparse file support based on fFlags? (Use Open(..., fFlags | SPARSE). */ 563 /** @todo Add fMode to opening flags. */ 564 rc = pCtx->mURI.objURI.OpenEx(pszPathAbs, DnDURIObject::File, DnDURIObject::Target, 565 RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE); 566 if (RT_SUCCESS(rc)) 567 { 568 /** @todo Unescpae path before printing. */ 569 LogRel2(("DnD: Transferring file to host: %s\n", pCtx->mURI.objURI.GetDestPath().c_str())); 570 571 /* Note: Protocol v1 does not send any file sizes, so always 0. */ 572 if (mData.mProtocolVersion >= 2) 573 rc = pCtx->mURI.objURI.SetSize(cbSize); 574 } 575 576 } while (0); 577 578 LogFlowFuncLeaveRC(rc); 579 return rc; 580 } 581 582 int GuestDnDSource::i_onReceiveFileData(PRECVDATACTX pCtx, const void *pvData, uint32_t cbData) 583 { 584 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 585 AssertPtrReturn(pvData, VERR_INVALID_POINTER); 586 AssertReturn(cbData, VERR_INVALID_PARAMETER); 587 588 int rc = VINF_SUCCESS; 589 590 do 591 { 592 if (pCtx->mURI.objURI.IsComplete()) 593 { 594 LogFlowFunc(("Warning: Object \"%s\" already completed\n", pCtx->mURI.objURI.GetDestPath().c_str())); 595 rc = VERR_WRONG_ORDER; 596 break; 597 } 598 599 if (!pCtx->mURI.objURI.IsOpen()) /* File opened on host? */ 600 { 601 LogFlowFunc(("Warning: Object \"%s\" not opened\n", pCtx->mURI.objURI.GetDestPath().c_str())); 602 rc = VERR_WRONG_ORDER; 603 break; 604 } 605 606 uint32_t cbWritten; 607 rc = pCtx->mURI.objURI.Write(pvData, cbData, &cbWritten); 608 if (RT_SUCCESS(rc)) 609 { 610 Assert(cbWritten <= cbData); 611 if (cbWritten < cbData) 612 { 613 /** @todo What to do when the host's disk is full? */ 614 rc = VERR_DISK_FULL; 615 } 616 } 617 618 if (RT_SUCCESS(rc)) 619 { 620 if (pCtx->mURI.objURI.IsComplete()) 621 { 622 LogRel2(("DnD: File transfer to host complete: %s", pCtx->mURI.objURI.GetDestPath().c_str())); 623 rc = VINF_EOF; 624 } 625 } 626 else 627 LogRel(("DnD: Error: Can't write guest file to host to \"%s\": %Rrc\n", pCtx->mURI.objURI.GetDestPath().c_str(), rc)); 628 629 } while (0); 630 631 LogFlowFuncLeaveRC(rc); 632 return rc; 633 } 634 #endif /* VBOX_WITH_DRAG_AND_DROP_GH */ 635 636 int GuestDnDSource::i_receiveData(PRECVDATACTX pCtx) 637 { 638 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 639 640 int rc; 641 642 GuestDnD *pInst = GuestDnDInst(); 643 if (!pInst) 644 return VERR_INVALID_POINTER; 645 646 GuestDnDResponse *pResp = pCtx->mpResp; 647 AssertPtr(pCtx->mpResp); 648 649 ASMAtomicWriteBool(&pCtx->mIsActive, true); 650 651 /* Create event semaphore. */ 652 pCtx->SemEvent = NIL_RTSEMEVENT; 653 rc = RTSemEventCreate(&pCtx->SemEvent); 654 if (RT_FAILURE(rc)) 655 return rc; 656 657 pCtx->mURI.cbToProcess = 0; 658 pCtx->mURI.cbProcessed = 0; 659 pCtx->mURI.cProcessed = 0; 660 661 do 662 { 663 /* Reset any old data. */ 664 pResp->reset(); 665 pResp->resetProgress(m_pGuest); 666 667 /* Set the format we are going to retrieve to have it around 668 * when retrieving the data later. */ 669 pResp->setFormat(pCtx->mFormat); 670 671 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFormat.c_str(), pCtx->mFormat.length()); 672 LogFlowFunc(("strFormat=%s, uAction=0x%x, fHasURIList=%RTbool\n", pCtx->mFormat.c_str(), pCtx->mAction, fHasURIList)); 673 674 if (fHasURIList) 675 { 676 rc = i_receiveURIData(pCtx); 677 } 678 else 679 { 680 rc = i_receiveRawData(pCtx); 681 } 682 683 } while (0); 684 685 if (pCtx->SemEvent != NIL_RTSEMEVENT) 686 { 687 RTSemEventDestroy(pCtx->SemEvent); 688 pCtx->SemEvent = NIL_RTSEMEVENT; 689 } 690 691 ASMAtomicWriteBool(&pCtx->mIsActive, false); 692 693 LogFlowFuncLeaveRC(rc); 694 return rc; 695 } 696 697 /* static */ 698 DECLCALLBACK(int) GuestDnDSource::i_receiveDataThread(RTTHREAD Thread, void *pvUser) 699 { 700 LogFlowFunc(("pvUser=%p\n", pvUser)); 701 702 std::unique_ptr<RecvDataTask> pTask(static_cast<RecvDataTask*>(pvUser)); 703 AssertPtr(pTask.get()); 704 705 const ComObjPtr<GuestDnDSource> pTarget(pTask->getSource()); 706 Assert(!pTarget.isNull()); 707 708 AutoCaller autoCaller(pTarget); 709 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 710 711 int rc = pTarget->i_receiveData(pTask->getCtx()); 712 /* Nothing to do here anymore. */ 713 714 LogFlowFunc(("pSource=%p returning rc=%Rrc\n", (GuestDnDSource *)pTarget, rc)); 715 return rc; 716 } 717 718 int GuestDnDSource::i_receiveRawData(PRECVDATACTX pCtx) 719 { 720 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 721 722 int rc; 723 724 GuestDnDResponse *pResp = pCtx->mpResp; 725 AssertPtr(pCtx->mpResp); 726 727 GuestDnD *pInst = GuestDnDInst(); 728 if (!pInst) 729 return VERR_INVALID_POINTER; 730 731 #define REGISTER_CALLBACK(x) \ 732 rc = pResp->setCallback(x, i_receiveRawDataCallback, pCtx); \ 733 if (RT_FAILURE(rc)) \ 734 return rc; 735 736 #define UNREGISTER_CALLBACK(x) \ 737 rc = pCtx->mpResp->setCallback(x, NULL); \ 738 AssertRC(rc); 739 740 /* 741 * Register callbacks. 742 */ 743 /* Guest callbacks. */ 744 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA); 745 746 do 747 { 748 /* 749 * Receive the raw data. 750 */ 751 GuestDnDMsg Msg; 752 Msg.setType(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED); 753 Msg.setNextPointer((void*)pCtx->mFormat.c_str(), (uint32_t)pCtx->mFormat.length() + 1); 754 Msg.setNextUInt32((uint32_t)pCtx->mFormat.length() + 1); 755 Msg.setNextUInt32(pCtx->mAction); 756 757 /* Make the initial call to the guest by telling that we initiated the "dropped" event on 758 * the host and therefore now waiting for the actual raw data. */ 759 rc = pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms()); 760 if (RT_SUCCESS(rc)) 761 { 762 /* 763 * Wait until our callback i_receiveRawDataCallback triggered the 764 * wait event. 765 */ 766 LogFlowFunc(("Waiting for raw data callback ...\n")); 767 rc = RTSemEventWait(pCtx->SemEvent, RT_INDEFINITE_WAIT); 768 LogFlowFunc(("Raw data callback done\n")); 769 } 770 771 } while (0); 772 773 /* 774 * Unregister callbacks. 775 */ 776 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA); 777 778 #undef REGISTER_CALLBACK 779 #undef UNREGISTER_CALLBACK 780 781 LogFlowFuncLeaveRC(rc); 782 return rc; 783 } 784 785 int GuestDnDSource::i_receiveURIData(PRECVDATACTX pCtx) 786 { 787 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 788 789 int rc; 790 791 GuestDnDResponse *pResp = pCtx->mpResp; 792 AssertPtr(pCtx->mpResp); 793 794 GuestDnD *pInst = GuestDnDInst(); 795 if (!pInst) 796 return VERR_INVALID_POINTER; 797 798 #define REGISTER_CALLBACK(x) \ 799 rc = pResp->setCallback(x, i_receiveURIDataCallback, pCtx); \ 800 if (RT_FAILURE(rc)) \ 801 return rc; 802 803 #define UNREGISTER_CALLBACK(x) \ 804 rc = pResp->setCallback(x, NULL); \ 805 AssertRC(rc); 806 807 /* 808 * Register callbacks. 809 */ 810 /* Guest callbacks. */ 811 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA); 812 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DIR); 813 if (mData.mProtocolVersion >= 2) 814 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR); 815 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA); 816 817 do 818 { 819 char szDropDir[RTPATH_MAX]; 820 rc = DnDDirCreateDroppedFiles(szDropDir, sizeof(szDropDir)); 821 LogFlowFunc(("rc=%Rrc, szDropDir=%s\n", rc, szDropDir)); 822 if (RT_FAILURE(rc)) 823 break; 824 825 pCtx->mURI.strDropDir = szDropDir; /** @todo Keep directory handle open? */ 826 pResp->setDropDir(szDropDir); 827 828 /* 829 * Receive the URI list. 830 */ 831 GuestDnDMsg Msg; 832 Msg.setType(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED); 833 Msg.setNextPointer((void*)pCtx->mFormat.c_str(), (uint32_t)pCtx->mFormat.length() + 1); 834 Msg.setNextUInt32((uint32_t)pCtx->mFormat.length() + 1); 835 Msg.setNextUInt32(pCtx->mAction); 836 837 /* Make the initial call to the guest by telling that we initiated the "dropped" event on 838 * the host and therefore now waiting for the actual URI actual data. */ 839 rc = pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms()); 840 if (RT_SUCCESS(rc)) 841 { 842 /* 843 * Wait until our callback i_receiveURIDataCallback triggered the 844 * wait event. 845 */ 846 LogFlowFunc(("Waiting for URI callback ...\n")); 847 rc = RTSemEventWait(pCtx->SemEvent, RT_INDEFINITE_WAIT); 848 LogFlowFunc(("URI callback done, rc=%Rrc\n", rc)); 849 } 850 851 } while (0); 852 853 /* 854 * Unregister callbacks. 855 */ 856 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA); 857 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DIR); 858 if (mData.mProtocolVersion >= 2) 859 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR); 860 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA); 861 862 #undef REGISTER_CALLBACK 863 #undef UNREGISTER_CALLBACK 864 865 if (RT_FAILURE(rc)) 866 { 867 LogFlowFunc(("Rolling back ...\n")); 868 869 /* Rollback by removing any stuff created. */ 870 for (size_t i = 0; i < pCtx->mURI.lstFiles.size(); ++i) 871 RTFileDelete(pCtx->mURI.lstFiles.at(i).c_str()); 872 for (size_t i = 0; i < pCtx->mURI.lstDirs.size(); ++i) 873 RTDirRemove(pCtx->mURI.lstDirs.at(i).c_str()); 874 } 875 876 /* Try removing (hopefully) empty drop directory in any case. */ 877 if (pCtx->mURI.strDropDir.isNotEmpty()) 878 RTDirRemove(pCtx->mURI.strDropDir.c_str()); 879 880 LogFlowFuncLeaveRC(rc); 881 return rc; 882 } 883 884 /* static */ 885 DECLCALLBACK(int) GuestDnDSource::i_receiveRawDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser) 886 { 887 PRECVDATACTX pCtx = (PRECVDATACTX)pvUser; 888 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 889 890 GuestDnDSource *pThis = pCtx->mpSource; 891 AssertPtrReturn(pThis, VERR_INVALID_POINTER); 892 893 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg)); 894 895 int rc = VINF_SUCCESS; 896 897 switch (uMsg) 898 { 899 case DragAndDropSvc::GUEST_DND_GH_SND_DATA: 900 { 901 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms); 902 AssertPtr(pCBData); 903 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER); 904 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER); 905 906 rc = pThis->i_onReceiveData(pCtx, pCBData->pvData, pCBData->cbData, pCBData->cbTotalSize); 907 break; 908 } 909 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR: 910 { 911 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms); 912 AssertPtr(pCBData); 913 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER); 914 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER); 915 916 /* Cleanup. */ 917 pCtx->mpResp->reset(); 918 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc); 919 break; 920 } 921 default: 922 rc = VERR_NOT_SUPPORTED; 923 break; 924 } 925 926 if (RT_FAILURE(rc)) 927 { 928 if (pCtx->SemEvent != NIL_RTSEMEVENT) 929 { 930 LogFlowFunc(("Signalling ...\n")); 931 int rc2 = RTSemEventSignal(pCtx->SemEvent); 932 AssertRC(rc2); 933 } 934 } 935 936 LogFlowFuncLeaveRC(rc); 937 return rc; /* Tell the guest. */ 938 } 939 940 /* static */ 941 DECLCALLBACK(int) GuestDnDSource::i_receiveURIDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser) 942 { 943 PRECVDATACTX pCtx = (PRECVDATACTX)pvUser; 944 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 945 946 GuestDnDSource *pThis = pCtx->mpSource; 947 AssertPtrReturn(pThis, VERR_INVALID_POINTER); 948 949 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg)); 950 951 int rc = VINF_SUCCESS; 952 953 switch (uMsg) 954 { 955 case DragAndDropSvc::GUEST_DND_GH_SND_DATA: 956 { 957 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms); 958 AssertPtr(pCBData); 959 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER); 960 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER); 961 962 rc = pThis->i_onReceiveData(pCtx, pCBData->pvData, pCBData->cbData, pCBData->cbTotalSize); 963 break; 964 } 965 case DragAndDropSvc::GUEST_DND_GH_SND_DIR: 966 { 967 DragAndDropSvc::PVBOXDNDCBSNDDIRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDIRDATA>(pvParms); 968 AssertPtr(pCBData); 969 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDIRDATA) == cbParms, VERR_INVALID_PARAMETER); 970 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER); 971 972 rc = pThis->i_onReceiveDir(pCtx, pCBData->pszPath, pCBData->cbPath, pCBData->fMode); 973 break; 974 } 975 case DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR: 976 { 977 DragAndDropSvc::PVBOXDNDCBSNDFILEHDRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEHDRDATA>(pvParms); 978 AssertPtr(pCBData); 979 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEHDRDATA) == cbParms, VERR_INVALID_PARAMETER); 980 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE_HDR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER); 981 982 rc = pThis->i_onReceiveFileHdr(pCtx, pCBData->pszFilePath, pCBData->cbFilePath, 983 pCBData->cbSize, pCBData->fMode, pCBData->fFlags); 984 break; 985 } 986 case DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA: 987 { 988 DragAndDropSvc::PVBOXDNDCBSNDFILEDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEDATADATA>(pvParms); 989 AssertPtr(pCBData); 990 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEDATADATA) == cbParms, VERR_INVALID_PARAMETER); 991 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER); 992 993 if (pThis->mData.mProtocolVersion <= 1) 994 { 995 /** 996 * Notes for protocol v1 (< VBox 5.0): 997 * - Every time this command is being sent it includes the file header, 998 * so just process both calls here. 999 * - There was no information whatsoever about the total file size; the old code only 1000 * appended data to the desired file. So just pass 0 as cbSize. 1001 */ 1002 rc = pThis->i_onReceiveFileHdr(pCtx, 1003 pCBData->u.v1.pszFilePath, pCBData->u.v1.cbFilePath, 1004 0 /* cbSize */, pCBData->u.v1.fMode, 0 /* fFlags */); 1005 if (RT_SUCCESS(rc)) 1006 rc = pThis->i_onReceiveFileData(pCtx, pCBData->pvData, pCBData->cbData); 1007 } 1008 else /* Protocol v2 and up. */ 1009 rc = pThis->i_onReceiveFileData(pCtx, pCBData->pvData, pCBData->cbData); 1010 1011 /* Current file done? */ 1012 if (rc == VINF_EOF) 1013 { 1014 /* Remove it from the list. */ 1015 pCtx->mURI.lstURI.RemoveFirst(); 1016 1017 if (pCtx->mURI.lstURI.IsEmpty()) /* Current file processed? Check if there's more. */ 1018 { 1019 /* Let waiters know. */ 1020 if (pCtx->SemEvent != NIL_RTSEMEVENT) 1021 { 1022 LogFlowFunc(("Signalling ...\n")); 1023 int rc2 = RTSemEventSignal(pCtx->SemEvent); 1024 AssertRC(rc2); 1025 } 1026 } 1027 } 1028 break; 1029 } 1030 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR: 1031 { 1032 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms); 1033 AssertPtr(pCBData); 1034 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER); 1035 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER); 1036 1037 /* Cleanup. */ 1038 pCtx->mpResp->reset(); 1039 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc); 1040 break; 1041 } 1042 default: 1043 rc = VERR_NOT_SUPPORTED; 1044 break; 1045 } 1046 1047 if (RT_FAILURE(rc)) 1048 { 1049 if (pCtx->SemEvent != NIL_RTSEMEVENT) 1050 { 1051 LogFlowFunc(("Signalling ...\n")); 1052 int rc2 = RTSemEventSignal(pCtx->SemEvent); 1053 AssertRC(rc2); 1054 } 1055 } 1056 1057 LogFlowFuncLeaveRC(rc); 1058 return rc; /* Tell the guest. */ 1059 } 1060 -
trunk/src/VBox/Main/src-client/GuestDnDTargetImpl.cpp
r55244 r55422 5 5 6 6 /* 7 * Copyright (C) 2014 Oracle Corporation7 * Copyright (C) 2014-2015 Oracle Corporation 8 8 * 9 9 * This file is part of VirtualBox Open Source Edition (OSE), as … … 22 22 #include "GuestImpl.h" 23 23 #include "GuestDnDTargetImpl.h" 24 #include "VirtualBoxErrorInfoImpl.h" 24 25 25 26 #include "Global.h" … … 27 28 28 29 #include <algorithm> /* For std::find(). */ 30 #include <memory> /* For unique_ptr, see #7179. */ 31 32 #include <iprt/file.h> 33 #include <iprt/dir.h> 34 #include <iprt/path.h> 35 #include <iprt/uri.h> 29 36 #include <iprt/cpp/utils.h> /* For unconst(). */ 30 37 31 38 #include <VBox/com/array.h> 32 #include <VBox/HostServices/DragAndDropSvc.h> 39 40 #include <VBox/GuestHost/DragAndDrop.h> 41 #include <VBox/HostServices/Service.h> 33 42 34 43 #ifdef LOG_GROUP … … 39 48 40 49 50 /** 51 * Base class for a target task. 52 */ 53 class GuestDnDTargetTask 54 { 55 public: 56 57 GuestDnDTargetTask(GuestDnDTarget *pTarget) 58 : mTarget(pTarget), 59 mRC(VINF_SUCCESS) { } 60 61 virtual ~GuestDnDTargetTask(void) { } 62 63 int getRC(void) const { return mRC; } 64 bool isOk(void) const { return RT_SUCCESS(mRC); } 65 const ComObjPtr<GuestDnDTarget> &getTarget(void) const { return mTarget; } 66 67 protected: 68 69 const ComObjPtr<GuestDnDTarget> mTarget; 70 int mRC; 71 }; 72 73 /** 74 * Task structure for sending data to a target using 75 * a worker thread. 76 */ 77 class SendDataTask : public GuestDnDTargetTask 78 { 79 public: 80 81 SendDataTask(GuestDnDTarget *pTarget, PSENDDATACTX pCtx) 82 : GuestDnDTargetTask(pTarget), 83 mpCtx(pCtx) { } 84 85 virtual ~SendDataTask(void) 86 { 87 if (mpCtx) 88 { 89 delete mpCtx; 90 mpCtx = NULL; 91 } 92 } 93 94 95 PSENDDATACTX getCtx(void) { return mpCtx; } 96 97 protected: 98 99 /** Pointer to send data context. */ 100 PSENDDATACTX mpCtx; 101 }; 102 41 103 // constructor / destructor 42 104 ///////////////////////////////////////////////////////////////////////////// … … 46 108 HRESULT GuestDnDTarget::FinalConstruct(void) 47 109 { 110 /* Set the maximum block size our guests can handle to 64K. This always has 111 * been hardcoded until now. */ 112 /* Note: Never ever rely on information from the guest; the host dictates what and 113 * how to do something, so try to negogiate a sensible value here later. */ 114 m_cbBlockSize = _32K; /** @todo Make this configurable. */ 115 48 116 LogFlowThisFunc(("\n")); 49 117 return BaseFinalConstruct(); … … 94 162 ///////////////////////////////////////////////////////////////////////////// 95 163 96 HRESULT GuestDnDTarget::isFormatSupported(const com::Utf8Str &aFormat, 97 BOOL *aSupported) 98 { 99 #if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH) 164 HRESULT GuestDnDTarget::isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported) 165 { 166 #if !defined(VBOX_WITH_DRAG_AND_DROP) 100 167 ReturnComNotImplemented(); 101 168 #else /* VBOX_WITH_DRAG_AND_DROP */ … … 106 173 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 107 174 108 return GuestDnDBase::i sFormatSupported(aFormat, aSupported);175 return GuestDnDBase::i_isFormatSupported(aFormat, aSupported); 109 176 #endif /* VBOX_WITH_DRAG_AND_DROP */ 110 177 } … … 112 179 HRESULT GuestDnDTarget::getFormats(std::vector<com::Utf8Str> &aFormats) 113 180 { 114 #if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)181 #if !defined(VBOX_WITH_DRAG_AND_DROP) 115 182 ReturnComNotImplemented(); 116 183 #else /* VBOX_WITH_DRAG_AND_DROP */ … … 121 188 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 122 189 123 return GuestDnDBase:: getFormats(aFormats);190 return GuestDnDBase::i_getFormats(aFormats); 124 191 #endif /* VBOX_WITH_DRAG_AND_DROP */ 125 192 } … … 127 194 HRESULT GuestDnDTarget::addFormats(const std::vector<com::Utf8Str> &aFormats) 128 195 { 129 #if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)196 #if !defined(VBOX_WITH_DRAG_AND_DROP) 130 197 ReturnComNotImplemented(); 131 198 #else /* VBOX_WITH_DRAG_AND_DROP */ … … 136 203 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 137 204 138 return GuestDnDBase:: addFormats(aFormats);205 return GuestDnDBase::i_addFormats(aFormats); 139 206 #endif /* VBOX_WITH_DRAG_AND_DROP */ 140 207 } … … 142 209 HRESULT GuestDnDTarget::removeFormats(const std::vector<com::Utf8Str> &aFormats) 143 210 { 144 #if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)211 #if !defined(VBOX_WITH_DRAG_AND_DROP) 145 212 ReturnComNotImplemented(); 146 213 #else /* VBOX_WITH_DRAG_AND_DROP */ … … 151 218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 152 219 153 return GuestDnDBase::removeFormats(aFormats); 220 return GuestDnDBase::i_removeFormats(aFormats); 221 #endif /* VBOX_WITH_DRAG_AND_DROP */ 222 } 223 224 HRESULT GuestDnDTarget::getProtocolVersion(ULONG *aProtocolVersion) 225 { 226 #if !defined(VBOX_WITH_DRAG_AND_DROP) 227 ReturnComNotImplemented(); 228 #else /* VBOX_WITH_DRAG_AND_DROP */ 229 230 AutoCaller autoCaller(this); 231 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 232 233 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 234 235 return GuestDnDBase::i_getProtocolVersion(aProtocolVersion); 154 236 #endif /* VBOX_WITH_DRAG_AND_DROP */ 155 237 } … … 164 246 DnDAction_T *aResultAction) 165 247 { 166 #if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)248 #if !defined(VBOX_WITH_DRAG_AND_DROP) 167 249 ReturnComNotImplemented(); 168 250 #else /* VBOX_WITH_DRAG_AND_DROP */ … … 178 260 AutoCaller autoCaller(this); 179 261 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 262 263 /* Determine guest DnD protocol to use. */ 264 GuestDnDBase::getProtocolVersion(&mData.mProtocolVersion); 180 265 181 266 /* Default action is ignoring. */ … … 237 322 DnDAction_T *aResultAction) 238 323 { 239 #if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)324 #if !defined(VBOX_WITH_DRAG_AND_DROP) 240 325 ReturnComNotImplemented(); 241 326 #else /* VBOX_WITH_DRAG_AND_DROP */ … … 299 384 HRESULT GuestDnDTarget::leave(ULONG uScreenId) 300 385 { 301 #if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)386 #if !defined(VBOX_WITH_DRAG_AND_DROP) 302 387 ReturnComNotImplemented(); 303 388 #else /* VBOX_WITH_DRAG_AND_DROP */ … … 327 412 com::Utf8Str &aFormat, DnDAction_T *aResultAction) 328 413 { 329 #if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)414 #if !defined(VBOX_WITH_DRAG_AND_DROP) 330 415 ReturnComNotImplemented(); 331 416 #else /* VBOX_WITH_DRAG_AND_DROP */ … … 395 480 } 396 481 397 HRESULT GuestDnDTarget::sendData(ULONG aScreenId, 398 const com::Utf8Str &aFormat, 399 const std::vector<BYTE> &aData, 482 /* static */ 483 DECLCALLBACK(int) GuestDnDTarget::i_sendDataThread(RTTHREAD Thread, void *pvUser) 484 { 485 LogFlowFunc(("pvUser=%p\n", pvUser)); 486 487 std::unique_ptr<SendDataTask> pTask(static_cast<SendDataTask*>(pvUser)); 488 AssertPtr(pTask.get()); 489 490 const ComObjPtr<GuestDnDTarget> pTarget(pTask->getTarget()); 491 Assert(!pTarget.isNull()); 492 493 AutoCaller autoCaller(pTarget); 494 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 495 496 int rc = pTarget->i_sendData(pTask->getCtx()); 497 /* Nothing to do here anymore. */ 498 499 LogFlowFunc(("pTarget=%p returning rc=%Rrc\n", (GuestDnDTarget *)pTarget, rc)); 500 return rc; 501 } 502 503 /** 504 * Initiates a data transfer from the host to the guest. The source is the host whereas the target is the 505 * guest in this case. 506 * 507 * @return HRESULT 508 * @param aScreenId 509 * @param aFormat 510 * @param aData 511 * @param aProgress 512 */ 513 HRESULT GuestDnDTarget::sendData(ULONG aScreenId, const com::Utf8Str &aFormat, const std::vector<BYTE> &aData, 400 514 ComPtr<IProgress> &aProgress) 401 515 { 402 #if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)516 #if !defined(VBOX_WITH_DRAG_AND_DROP) 403 517 ReturnComNotImplemented(); 404 518 #else /* VBOX_WITH_DRAG_AND_DROP */ 405 519 406 /* Input validation*/407 520 /** @todo Add input validation. */ 521 /** @todo Check if another sendData() call currently is being processed. */ 408 522 409 523 AutoCaller autoCaller(this); 410 524 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 411 525 412 HRESULT hr = S_OK; 413 414 VBOXHGCMSVCPARM paParms[8]; 415 int i = 0; 416 paParms[i++].setUInt32(aScreenId); 417 paParms[i++].setPointer((void *)aFormat.c_str(), (uint32_t)aFormat.length() + 1); 418 paParms[i++].setUInt32((uint32_t)aFormat.length() + 1); 419 paParms[i++].setPointer((void*)&aData.front(), (uint32_t)aData.size()); 420 paParms[i++].setUInt32((uint32_t)aData.size()); 421 526 HRESULT hr; 527 int vrc; 528 529 /* Note: At the moment we only support one response at a time. */ 422 530 GuestDnDResponse *pResp = GuestDnDInst()->response(); 423 531 if (pResp) 424 532 { 425 /* Reset any old progress status. */426 533 pResp->resetProgress(m_pGuest); 427 534 428 /* Note: The actual data transfer of files/directoies is performed by the 429 * DnD host service. */ 430 int rc = GuestDnDInst()->hostCall(DragAndDropSvc::HOST_DND_HG_SND_DATA, 431 i, paParms); 432 if (RT_SUCCESS(rc)) 433 { 434 hr = pResp->queryProgressTo(aProgress.asOutParam()); 435 ComAssertComRC(hr); 436 } 437 } 535 try 536 { 537 PSENDDATACTX pSendCtx = new SENDDATACTX; 538 RT_BZERO(pSendCtx, sizeof(SENDDATACTX)); 539 540 pSendCtx->mpTarget = this; 541 pSendCtx->mpResp = pResp; 542 pSendCtx->mScreenID = aScreenId; 543 pSendCtx->mFormat = aFormat; 544 pSendCtx->mData = aData; 545 546 std::unique_ptr<SendDataTask> pTask(new SendDataTask(this, pSendCtx)); 547 AssertReturn(pTask->isOk(), pTask->getRC()); 548 549 vrc = RTThreadCreate(NULL, GuestDnDTarget::i_sendDataThread, 550 (void *)pTask.get(), 0, RTTHREADTYPE_MAIN_WORKER, 0, "dndTgtSndData"); 551 if (RT_SUCCESS(vrc)) 552 { 553 hr = pResp->queryProgressTo(aProgress.asOutParam()); 554 ComAssertComRC(hr); 555 556 /* pTask is now owned by i_sendDataThread(), so release it. */ 557 pTask.release(); 558 } 559 else if (pSendCtx) 560 delete pSendCtx; 561 } 562 catch(std::bad_alloc &) 563 { 564 vrc = VERR_NO_MEMORY; 565 } 566 567 /*if (RT_FAILURE(vrc)) ** @todo SetError(...) */ 568 } 569 /** @todo SetError(...) */ 438 570 439 571 return hr; … … 441 573 } 442 574 575 int GuestDnDTarget::i_cancelOperation(void) 576 { 577 /** @todo Check for pending cancel requests. */ 578 579 #if 0 /** @todo Later. */ 580 /* Cancel any outstanding waits for guest responses first. */ 581 if (pResp) 582 pResp->notifyAboutGuestResponse(); 583 #endif 584 585 LogFlowFunc(("Cancelling operation, telling guest ...\n")); 586 return GuestDnDInst()->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_CANCEL, 0 /* cParms */, NULL /*paParms*/); 587 } 588 589 int GuestDnDTarget::i_sendData(PSENDDATACTX pCtx) 590 { 591 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 592 593 #define DATA_IS_VALID_BREAK(x) \ 594 if (!x) \ 595 { \ 596 LogFlowFunc(("Invalid URI data value for \"" #x "\"\n")); \ 597 rc = VERR_INVALID_PARAMETER; \ 598 break; \ 599 } 600 601 GuestDnD *pInst = GuestDnDInst(); 602 if (!pInst) 603 return VERR_INVALID_POINTER; 604 605 int rc; 606 607 ASMAtomicWriteBool(&pCtx->mIsActive, true); 608 609 do 610 { 611 const char *pszFormat = pCtx->mFormat.c_str(); 612 DATA_IS_VALID_BREAK(pszFormat); 613 uint32_t cbFormat = pCtx->mFormat.length() + 1; 614 615 /* Do we need to build up a file tree? */ 616 bool fHasURIList = DnDMIMEHasFileURLs(pszFormat, cbFormat); 617 if (fHasURIList) 618 { 619 rc = i_sendURIData(pCtx); 620 } 621 else 622 { 623 GuestDnDMsg Msg; 624 625 size_t cbDataTotal = pCtx->mData.size(); 626 DATA_IS_VALID_BREAK(cbDataTotal); 627 628 /* Just copy over the raw data. */ 629 Msg.setType(DragAndDropSvc::HOST_DND_HG_SND_DATA); 630 Msg.setNextUInt32(pCtx->mScreenID); 631 Msg.setNextPointer((void *)pCtx->mFormat.c_str(), (uint32_t)pCtx->mFormat.length() + 1); 632 Msg.setNextUInt32((uint32_t)pCtx->mFormat.length() + 1); 633 Msg.setNextPointer((void*)&pCtx->mData.front(), (uint32_t)cbDataTotal); 634 Msg.setNextUInt32(cbDataTotal); 635 636 LogFlowFunc(("%zu total bytes of raw data to transfer\n", cbDataTotal)); 637 638 /* Make the initial call to the guest by sending the actual data. This might 639 * be an URI list which in turn can lead to more data to send afterwards. */ 640 rc = pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms()); 641 if (RT_FAILURE(rc)) 642 break; 643 } 644 645 } while (0); 646 647 ASMAtomicWriteBool(&pCtx->mIsActive, false); 648 649 #undef DATA_IS_VALID_BREAK 650 651 LogFlowFuncLeaveRC(rc); 652 return rc; 653 } 654 655 int GuestDnDTarget::i_sendDirectory(PSENDDATACTX pCtx, GuestDnDMsg *pMsg, DnDURIObject &aDirectory) 656 { 657 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 658 659 RTCString strPath = aDirectory.GetDestPath(); 660 if (strPath.isEmpty()) 661 return VERR_INVALID_PARAMETER; 662 if (strPath.length() >= RTPATH_MAX) /* Note: Maximum is RTPATH_MAX on guest side. */ 663 return VERR_BUFFER_OVERFLOW; 664 665 LogFlowFunc(("Sending directory \"%s\" using protocol v%RU32 ...\n", strPath.c_str(), mData.mProtocolVersion)); 666 667 pMsg->setType(DragAndDropSvc::HOST_DND_HG_SND_DIR); 668 pMsg->setNextString(strPath.c_str()); /* path */ 669 pMsg->setNextUInt32((uint32_t)(strPath.length() + 1)); /* path length - note: Maximum is RTPATH_MAX on guest side. */ 670 pMsg->setNextUInt32(aDirectory.GetMode()); /* mode */ 671 672 return VINF_SUCCESS; 673 } 674 675 int GuestDnDTarget::i_sendFile(PSENDDATACTX pCtx, GuestDnDMsg *pMsg, DnDURIObject &aFile) 676 { 677 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 678 679 RTCString strPath = aFile.GetDestPath(); 680 if (strPath.isEmpty()) 681 return VERR_INVALID_PARAMETER; 682 683 int rc = VINF_SUCCESS; 684 685 LogFlowFunc(("Sending \"%s\" (%RU32 bytes buffer) using protocol v%RU32 ...\n", 686 aFile.GetDestPath().c_str(), m_cbBlockSize, mData.mProtocolVersion)); 687 688 bool fSendFileData = false; 689 if (mData.mProtocolVersion >= 2) 690 { 691 if (!aFile.IsOpen()) 692 { 693 rc = aFile.OpenEx(aFile.GetSourcePath(), DnDURIObject::Type::File, DnDURIObject::Dest::Source, 694 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE, 0 /* fFlags */); 695 if (RT_SUCCESS(rc)) 696 { 697 /* 698 * Since protocol v2 the file header and the actual file contents are 699 * separate messages, so send the file header first. 700 * The just registered callback will be called by the guest afterwards. 701 */ 702 pMsg->setType(DragAndDropSvc::HOST_DND_HG_SND_FILE_HDR); 703 pMsg->setNextUInt32(0); /* context ID */ 704 pMsg->setNextString(strPath.c_str()); /* pvName */ 705 pMsg->setNextUInt32((uint32_t)(strPath.length() + 1)); /* cbName */ 706 pMsg->setNextUInt32(0); /* uFlags */ 707 pMsg->setNextUInt32(aFile.GetMode()); /* fMode */ 708 pMsg->setNextUInt64(aFile.GetSize()); /* uSize */ 709 710 LogFlowFunc(("Sending file header ...\n")); 711 } 712 } 713 else 714 { 715 /* File header was sent, so only send the actual file data. */ 716 fSendFileData = true; 717 } 718 } 719 else /* Protocol v1. */ 720 { 721 /* Always send the file data, every time. */ 722 fSendFileData = true; 723 } 724 725 if ( RT_SUCCESS(rc) 726 && fSendFileData) 727 { 728 rc = i_sendFileData(pCtx, pMsg, aFile); 729 } 730 731 LogFlowFuncLeaveRC(rc); 732 return rc; 733 } 734 735 int GuestDnDTarget::i_sendFileData(PSENDDATACTX pCtx, GuestDnDMsg *pMsg, DnDURIObject &aFile) 736 { 737 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 738 AssertPtrReturn(pMsg, VERR_INVALID_POINTER); 739 740 GuestDnDResponse *pResp = pCtx->mpResp; 741 AssertPtr(pResp); 742 743 /** @todo Don't allow concurrent reads per context! */ 744 745 /* Something to transfer? */ 746 if ( pCtx->mURI.lstURI.IsEmpty() 747 || !pCtx->mIsActive) 748 { 749 return VERR_WRONG_ORDER; 750 } 751 752 /* 753 * Start sending stuff. 754 */ 755 756 /* Set the message type. */ 757 pMsg->setType(DragAndDropSvc::HOST_DND_HG_SND_FILE_DATA); 758 759 /* Protocol version 1 sends the file path *every* time with a new file chunk. 760 * In protocol version 2 we only do this once with HOST_DND_HG_SND_FILE_HDR. */ 761 if (mData.mProtocolVersion <= 1) 762 { 763 pMsg->setNextUInt32(0); /* context ID */ 764 pMsg->setNextString(aFile.GetSourcePath().c_str()); /* pvName */ 765 pMsg->setNextUInt32((uint32_t)(aFile.GetSourcePath().length() + 1)); /* cbName */ 766 } 767 768 uint32_t cbRead = 0; 769 770 int rc = aFile.Read(pCtx->mURI.pvScratchBuf, pCtx->mURI.cbScratchBuf, &cbRead); 771 if (RT_SUCCESS(rc)) 772 { 773 pCtx->mURI.cbProcessed += cbRead; 774 775 if (mData.mProtocolVersion <= 1) 776 { 777 pMsg->setNextPointer(pCtx->mURI.pvScratchBuf, cbRead); /* pvData */ 778 pMsg->setNextUInt32(cbRead); /* cbData */ 779 pMsg->setNextUInt32(aFile.GetMode()); /* fMode */ 780 } 781 else 782 { 783 pMsg->setNextPointer(pCtx->mURI.pvScratchBuf, cbRead); /* pvData */ 784 pMsg->setNextUInt32(cbRead); /* cbData */ 785 } 786 787 if (aFile.IsComplete()) /* Done reading? */ 788 { 789 LogFlowFunc(("File \"%s\" complete\n", aFile.GetSourcePath().c_str())); 790 rc = VINF_EOF; 791 } 792 } 793 794 LogFlowFuncLeaveRC(rc); 795 return rc; 796 } 797 798 /* static */ 799 DECLCALLBACK(int) GuestDnDTarget::i_sendURIDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser) 800 { 801 PSENDDATACTX pCtx = (PSENDDATACTX)pvUser; 802 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 803 804 GuestDnDTarget *pThis = pCtx->mpTarget; 805 AssertPtrReturn(pThis, VERR_INVALID_POINTER); 806 807 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg)); 808 809 int rc = VINF_SUCCESS; 810 811 switch (uMsg) 812 { 813 case DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG: 814 { 815 DragAndDropSvc::PVBOXDNDCBHGGETNEXTHOSTMSG pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGGETNEXTHOSTMSG>(pvParms); 816 AssertPtr(pCBData); 817 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGGETNEXTHOSTMSG) == cbParms, VERR_INVALID_PARAMETER); 818 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_GET_NEXT_HOST_MSG == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER); 819 820 GuestDnDMsg *pMsg; 821 try 822 { 823 pMsg = new GuestDnDMsg(); 824 rc = pThis->i_sendURIDataLoop(pCtx, pMsg); 825 if (RT_SUCCESS(rc)) 826 { 827 rc = pThis->addMsg(pMsg); 828 if (RT_SUCCESS(rc)) /* Return message type & required parameter count to the guest. */ 829 { 830 LogFlowFunc(("GUEST_DND_GET_NEXT_HOST_MSG -> %RU32 (%RU32 params)\n", pMsg->getType(), pMsg->getCount())); 831 pCBData->uMsg = pMsg->getType(); 832 pCBData->cParms = pMsg->getCount(); 833 } 834 } 835 836 if (RT_FAILURE(rc)) 837 { 838 if (rc == VERR_NO_DATA) /* All URI objects processed? */ 839 { 840 /* Unregister this callback. */ 841 AssertPtr(pCtx->mpResp); 842 int rc2 = pCtx->mpResp->setCallback(uMsg, NULL /* PFNGUESTDNDCALLBACK */); 843 if (RT_FAILURE(rc2)) 844 LogFlowFunc(("Error: Unable to unregister callback for message %RU32, rc=%Rrc\n", uMsg, rc2)); 845 } 846 847 delete pMsg; 848 } 849 } 850 catch(std::bad_alloc & /*e*/) 851 { 852 rc = VERR_NO_MEMORY; 853 } 854 break; 855 } 856 case DragAndDropSvc::HOST_DND_HG_SND_DIR: 857 case DragAndDropSvc::HOST_DND_HG_SND_FILE_HDR: 858 case DragAndDropSvc::HOST_DND_HG_SND_FILE_DATA: 859 { 860 DragAndDropSvc::PVBOXDNDCBHGGETNEXTHOSTMSGDATA pCBData 861 = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGGETNEXTHOSTMSGDATA>(pvParms); 862 AssertPtr(pCBData); 863 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGGETNEXTHOSTMSGDATA) == cbParms, VERR_INVALID_PARAMETER); 864 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_GET_NEXT_HOST_MSG_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER); 865 866 GuestDnDMsg *pMsg = pThis->nextMsg(); 867 if (pMsg) 868 { 869 /* 870 * Sanity checks. 871 */ 872 if ( pCBData->uMsg != uMsg 873 || pCBData->paParms == NULL 874 || pCBData->cParms != pMsg->getCount()) 875 { 876 rc = VERR_INVALID_PARAMETER; 877 } 878 879 if (RT_SUCCESS(rc)) 880 { 881 LogFlowFunc(("Sending uMsg=%RU32, cParms=%RU32 ...\n", uMsg, pCBData->cParms)); 882 rc = HGCM::Message::copyParms(pMsg->getCount(), pMsg->getParms(), pCBData->paParms); 883 if (RT_SUCCESS(rc)) 884 { 885 pCBData->cParms = pMsg->getCount(); 886 pThis->removeNext(); 887 } 888 } 889 } 890 else 891 rc = VERR_NO_DATA; 892 893 LogFlowFunc(("Returning msg %RU32, rc=%Rrc\n", uMsg, rc)); 894 break; 895 } 896 default: 897 rc = VERR_NOT_SUPPORTED; 898 break; 899 } 900 901 if (RT_FAILURE(rc)) 902 { 903 if (pCtx->mURI.SemEvent != NIL_RTSEMEVENT) 904 { 905 LogFlowFunc(("Signalling ...\n")); 906 int rc2 = RTSemEventSignal(pCtx->mURI.SemEvent); 907 AssertRC(rc2); 908 } 909 } 910 911 LogFlowFuncLeaveRC(rc); 912 return rc; /* Tell the guest. */ 913 } 914 915 int GuestDnDTarget::i_sendURIData(PSENDDATACTX pCtx) 916 { 917 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 918 AssertPtr(pCtx->mpResp); 919 920 #define URI_DATA_IS_VALID_BREAK(x) \ 921 if (!x) \ 922 { \ 923 LogFlowFunc(("Invalid URI data value for \"" #x "\"\n")); \ 924 rc = VERR_INVALID_PARAMETER; \ 925 break; \ 926 } 927 928 void *pvBuf = RTMemAlloc(m_cbBlockSize); 929 if (!pvBuf) 930 return VERR_NO_MEMORY; 931 932 int rc; 933 934 #define REGISTER_CALLBACK(x) \ 935 rc = pCtx->mpResp->setCallback(x, i_sendURIDataCallback, pCtx); \ 936 if (RT_FAILURE(rc)) \ 937 return rc; 938 939 #define UNREGISTER_CALLBACK(x) \ 940 rc = pCtx->mpResp->setCallback(x, NULL); \ 941 AssertRC(rc); 942 943 /* 944 * Register callbacks. 945 */ 946 /* Generic callbacks. */ 947 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG); 948 /* Host callbacks. */ 949 REGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_DIR); 950 if (mData.mProtocolVersion >= 2) 951 REGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_FILE_HDR); 952 REGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_FILE_DATA); 953 954 do 955 { 956 /* 957 * Set our scratch buffer. 958 */ 959 pCtx->mURI.pvScratchBuf = pvBuf; 960 pCtx->mURI.cbScratchBuf = m_cbBlockSize; 961 962 /* Create event semaphore. */ 963 pCtx->mURI.SemEvent = NIL_RTSEMEVENT; 964 rc = RTSemEventCreate(&pCtx->mURI.SemEvent); 965 if (RT_FAILURE(rc)) 966 break; 967 968 /* 969 * Extract URI list from byte data. 970 */ 971 DnDURIList &lstURI = pCtx->mURI.lstURI; /* Use the URI list from the context. */ 972 973 const char *pszList = (const char *)&pCtx->mData.front(); 974 URI_DATA_IS_VALID_BREAK(pszList); 975 976 uint32_t cbList = pCtx->mData.size(); 977 URI_DATA_IS_VALID_BREAK(cbList); 978 979 RTCList<RTCString> lstURIOrg = RTCString(pszList, cbList).split("\r\n"); 980 URI_DATA_IS_VALID_BREAK(!lstURIOrg.isEmpty()); 981 982 rc = lstURI.AppendURIPathsFromList(lstURIOrg, 0 /* fFlags */); 983 if (RT_SUCCESS(rc)) 984 LogFlowFunc(("URI root objects: %zu, total bytes (raw data to transfer): %zu\n", 985 lstURI.RootCount(), lstURI.TotalBytes())); 986 else 987 break; 988 989 /* 990 * The first message always is the meta info for the data. The meta 991 * info *only* contains the root elements of an URI list. 992 * 993 * After the meta data we generate the messages required to send the data itself. 994 */ 995 Assert(!lstURI.IsEmpty()); 996 RTCString strData = lstURI.RootToString().c_str(); 997 size_t cbData = strData.length() + 1; /* Include terminating zero. */ 998 999 GuestDnDMsg Msg; 1000 Msg.setType(DragAndDropSvc::HOST_DND_HG_SND_DATA); 1001 Msg.setNextUInt32(pCtx->mScreenID); 1002 Msg.setNextPointer((void *)pCtx->mFormat.c_str(), (uint32_t)pCtx->mFormat.length() + 1); 1003 Msg.setNextUInt32((uint32_t)pCtx->mFormat.length() + 1); 1004 Msg.setNextPointer((void*)strData.c_str(), (uint32_t)cbData); 1005 Msg.setNextUInt32((uint32_t)cbData); 1006 1007 rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms()); 1008 if (RT_SUCCESS(rc)) 1009 { 1010 /* 1011 * Wait until our callback i_sendURIDataCallback triggered the 1012 * wait event. 1013 */ 1014 LogFlowFunc(("Waiting for URI callback ...\n")); 1015 rc = RTSemEventWait(pCtx->mURI.SemEvent, RT_INDEFINITE_WAIT); 1016 LogFlowFunc(("URI callback done\n")); 1017 } 1018 1019 } while (0); 1020 1021 if (pCtx->mURI.SemEvent != NIL_RTSEMEVENT) 1022 { 1023 RTSemEventDestroy(pCtx->mURI.SemEvent); 1024 pCtx->mURI.SemEvent = NIL_RTSEMEVENT; 1025 } 1026 1027 /* 1028 * Unregister callbacksagain. 1029 */ 1030 /* Guest callbacks. */ 1031 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG); 1032 /* Host callbacks. */ 1033 UNREGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_DIR); 1034 if (mData.mProtocolVersion >= 2) 1035 UNREGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_FILE_HDR); 1036 UNREGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_FILE_DATA); 1037 1038 #undef REGISTER_CALLBACK 1039 #undef UNREGISTER_CALLBACK 1040 1041 if (pvBuf) 1042 RTMemFree(pvBuf); 1043 1044 #undef URI_DATA_IS_VALID_BREAK 1045 1046 LogFlowFuncLeaveRC(rc); 1047 return rc; 1048 } 1049 1050 int GuestDnDTarget::i_sendURIDataLoop(PSENDDATACTX pCtx, GuestDnDMsg *pMsg) 1051 { 1052 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 1053 1054 DnDURIList &lstURI = pCtx->mURI.lstURI; 1055 1056 int rc; 1057 1058 uint64_t cbTotal = pCtx->mURI.lstURI.TotalBytes(); 1059 uint8_t uPercent = pCtx->mURI.cbProcessed * 100 / (cbTotal ? cbTotal : 1); 1060 Assert(uPercent <= 100); 1061 1062 LogFlowFunc(("%RU64 / %RU64 -- %RU8%%\n", pCtx->mURI.cbProcessed, cbTotal, uPercent)); 1063 1064 bool fComplete = (uPercent >= 100) || lstURI.IsEmpty(); 1065 1066 if (pCtx->mpResp) 1067 { 1068 int rc2 = pCtx->mpResp->setProgress(uPercent, 1069 fComplete 1070 ? DragAndDropSvc::DND_PROGRESS_COMPLETE 1071 : DragAndDropSvc::DND_PROGRESS_RUNNING); 1072 AssertRC(rc2); 1073 } 1074 1075 if (fComplete) 1076 { 1077 LogFlowFunc(("Last URI item processed, bailing out\n")); 1078 return VERR_NO_DATA; 1079 } 1080 1081 Assert(!lstURI.IsEmpty()); 1082 DnDURIObject &curObj = lstURI.First(); 1083 1084 uint32_t fMode = curObj.GetMode(); 1085 LogFlowFunc(("Processing srcPath=%s, dstPath=%s, fMode=0x%x, cbSize=%RU32, fIsDir=%RTbool, fIsFile=%RTbool\n", 1086 curObj.GetSourcePath().c_str(), curObj.GetDestPath().c_str(), 1087 fMode, curObj.GetSize(), 1088 RTFS_IS_DIRECTORY(fMode), RTFS_IS_FILE(fMode))); 1089 1090 if (RTFS_IS_DIRECTORY(fMode)) 1091 { 1092 rc = i_sendDirectory(pCtx, pMsg, curObj); 1093 } 1094 else if (RTFS_IS_FILE(fMode)) 1095 { 1096 rc = i_sendFile(pCtx, pMsg, curObj); 1097 } 1098 else 1099 { 1100 AssertMsgFailed(("fMode=0x%x is not supported for srcPath=%s, dstPath=%s\n", 1101 fMode, curObj.GetSourcePath().c_str(), curObj.GetDestPath().c_str())); 1102 rc = VERR_NOT_SUPPORTED; 1103 } 1104 1105 bool fRemove = false; /* Remove current entry? */ 1106 if ( curObj.IsComplete() 1107 || RT_FAILURE(rc)) 1108 { 1109 fRemove = true; 1110 } 1111 1112 if (fRemove) 1113 { 1114 LogFlowFunc(("Removing \"%s\" from list, rc=%Rrc\n", curObj.GetSourcePath().c_str(), rc)); 1115 lstURI.RemoveFirst(); 1116 } 1117 1118 if ( pCtx->mpResp 1119 && pCtx->mpResp->isProgressCanceled()) 1120 { 1121 LogFlowFunc(("Cancelling ...\n")); 1122 1123 rc = i_cancelOperation(); 1124 if (RT_SUCCESS(rc)) 1125 rc = VERR_CANCELLED; 1126 } 1127 1128 LogFlowFuncLeaveRC(rc); 1129 return rc; 1130 } 1131 1132 HRESULT GuestDnDTarget::cancel(BOOL *aVeto) 1133 { 1134 #if !defined(VBOX_WITH_DRAG_AND_DROP) 1135 ReturnComNotImplemented(); 1136 #else /* VBOX_WITH_DRAG_AND_DROP */ 1137 1138 int rc = i_cancelOperation(); 1139 1140 if (aVeto) 1141 *aVeto = FALSE; /** @todo */ 1142 1143 return RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR; 1144 #endif /* VBOX_WITH_DRAG_AND_DROP */ 1145 } 1146
Note:
See TracChangeset
for help on using the changeset viewer.