VirtualBox

Changeset 55422 in vbox for trunk/src/VBox/Main/src-client


Ignore:
Timestamp:
Apr 24, 2015 1:52:33 PM (10 years ago)
Author:
vboxsync
Message:

DnD: Protocol overhaul with versioning added which now can communicate with Main.

Location:
trunk/src/VBox/Main/src-client
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/src-client/GuestDnDPrivate.cpp

    r55244 r55422  
    66
    77/*
    8  * Copyright (C) 2011-2014 Oracle Corporation
     8 * Copyright (C) 2011-2015 Oracle Corporation
    99 *
    1010 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    3737# include <VBox/GuestHost/DragAndDrop.h>
    3838# include <VBox/HostServices/DragAndDropSvc.h>
     39# include <VBox/version.h>
    3940
    4041# ifdef LOG_GROUP
     
    167168    , m_cbDataCurrent(0)
    168169    , m_cbDataTotal(0)
    169     , m_hFile(NIL_RTFILE)
    170170    , m_parent(pGuest)
    171171{
    172172    int rc = RTSemEventCreate(&m_EventSem);
    173     AssertRC(rc);
     173    if (RT_FAILURE(rc))
     174        throw rc;
    174175}
    175176
     
    242243}
    243244
    244 int GuestDnDResponse::notifyAboutGuestResponse(void)
     245int GuestDnDResponse::notifyAboutGuestResponse(void) const
    245246{
    246247    return RTSemEventSignal(m_EventSem);
     
    262263        m_pvData = NULL;
    263264    }
    264     m_cbData = 0;
     265
     266    m_cbData        = 0;
    265267    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;
    274269}
    275270
     
    280275    if (SUCCEEDED(rc))
    281276    {
    282         rc = m_progress->init(static_cast<IGuest*>(pParent),
     277        rc = m_progress->init(static_cast<IGuest *>(pParent),
    283278                              Bstr(pParent->tr("Dropping data")).raw(),
    284                               FALSE /* fCancelable */);
     279                              TRUE /* aCancelable */);
    285280    }
    286281    return rc;
    287282}
    288283
     284bool 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
     297int 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
    289321int GuestDnDResponse::setProgress(unsigned uPercentage,
    290                                   uint32_t uState, int rcOp /* = VINF_SUCCESS */)
    291 {
    292     LogFlowFunc(("uPercentage=%RU32, uState=%RU32, rcOp=%Rrc\n",
    293                  uPercentage, uState, rcOp));
     322                                  uint32_t uStatus, int rcOp /* = VINF_SUCCESS */)
     323{
     324    LogFlowFunc(("uStatus=%RU32, uPercentage=%RU32, rcOp=%Rrc\n",
     325                 uStatus, uPercentage, rcOp));
    294326
    295327    int vrc = VINF_SUCCESS;
     
    298330        BOOL fCompleted;
    299331        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
    300340        if (!fCompleted)
    301341        {
    302             if (uState == DragAndDropSvc::DND_PROGRESS_ERROR)
     342            switch (uStatus)
    303343            {
    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                {
    324356                    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;
    325382            }
    326383        }
    327384    }
    328385
     386    LogFlowFuncLeaveRC(vrc);
    329387    return vrc;
    330388}
     
    348406    /** @todo Don't use anonymous enums (uint32_t). */
    349407    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));
    357412    AssertMsg(m_cbDataCurrent <= m_cbDataTotal,
    358413              ("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
    361416    int rc = setProgress(cPercentage, uStatus);
    362417
     
    364419     *        guest should first clean up stuff itself and than really confirm
    365420     *        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);
    368422
    369423    LogFlowFuncLeaveRC(rc);
     
    371425}
    372426
     427int 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
    373515HRESULT GuestDnDResponse::queryProgressTo(IProgress **ppProgress)
    374516{
     
    376518}
    377519
    378 int GuestDnDResponse::waitForGuestResponse(RTMSINTERVAL msTimeout /*= 500 */)
     520int GuestDnDResponse::waitForGuestResponse(RTMSINTERVAL msTimeout /*= 500 */) const
    379521{
    380522    int rc = RTSemEventWait(m_EventSem, msTimeout);
     
    382524    LogFlowFunc(("msTimeout=%RU32, rc=%Rrc\n", msTimeout, rc));
    383525#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! We
    391      *        might want to have a table in DnDGuestImpl which
    392      *        keeps those file pointers around, or extend the
    393      *        actual protocol for explicit open calls.
    394      *
    395      *        For now we only keep one file open at a time, so if
    396      *        a client does alternating writes to different files
    397      *        this function will close the old and re-open the new
    398      *        file on every call. */
    399     int rc;
    400     if (   m_hFile == NIL_RTFILE
    401         || 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_WRITE
    410                             | 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         else
    423             rc = VERR_NO_MEMORY;
    424     }
    425     else
    426         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 
    437526    return rc;
    438527}
     
    507596        return VERR_COM_OBJECT_NOT_FOUND;
    508597
    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));
    513600    return rc;
    514601}
     602
     603/* static */
     604DECLCALLBACK(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
    515623
    516624/* static */
     
    637745}
    638746
    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_GH
    694         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_GH
    768 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     else
    802         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 
    829747///////////////////////////////////////////////////////////////////////////////
    830748
     
    834752}
    835753
    836 HRESULT GuestDnDBase::isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported)
     754HRESULT GuestDnDBase::i_isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported)
    837755{
    838756    *aSupported = std::find(m_strFormats.begin(),
     
    842760}
    843761
    844 HRESULT GuestDnDBase::getFormats(std::vector<com::Utf8Str> &aFormats)
     762HRESULT GuestDnDBase::i_getFormats(std::vector<com::Utf8Str> &aFormats)
    845763{
    846764    aFormats = m_strFormats;
     
    849767}
    850768
    851 HRESULT GuestDnDBase::addFormats(const std::vector<com::Utf8Str> &aFormats)
     769HRESULT GuestDnDBase::i_addFormats(const std::vector<com::Utf8Str> &aFormats)
    852770{
    853771    for (size_t i = 0; i < aFormats.size(); ++i)
     
    864782}
    865783
    866 HRESULT GuestDnDBase::removeFormats(const std::vector<com::Utf8Str> &aFormats)
     784HRESULT GuestDnDBase::i_removeFormats(const std::vector<com::Utf8Str> &aFormats)
    867785{
    868786    for (size_t i = 0; i < aFormats.size(); ++i)
     
    878796}
    879797
     798HRESULT GuestDnDBase::i_getProtocolVersion(ULONG *puVersion)
     799{
     800    int rc = getProtocolVersion((uint32_t *)puVersion);
     801    return RT_SUCCESS(rc) ? S_OK : E_FAIL;
     802}
     803
     804int 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 */
     854DECLCALLBACK(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
    880874#endif /* VBOX_WITH_DRAG_AND_DROP */
    881875
  • trunk/src/VBox/Main/src-client/GuestDnDSourceImpl.cpp

    r55180 r55422  
    55
    66/*
    7  * Copyright (C) 2014 Oracle Corporation
     7 * Copyright (C) 2014-2015 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    2727#include "AutoCaller.h"
    2828
     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
    2935#include <iprt/cpp/utils.h> /* For unconst(). */
    3036
     
    3945#include <VBox/log.h>
    4046
     47/**
     48 * Base class for a source task.
     49 */
     50class GuestDnDSourceTask
     51{
     52public:
     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
     64protected:
     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 */
     74class RecvDataTask : public GuestDnDSourceTask
     75{
     76public:
     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
     93protected:
     94
     95    /** Pointer to receive data context. */
     96    PRECVDATACTX mpCtx;
     97};
    4198
    4299// constructor / destructor
     
    95152/////////////////////////////////////////////////////////////////////////////
    96153
    97 HRESULT GuestDnDSource::isFormatSupported(const com::Utf8Str &aFormat,
    98                                           BOOL *aSupported)
     154HRESULT GuestDnDSource::isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported)
    99155{
    100156#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
     
    107163    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    108164
    109     return GuestDnDBase::isFormatSupported(aFormat, aSupported);
     165    return GuestDnDBase::i_isFormatSupported(aFormat, aSupported);
    110166#endif /* VBOX_WITH_DRAG_AND_DROP */
    111167}
     
    122178    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    123179
    124     return GuestDnDBase::getFormats(aFormats);
     180    return GuestDnDBase::i_getFormats(aFormats);
    125181#endif /* VBOX_WITH_DRAG_AND_DROP */
    126182}
     
    137193    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    138194
    139     return GuestDnDBase::addFormats(aFormats);
     195    return GuestDnDBase::i_addFormats(aFormats);
    140196#endif /* VBOX_WITH_DRAG_AND_DROP */
    141197}
     
    152208    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    153209
    154     return GuestDnDBase::removeFormats(aFormats);
     210    return GuestDnDBase::i_removeFormats(aFormats);
     211#endif /* VBOX_WITH_DRAG_AND_DROP */
     212}
     213
     214HRESULT 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);
    155226#endif /* VBOX_WITH_DRAG_AND_DROP */
    156227}
     
    171242    if (FAILED(autoCaller.rc())) return autoCaller.rc();
    172243
     244    /* Determine guest DnD protocol to use. */
     245    GuestDnDBase::getProtocolVersion(&mData.mProtocolVersion);
     246
    173247    /* Default is ignoring the action. */
    174248    DnDAction_T defaultAction = DnDAction_Ignore;
     
    180254    paParms[i++].setUInt32(uScreenId);
    181255
    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);
    184257    if (RT_SUCCESS(rc))
    185258    {
     
    210283    if (RT_FAILURE(rc))
    211284        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);
    213286
    214287    LogFlowFunc(("hr=%Rhrc, defaultAction=0x%x\n", hr, defaultAction));
     
    238311    HRESULT hr = S_OK;
    239312
    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. */
    245314    GuestDnDResponse *pResp = GuestDnDInst()->response();
    246315    if (pResp)
    247316    {
    248         /* Reset any old data. */
    249         pResp->reset();
    250317        pResp->resetProgress(m_pGuest);
    251318
    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(...) */
    286354
    287355    LogFlowFunc(("Returning hr=%Rhrc\n", hr));
     
    313381
    314382            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));
    317384
    318385            try
     
    329396                        size_t cbURIs = strURIs.length();
    330397
    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));
    333399
    334400                        aData.resize(cbURIs + 1 /* Include termination */);
     
    362428}
    363429
     430// implementation of internal methods.
     431/////////////////////////////////////////////////////////////////////////////
     432
     433#ifdef VBOX_WITH_DRAG_AND_DROP_GH
     434int 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
     494int 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
     516int 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
     582int 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
     636int 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 */
     698DECLCALLBACK(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
     718int 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
     785int 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 */
     885DECLCALLBACK(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 */
     941DECLCALLBACK(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  
    55
    66/*
    7  * Copyright (C) 2014 Oracle Corporation
     7 * Copyright (C) 2014-2015 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    2222#include "GuestImpl.h"
    2323#include "GuestDnDTargetImpl.h"
     24#include "VirtualBoxErrorInfoImpl.h"
    2425
    2526#include "Global.h"
     
    2728
    2829#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>
    2936#include <iprt/cpp/utils.h> /* For unconst(). */
    3037
    3138#include <VBox/com/array.h>
    32 #include <VBox/HostServices/DragAndDropSvc.h>
     39
     40#include <VBox/GuestHost/DragAndDrop.h>
     41#include <VBox/HostServices/Service.h>
    3342
    3443#ifdef LOG_GROUP
     
    3948
    4049
     50/**
     51 * Base class for a target task.
     52 */
     53class GuestDnDTargetTask
     54{
     55public:
     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
     67protected:
     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 */
     77class SendDataTask : public GuestDnDTargetTask
     78{
     79public:
     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
     97protected:
     98
     99    /** Pointer to send data context. */
     100    PSENDDATACTX mpCtx;
     101};
     102
    41103// constructor / destructor
    42104/////////////////////////////////////////////////////////////////////////////
     
    46108HRESULT GuestDnDTarget::FinalConstruct(void)
    47109{
     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
    48116    LogFlowThisFunc(("\n"));
    49117    return BaseFinalConstruct();
     
    94162/////////////////////////////////////////////////////////////////////////////
    95163
    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)
     164HRESULT GuestDnDTarget::isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported)
     165{
     166#if !defined(VBOX_WITH_DRAG_AND_DROP)
    100167    ReturnComNotImplemented();
    101168#else /* VBOX_WITH_DRAG_AND_DROP */
     
    106173    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    107174
    108     return GuestDnDBase::isFormatSupported(aFormat, aSupported);
     175    return GuestDnDBase::i_isFormatSupported(aFormat, aSupported);
    109176#endif /* VBOX_WITH_DRAG_AND_DROP */
    110177}
     
    112179HRESULT GuestDnDTarget::getFormats(std::vector<com::Utf8Str> &aFormats)
    113180{
    114 #if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
     181#if !defined(VBOX_WITH_DRAG_AND_DROP)
    115182    ReturnComNotImplemented();
    116183#else /* VBOX_WITH_DRAG_AND_DROP */
     
    121188    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    122189
    123     return GuestDnDBase::getFormats(aFormats);
     190    return GuestDnDBase::i_getFormats(aFormats);
    124191#endif /* VBOX_WITH_DRAG_AND_DROP */
    125192}
     
    127194HRESULT GuestDnDTarget::addFormats(const std::vector<com::Utf8Str> &aFormats)
    128195{
    129 #if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
     196#if !defined(VBOX_WITH_DRAG_AND_DROP)
    130197    ReturnComNotImplemented();
    131198#else /* VBOX_WITH_DRAG_AND_DROP */
     
    136203    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    137204
    138     return GuestDnDBase::addFormats(aFormats);
     205    return GuestDnDBase::i_addFormats(aFormats);
    139206#endif /* VBOX_WITH_DRAG_AND_DROP */
    140207}
     
    142209HRESULT GuestDnDTarget::removeFormats(const std::vector<com::Utf8Str> &aFormats)
    143210{
    144 #if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
     211#if !defined(VBOX_WITH_DRAG_AND_DROP)
    145212    ReturnComNotImplemented();
    146213#else /* VBOX_WITH_DRAG_AND_DROP */
     
    151218    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    152219
    153     return GuestDnDBase::removeFormats(aFormats);
     220    return GuestDnDBase::i_removeFormats(aFormats);
     221#endif /* VBOX_WITH_DRAG_AND_DROP */
     222}
     223
     224HRESULT 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);
    154236#endif /* VBOX_WITH_DRAG_AND_DROP */
    155237}
     
    164246                              DnDAction_T *aResultAction)
    165247{
    166 #if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
     248#if !defined(VBOX_WITH_DRAG_AND_DROP)
    167249    ReturnComNotImplemented();
    168250#else /* VBOX_WITH_DRAG_AND_DROP */
     
    178260    AutoCaller autoCaller(this);
    179261    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     262
     263    /* Determine guest DnD protocol to use. */
     264    GuestDnDBase::getProtocolVersion(&mData.mProtocolVersion);
    180265
    181266    /* Default action is ignoring. */
     
    237322                             DnDAction_T *aResultAction)
    238323{
    239 #if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
     324#if !defined(VBOX_WITH_DRAG_AND_DROP)
    240325    ReturnComNotImplemented();
    241326#else /* VBOX_WITH_DRAG_AND_DROP */
     
    299384HRESULT GuestDnDTarget::leave(ULONG uScreenId)
    300385{
    301 #if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
     386#if !defined(VBOX_WITH_DRAG_AND_DROP)
    302387    ReturnComNotImplemented();
    303388#else /* VBOX_WITH_DRAG_AND_DROP */
     
    327412                             com::Utf8Str &aFormat, DnDAction_T *aResultAction)
    328413{
    329 #if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
     414#if !defined(VBOX_WITH_DRAG_AND_DROP)
    330415    ReturnComNotImplemented();
    331416#else /* VBOX_WITH_DRAG_AND_DROP */
     
    395480}
    396481
    397 HRESULT GuestDnDTarget::sendData(ULONG aScreenId,
    398                                  const com::Utf8Str &aFormat,
    399                                  const std::vector<BYTE> &aData,
     482/* static */
     483DECLCALLBACK(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 */
     513HRESULT GuestDnDTarget::sendData(ULONG aScreenId, const com::Utf8Str &aFormat, const std::vector<BYTE> &aData,
    400514                                 ComPtr<IProgress> &aProgress)
    401515{
    402 #if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
     516#if !defined(VBOX_WITH_DRAG_AND_DROP)
    403517    ReturnComNotImplemented();
    404518#else /* VBOX_WITH_DRAG_AND_DROP */
    405519
    406     /* Input validation */
    407 
     520    /** @todo Add input validation. */
     521    /** @todo Check if another sendData() call currently is being processed. */
    408522
    409523    AutoCaller autoCaller(this);
    410524    if (FAILED(autoCaller.rc())) return autoCaller.rc();
    411525
    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. */
    422530    GuestDnDResponse *pResp = GuestDnDInst()->response();
    423531    if (pResp)
    424532    {
    425         /* Reset any old progress status. */
    426533        pResp->resetProgress(m_pGuest);
    427534
    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(...) */
    438570
    439571    return hr;
     
    441573}
    442574
     575int 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
     589int 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
     655int 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
     675int 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
     735int 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 */
     799DECLCALLBACK(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
     915int 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
     1050int 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
     1132HRESULT 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.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette