VirtualBox

Changeset 85371 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Jul 17, 2020 10:02:58 AM (5 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
139382
Message:

DnD: Revamped code to simplify / untangle of internal data handling:

  • C-ifying and renaming classes DnDURIList / DnDURIObject -> DnDTransferList / DnDTransferObject
  • Added testcases for DnDTransferList / DnDTransferObject + DnDPath API
  • Reduced memory footprint
  • Greatly simplified / stripped down internal data flow of Main side
  • More (optional) release logging for further diagnosis

Work in progress.

Location:
trunk/src/VBox
Files:
17 edited
2 moved

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxDnD.cpp

    r85121 r85371  
    10681068int VBoxDnDWnd::OnHgDataReceive(PVBGLR3GUESTDNDMETADATA pMeta)
    10691069{
    1070     LogFlowThisFunc(("mState=%ld, enmMetaType=%RU32, cbMeta=%RU32\n", mState, pMeta->enmType, pMeta->cbMeta));
    1071 
    1072     mState = Dropped;
    1073 
    1074     int rc = VINF_SUCCESS;
    1075     if (pMeta->pvMeta)
    1076     {
    1077         Assert(pMeta->cbMeta);
    1078         rc = RTCritSectEnter(&mCritSect);
     1070    LogFlowThisFunc(("mState=%ld, enmMetaType=%RU32\n", mState, pMeta->enmType));
     1071
     1072    int rc = RTCritSectEnter(&mCritSect);
     1073    if (RT_SUCCESS(rc))
     1074    {
     1075        mState = Dropped;
     1076
     1077        if (startupInfo.pDataObject)
     1078        {
     1079            switch (pMeta->enmType)
     1080            {
     1081                case VBGLR3GUESTDNDMETADATATYPE_RAW:
     1082                {
     1083                    AssertBreakStmt(pMeta->u.Raw.pvMeta != NULL, rc = VERR_INVALID_POINTER);
     1084                    AssertBreakStmt(pMeta->u.Raw.cbMeta, rc = VERR_INVALID_PARAMETER);
     1085
     1086                    rc = startupInfo.pDataObject->Signal(mFormatRequested, pMeta->u.Raw.pvMeta, pMeta->u.Raw.cbMeta);
     1087                    break;
     1088                }
     1089
     1090                case VBGLR3GUESTDNDMETADATATYPE_URI_LIST:
     1091                {
     1092                    LogRel2(("DnD: URI transfer root directory is '%s'\n", DnDTransferListGetRootPathAbs(&pMeta->u.URI.Transfer)));
     1093
     1094                    char  *pszBuf;
     1095                    size_t cbBuf;
     1096                    /* Note: The transfer list already has its root set to a temporary directory, so no need to set/add a new
     1097                     *       path base here. */
     1098                    rc = DnDTransferListGetRootsEx(&pMeta->u.URI.Transfer, DNDTRANSFERLISTFMT_NATIVE, NULL /* pszPathBase */,
     1099                                                   DND_PATH_SEPARATOR, &pszBuf, &cbBuf);
     1100                    if (RT_SUCCESS(rc))
     1101                    {
     1102                        rc = startupInfo.pDataObject->Signal(mFormatRequested, pszBuf, cbBuf);
     1103                        RTStrFree(pszBuf);
     1104                    }
     1105                    break;
     1106                }
     1107
     1108                default:
     1109                    AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
     1110                    break;
     1111            }
     1112        }
     1113        else
     1114            rc = VERR_NOT_FOUND;
     1115
     1116        int rc2 = mouseRelease();
    10791117        if (RT_SUCCESS(rc))
    1080         {
    1081             if (startupInfo.pDataObject)
    1082                 rc = startupInfo.pDataObject->Signal(mFormatRequested, pMeta->pvMeta, pMeta->cbMeta);
    1083             else
    1084                 rc = VERR_NOT_FOUND;
    1085 
    1086             RTCritSectLeave(&mCritSect);
    1087         }
    1088     }
    1089 
    1090     int rc2 = mouseRelease();
    1091     if (RT_SUCCESS(rc))
    1092         rc = rc2;
     1118            rc = rc2;
     1119
     1120        RTCritSectLeave(&mCritSect);
     1121    }
    10931122
    10941123    LogFlowFuncLeaveRC(rc);
  • trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxDnD.h

    r85121 r85371  
    7171    int Abort(void);
    7272    void SetStatus(Status status);
    73     int Signal(const RTCString &strFormat, const void *pvData, uint32_t cbData);
     73    int Signal(const RTCString &strFormat, const void *pvData, size_t cbData);
    7474
    7575protected:
     
    8888    RTCString   mstrFormat;
    8989    void       *mpvData;
    90     uint32_t    mcbData;
     90    size_t      mcbData;
    9191};
    9292
  • trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxDnDDataObject.cpp

    r82968 r85371  
    229229         * URI list handling.
    230230         */
    231         if (mstrFormat.equalsIgnoreCase("text/uri-list"))
     231        if (DnDMIMEHasFileURLs(mstrFormat.c_str(), RTSTR_MAX))
    232232        {
    233             int rc = VINF_SUCCESS;
    234 
    235             RTCList<RTCString> lstFilesURI = RTCString((char*)mpvData, mcbData).split("\r\n");
    236             RTCList<RTCString> lstFiles;
    237             for (size_t i = 0; i < lstFilesURI.size(); i++)
    238             {
    239                 char *pszFilePath = RTUriFilePath(lstFilesURI.at(i).c_str());
    240                 if (pszFilePath)
    241                 {
    242                     lstFiles.append(pszFilePath);
    243                     RTStrFree(pszFilePath);
    244                 }
    245                 else /* Unable to parse -- refuse entire request. */
    246                 {
    247                     lstFiles.clear();
    248                     rc = VERR_INVALID_PARAMETER;
    249                     break;
    250                 }
    251             }
    252 
    253             size_t cFiles = lstFiles.size();
     233            char **papszFiles;
     234            size_t cFiles;
     235            int rc = RTStrSplit((const char *)mpvData, mcbData, DND_PATH_SEPARATOR, &papszFiles, &cFiles);
    254236            if (   RT_SUCCESS(rc)
    255237                && cFiles)
    256238            {
    257 #ifdef DEBUG
    258                 LogFlowFunc(("Files (%zu)\n", cFiles));
     239                LogRel2(("DnD: Files (%zu)\n", cFiles));
    259240                for (size_t i = 0; i < cFiles; i++)
    260                     LogFlowFunc(("\tFile: %s\n", lstFiles.at(i).c_str()));
    261 #endif
     241                    LogRel2(("\tDnD: File '%s'\n", papszFiles[i]));
    262242
    263243#if 0
     
    310290                    for (size_t i = 0; i < cFiles; i++)
    311291                    {
    312                         cchFiles += strlen(lstFiles.at(i).c_str());
     292                        cchFiles += strlen(papszFiles[i]);
    313293                        cchFiles += 1; /* Terminating '\0'. */
    314294                    }
     
    328308                            size_t cchCurFile;
    329309                            PRTUTF16 pwszFile;
    330                             rc = RTStrToUtf16(lstFiles.at(i).c_str(), &pwszFile);
     310                            rc = RTStrToUtf16(papszFiles[i], &pwszFile);
    331311                            if (RT_SUCCESS(rc))
    332312                            {
     
    373353                        rc = VERR_NO_MEMORY;
    374354                }
     355
     356                for (size_t i = 0; i < cFiles; ++i)
     357                    RTStrFree(papszFiles[i]);
     358                RTMemFree(papszFiles);
    375359            }
    376360
     
    671655
    672656int VBoxDnDDataObject::Signal(const RTCString &strFormat,
    673                               const void *pvData, uint32_t cbData)
     657                              const void *pvData, size_t cbData)
    674658{
    675659    int rc;
  • trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxDnDDropTarget.cpp

    r82968 r85371  
    2828#include "VBox/HostServices/DragAndDropSvc.h"
    2929
     30#include <iprt/path.h>
    3031#include <iprt/utf16.h>
    3132#include <VBox/log.h>
     
    451452                                LogRel(("DnD: Adding guest file '%s'\n", pszFileUtf8));
    452453
    453                                 rc = RTStrAAppendExN(&pszFiles, 1 /* cPairs */, pszFileUtf8, cchFileUtf8);
    454454                                if (RT_SUCCESS(rc))
    455                                     cchFiles += cchFileUtf8;
     455                                {
     456                                    rc = RTStrAAppendExN(&pszFiles, 1 /* cPairs */, pszFileUtf8, cchFileUtf8);
     457                                    if (RT_SUCCESS(rc))
     458                                        cchFiles += cchFileUtf8;
     459                                }
    456460                            }
    457                             else
     461
     462                            if (RT_FAILURE(rc))
    458463                                LogRel(("DnD: Error handling file entry #%u, rc=%Rrc\n", i, rc));
    459464
    460                             if (pszFileUtf8)
    461                                 RTStrFree(pszFileUtf8);
     465                            RTStrFree(pszFileUtf8);
    462466
    463467                            if (RT_FAILURE(rc))
     
    479483                                         cFiles, cchFiles, cbFiles, pszFiles));
    480484
    481                             /* Translate the list into URI elements. */
    482                             DnDURIList lstURI;
    483                             rc = lstURI.AppendNativePathsFromList(pszFiles, cbFiles,
    484                                                                   DNDURILIST_FLAGS_ABSOLUTE_PATHS);
    485                             if (RT_SUCCESS(rc))
    486                             {
    487                                 RTCString strRoot = lstURI.GetRootEntries();
    488                                 size_t cbRoot = strRoot.length() + 1; /* Include termination */
    489 
    490                                 mpvData = RTMemAlloc(cbRoot);
    491                                 if (mpvData)
    492                                 {
    493                                     memcpy(mpvData, strRoot.c_str(), cbRoot);
    494                                     mcbData = cbRoot;
    495                                 }
    496                                 else
    497                                     rc = VERR_NO_MEMORY;
    498                             }
     485                            mpvData = pszFiles;
     486                            mcbData = cbFiles;
    499487                        }
    500 
    501                         LogFlowFunc(("Building CF_HDROP list rc=%Rrc, pszFiles=0x%p, cFiles=%RU16, cchFiles=%RU32\n",
    502                                      rc, pszFiles, cFiles, cchFiles));
    503 
    504                         if (pszFiles)
     488                        else
     489                        {
    505490                            RTStrFree(pszFiles);
     491                            pszFiles = NULL;
     492                        }
     493
     494                        LogFlowFunc(("Building CF_HDROP list rc=%Rrc, cFiles=%RU16, cchFiles=%RU32\n",
     495                                     rc, cFiles, cchFiles));
    506496                        break;
    507497                    }
  • trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxTray.cpp

    r85121 r85371  
    154154static VBOXSERVICEINFO g_aServices[] =
    155155{
    156     { &g_SvcDescClipboard,      NIL_RTTHREAD, NULL, false, false, false, false, true }
     156    {&g_SvcDescDnD,      NIL_RTTHREAD, NULL, false, false, false, false, true }
    157157};
    158158#else
  • trunk/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDragAndDrop.cpp

    r85029 r85371  
    349349 * @param   pDroppedFiles       Dropped files object to use for maintaining the file creation / locking.
    350350 */
    351 static int vbglR3DnDHGRecvURIData(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr, DnDDroppedFiles *pDroppedFiles)
     351static int vbglR3DnDHGRecvURIData(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr, PDNDDROPPEDFILES pDroppedFiles)
    352352{
    353353    AssertPtrReturn(pCtx,          VERR_INVALID_POINTER);
     
    384384     * Create and query the (unique) drop target directory in the user's temporary directory.
    385385     */
    386     int rc = pDroppedFiles->OpenTemp(0 /* fFlags */);
     386    int rc = DnDDroppedFilesOpenTemp(pDroppedFiles, 0 /* fFlags */);
    387387    if (RT_FAILURE(rc))
    388388    {
     
    391391    }
    392392
    393     const char *pszDropDir = pDroppedFiles->GetDirAbs();
     393    const char *pszDropDir = DnDDroppedFilesGetDirAbs(pDroppedFiles);
    394394    AssertPtr(pszDropDir);
    395395
     
    397397     * Enter the main loop of retieving files + directories.
    398398     */
    399     DnDURIObject objFile;
     399    DNDTRANSFEROBJECT objCur;
     400    RT_ZERO(objCur);
    400401
    401402    char szPathName[RTPATH_MAX] = { 0 };
     
    424425                                            &cbPathName,
    425426                                            &fMode);
    426                     LogFlowFunc(("HOST_DND_HG_SND_DIR pszPathName=%s, cbPathName=%RU32, fMode=0x%x, rc=%Rrc\n",
     427                    LogFlowFunc(("HOST_DND_HG_SND_DIR: "
     428                                 "pszPathName=%s, cbPathName=%RU32, fMode=0x%x, rc=%Rrc\n",
    427429                                 szPathName, cbPathName, fMode, rc));
    428430
     
    437439                        rc = RTDirCreate(pszPathAbs, fCreationMode, 0);
    438440                        if (RT_SUCCESS(rc))
    439                             rc = pDroppedFiles->AddDir(pszPathAbs);
     441                            rc = DnDDroppedFilesAddDir(pDroppedFiles, pszPathAbs);
    440442
    441443                        if (RT_SUCCESS(rc))
     
    452454                }
    453455                case HOST_DND_HG_SND_FILE_HDR:
     456                    RT_FALL_THROUGH();
    454457                case HOST_DND_HG_SND_FILE_DATA:
    455458                {
     
    489492
    490493                            /* Is there already a file open, e.g. in transfer? */
    491                             if (!objFile.IsOpen())
     494                            if (!DnDTransferObjectIsOpen(&objCur))
    492495                            {
    493                                 RTCString strPathAbs(pszPathAbs);
    494496#ifdef RT_OS_WINDOWS
    495497                                uint32_t fCreationMode = (fMode & RTFS_DOS_MASK) | RTFS_DOS_NT_NORMAL;
     
    497499                                uint32_t fCreationMode = (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR;
    498500#endif
    499                                 rc = objFile.Init(DnDURIObject::Type_File, strPathAbs);
     501                                rc = DnDTransferObjectInit(&objCur, DNDTRANSFEROBJTYPE_FILE,
     502                                                           pszDropDir /* Source (base) path */, szPathName /* Destination path */);
    500503                                if (RT_SUCCESS(rc))
    501504                                {
    502                                     rc = objFile.Open(fOpen, fCreationMode);
     505                                    rc = DnDTransferObjectOpen(&objCur, fOpen, fCreationMode, DNDTRANSFEROBJECT_FLAGS_NONE);
    503506                                    if (RT_SUCCESS(rc))
    504507                                    {
    505                                         rc = pDroppedFiles->AddFile(strPathAbs.c_str());
     508                                        rc = DnDDroppedFilesAddFile(pDroppedFiles, pszPathAbs);
    506509                                        if (RT_SUCCESS(rc))
    507510                                        {
    508511                                            cbFileWritten = 0;
    509                                             objFile.SetSize(cbFileSize);
     512                                            DnDTransferObjectSetSize(&objCur, cbFileSize);
    510513                                        }
    511514                                    }
     
    514517                            else
    515518                            {
    516                                 AssertMsgFailed(("ObjType=%RU32\n", objFile.GetType()));
     519                                AssertMsgFailed(("ObjType=%RU32\n", DnDTransferObjectGetType(&objCur)));
    517520                                rc = VERR_WRONG_ORDER;
    518521                            }
     
    529532                    {
    530533                        uint32_t cbChunkWritten;
    531                         rc = objFile.Write(pvChunk, cbChunkRead, &cbChunkWritten);
     534                        rc = DnDTransferObjectWrite(&objCur, pvChunk, cbChunkRead, &cbChunkWritten);
    532535                        if (RT_SUCCESS(rc))
    533536                        {
    534                             LogFlowFunc(("HOST_DND_HG_SND_FILE_DATA "
     537                            LogFlowFunc(("HOST_DND_HG_SND_FILE_DATA: "
    535538                                         "cbChunkRead=%RU32, cbChunkWritten=%RU32, cbFileWritten=%RU64 cbFileSize=%RU64\n",
    536539                                         cbChunkRead, cbChunkWritten, cbFileWritten + cbChunkWritten, cbFileSize));
     
    544547
    545548                    /* Data transfer complete? Close the file. */
    546                     bool fClose = objFile.IsComplete();
     549                    bool fClose = DnDTransferObjectIsComplete(&objCur);
    547550                    if (fClose)
    548551                    {
     
    557560                    {
    558561                        LogFlowFunc(("Closing file\n"));
    559                         objFile.Close();
     562                        DnDTransferObjectDestroy(&objCur);
    560563                    }
    561564
     
    604607    if (RT_FAILURE(rc))
    605608    {
    606         objFile.Close();
    607         pDroppedFiles->Rollback();
     609        DnDTransferObjectDestroy(&objCur);
     610        DnDDroppedFilesRollback(pDroppedFiles);
    608611    }
    609612    else
    610613    {
    611         /** @todo Compare the URI list with the dirs/files we really transferred. */
     614        /** @todo Compare the transfer list with the dirs/files we really transferred. */
    612615        /** @todo Implement checksum verification, if any. */
    613616    }
     
    618621     * by the client's drag'n drop operation lateron.
    619622     */
    620     int rc2 = pDroppedFiles->Reset(false /* fRemoveDropDir */);
     623    int rc2 = DnDDroppedFilesReset(pDroppedFiles, false /* fRemoveDropDir */);
    621624    if (RT_FAILURE(rc2)) /* Not fatal, don't report back to host. */
    622625        LogFlowFunc(("Closing dropped files directory failed with %Rrc\n", rc2));
     
    805808/**
    806809 * Host -> Guest
    807  * Main function for receiving the actual DnD data from the host, extended version.
    808  *
    809  * @returns IPRT status code.
    810  * @param   pCtx                DnD context to use.
    811  * @param   pEnmType            Where to store the meta data type. Optional.
    812  * @param   ppvData             Returns the received meta data. Needs to be free'd by the caller.  Optional.
    813  * @param   pcbData             Where to store the size (in bytes) of the received meta data. Optional.
    814  */
    815 static int vbglR3DnDHGRecvDataMainEx(PVBGLR3GUESTDNDCMDCTX        pCtx,
    816                                      VBGLR3GUESTDNDMETADATATYPE  *pEnmType,
    817                                      void                       **ppvData,
    818                                      uint32_t                    *pcbData)
     810 * Main function for receiving the actual DnD data from the host.
     811 *
     812 * @returns IPRT status code.
     813 * @param   pCtx                DnD context to use.
     814 * @param   pMeta               Where to store the actual meta data received from the host.
     815 */
     816static int vbglR3DnDHGRecvDataMain(PVBGLR3GUESTDNDCMDCTX   pCtx,
     817                                   PVBGLR3GUESTDNDMETADATA pMeta)
    819818{
    820819    AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
    821     /* The rest is optional. */
     820    AssertPtrReturn(pMeta, VERR_INVALID_POINTER);
    822821
    823822    VBOXDNDDATAHDR dataHdr;
     
    831830        return VERR_NO_MEMORY;
    832831
    833     DnDURIList lstURI;
    834     DnDDroppedFiles droppedFiles;
     832    DNDDROPPEDFILES droppedFiles;
    835833
    836834    void    *pvData = NULL;
     
    855853            Assert(cbData);
    856854
    857             rc = lstURI.SetFromURIData(pvData, cbData, 0 /* fFlags */);
     855            /* Use the dropped files directory as the root directory for the current transfer. */
     856            rc = DnDTransferListInit(&pMeta->u.URI.Transfer, DnDDroppedFilesGetDirAbs(&droppedFiles));
    858857            if (RT_SUCCESS(rc))
    859                 rc = vbglR3DnDHGRecvURIData(pCtx, &dataHdr, &droppedFiles);
    860 
    861             if (RT_SUCCESS(rc)) /** @todo Remove this block as soon as we hand in DnDURIList. */
    862858            {
    863                 if (pvData)
    864                 {
    865                     /* Reuse data buffer to fill in the transformed URI file list. */
    866                     RTMemFree(pvData);
    867                     pvData = NULL;
    868                 }
    869 
    870                 RTCString strData = lstURI.GetRootEntries(droppedFiles.GetDirAbs());
    871                 Assert(!strData.isEmpty());
    872 
    873                 cbData = strData.length() + 1;
    874                 LogFlowFunc(("URI list has %zu bytes\n", cbData));
    875 
    876                 pvData = RTMemAlloc(cbData);
    877                 if (pvData)
    878                 {
    879                     memcpy(pvData, strData.c_str(), cbData);
    880 
    881                     if (pEnmType)
    882                         *pEnmType = VBGLR3GUESTDNDMETADATATYPE_URI_LIST;
    883                 }
    884                 else
    885                     rc =  VERR_NO_MEMORY;
     859                rc = DnDTransferListAppendPathsFromBuffer(&pMeta->u.URI.Transfer, DNDTRANSFERLISTFMT_URI, (const char *)pvData, cbData,
     860                                                          DND_PATH_SEPARATOR, 0 /* fFlags */);
     861                if (RT_SUCCESS(rc))
     862                    rc = vbglR3DnDHGRecvURIData(pCtx, &dataHdr, &droppedFiles);
    886863            }
    887864        }
    888865        else /* Raw data. */
    889866        {
    890             if (pEnmType)
    891                 *pEnmType = VBGLR3GUESTDNDMETADATATYPE_RAW;
     867            pMeta->enmType = VBGLR3GUESTDNDMETADATATYPE_RAW;
    892868        }
    893869    }
     
    898874    if (RT_SUCCESS(rc))
    899875    {
    900         if (   pvData
    901             && cbData)
    902         {
    903             if (pcbData)
    904                 *pcbData = cbData;
    905             if (ppvData)
    906                 *ppvData = pvData;
    907             else
    908                 RTMemFree(pvData);
    909         }
     876
    910877    }
    911878    else if (   RT_FAILURE(rc)
     
    921888
    922889    LogFlowFuncLeaveRC(rc);
    923     return rc;
    924 }
    925 
    926 /**
    927  * Host -> Guest
    928  * Main function for receiving the actual DnD data from the host.
    929  *
    930  * @returns IPRT status code.
    931  * @param   pCtx                DnD context to use.
    932  * @param   pMeta               Where to store the actual meta data received from the host.
    933  */
    934 static int vbglR3DnDHGRecvDataMain(PVBGLR3GUESTDNDCMDCTX   pCtx,
    935                                    PVBGLR3GUESTDNDMETADATA pMeta)
    936 {
    937     AssertPtrReturn(pMeta, VERR_INVALID_POINTER);
    938 
    939     int rc = vbglR3DnDHGRecvDataMainEx(pCtx,
    940                                        &pMeta->enmType,
    941                                        &pMeta->pvMeta,
    942                                        &pMeta->cbMeta);
    943890    return rc;
    944891}
     
    13001247        {
    13011248            PVBGLR3GUESTDNDMETADATA pMeta = &pEvent->u.HG_Received.Meta;
    1302             if (pMeta->pvMeta)
     1249            switch (pMeta->enmType)
    13031250            {
    1304                 Assert(pMeta->cbMeta);
    1305                 RTMemFree(pMeta->pvMeta);
    1306                 pMeta->cbMeta = 0;
     1251                case VBGLR3GUESTDNDMETADATATYPE_RAW:
     1252                {
     1253                    if (pMeta->u.Raw.pvMeta)
     1254                    {
     1255                        Assert(pMeta->u.Raw.cbMeta);
     1256                        RTMemFree(pMeta->u.Raw.pvMeta);
     1257                        pMeta->u.Raw.cbMeta = 0;
     1258                    }
     1259                    break;
     1260                }
     1261
     1262                case VBGLR3GUESTDNDMETADATATYPE_URI_LIST:
     1263                {
     1264                    DnDTransferListDestroy(&pMeta->u.URI.Transfer);
     1265                    break;
     1266                }
     1267
     1268                default:
     1269                    break;
    13071270            }
    13081271            break;
     
    15031466 * @returns IPRT status code.
    15041467 * @param   pCtx                DnD context to use.
    1505  * @param   pObj                URI object containing the directory to send.
    1506  */
    1507 static int vbglR3DnDGHSendDir(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj)
     1468 * @param   pObj                transfer object containing the directory to send.
     1469 */
     1470static int vbglR3DnDGHSendDir(PVBGLR3GUESTDNDCMDCTX pCtx, DNDTRANSFEROBJECT *pObj)
    15081471{
    15091472    AssertPtrReturn(pObj,                                         VERR_INVALID_POINTER);
    15101473    AssertPtrReturn(pCtx,                                         VERR_INVALID_POINTER);
    1511     AssertReturn(pObj->GetType() == DnDURIObject::Type_Directory, VERR_INVALID_PARAMETER);
    1512 
    1513     RTCString strPath = pObj->GetPath();
    1514     LogFlowFunc(("strDir=%s (%zu), fMode=0x%x\n",
    1515                  strPath.c_str(), strPath.length(), pObj->GetMode()));
    1516 
    1517     if (strPath.length() > RTPATH_MAX)
     1474    AssertReturn(DnDTransferObjectGetType(pObj) == DNDTRANSFEROBJTYPE_DIRECTORY, VERR_INVALID_PARAMETER);
     1475
     1476    const char *pcszPath = DnDTransferObjectGetDestPath(pObj);
     1477    const size_t cchPath = RTStrNLen(pcszPath, RTPATH_MAX);
     1478    const RTFMODE fMode  = DnDTransferObjectGetMode(pObj);
     1479
     1480    LogFlowFunc(("strDir=%s (%zu), fMode=0x%x\n", pcszPath, cchPath, fMode));
     1481
     1482    if (cchPath > RTPATH_MAX) /* Can't happen, but check anyway. */
    15181483        return VERR_INVALID_PARAMETER;
    1519 
    1520     const uint32_t cbPath = (uint32_t)strPath.length() + 1; /* Include termination. */
    15211484
    15221485    HGCMMsgGHSendDir Msg;
     
    15241487    /** @todo Context ID not used yet. */
    15251488    Msg.u.v3.uContext.SetUInt32(0);
    1526     Msg.u.v3.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)cbPath);
    1527     Msg.u.v3.cbName.SetUInt32((uint32_t)cbPath);
    1528     Msg.u.v3.fMode.SetUInt32(pObj->GetMode());
     1489    Msg.u.v3.pvName.SetPtr((void *)pcszPath, (uint32_t)cchPath);
     1490    Msg.u.v3.cbName.SetUInt32((uint32_t)cchPath + 1); /* Include termination. */
     1491    Msg.u.v3.fMode.SetUInt32(fMode);
    15291492
    15301493    return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
     
    15371500 * @returns IPRT status code.
    15381501 * @param   pCtx                DnD context to use.
    1539  * @param   pObj                URI object containing the file to send.
    1540  */
    1541 static int vbglR3DnDGHSendFile(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj)
     1502 * @param   pObj                Transfer object containing the file to send.
     1503 */
     1504static int vbglR3DnDGHSendFile(PVBGLR3GUESTDNDCMDCTX pCtx, PDNDTRANSFEROBJECT pObj)
    15421505{
    15431506    AssertPtrReturn(pCtx,                                    VERR_INVALID_POINTER);
    15441507    AssertPtrReturn(pObj,                                    VERR_INVALID_POINTER);
    1545     AssertReturn(pObj->GetType() == DnDURIObject::Type_File, VERR_INVALID_PARAMETER);
    1546     AssertReturn(pObj->IsOpen(),                             VERR_INVALID_STATE);
     1508    AssertReturn(DnDTransferObjectGetType(pObj) == DNDTRANSFEROBJTYPE_FILE, VERR_INVALID_PARAMETER);
     1509    AssertReturn(DnDTransferObjectIsOpen(pObj),              VERR_INVALID_STATE);
    15471510
    15481511    uint32_t cbBuf = _64K;           /** @todo Make this configurable? */
     
    15511514        return VERR_NO_MEMORY;
    15521515
    1553     RTCString strPath = pObj->GetPath();
    1554 
    1555     LogFlowFunc(("strFile=%s (%zu), cbSize=%RU64, fMode=0x%x\n", strPath.c_str(), strPath.length(),
    1556                  pObj->GetSize(), pObj->GetMode()));
     1516    const char *pcszPath  = DnDTransferObjectGetDestPath(pObj);
     1517    const size_t cchPath  = RTStrNLen(pcszPath, RTPATH_MAX);
     1518    const uint64_t cbSize = DnDTransferObjectGetSize(pObj);
     1519    const RTFMODE fMode   = DnDTransferObjectGetMode(pObj);
     1520
     1521    LogFlowFunc(("strFile=%s (%zu), cbSize=%RU64, fMode=0x%x\n", pcszPath, cchPath, cbSize, fMode));
    15571522
    15581523    HGCMMsgGHSendFileHdr MsgHdr;
    15591524    VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_GH_SND_FILE_HDR, 6);
    15601525    MsgHdr.uContext.SetUInt32(0);                                                    /* Context ID; unused at the moment. */
    1561     MsgHdr.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)(strPath.length() + 1));
    1562     MsgHdr.cbName.SetUInt32((uint32_t)(strPath.length() + 1));
     1526    MsgHdr.pvName.SetPtr((void *)pcszPath, (uint32_t)(cchPath + 1));                 /* Include termination. */
     1527    MsgHdr.cbName.SetUInt32((uint32_t)(cchPath + 1));                                /* Ditto. */
    15631528    MsgHdr.uFlags.SetUInt32(0);                                                      /* Flags; unused at the moment. */
    1564     MsgHdr.fMode.SetUInt32(pObj->GetMode());                                         /* File mode */
    1565     MsgHdr.cbTotal.SetUInt64(pObj->GetSize());                                       /* File size (in bytes). */
     1529    MsgHdr.fMode.SetUInt32(fMode);                                                   /* File mode */
     1530    MsgHdr.cbTotal.SetUInt64(cbSize);                                                /* File size (in bytes). */
    15661531
    15671532    int rc = VbglR3HGCMCall(&MsgHdr.hdr, sizeof(MsgHdr));
     
    15801545        Msg.u.v3.cbChecksum.SetUInt32(0);
    15811546
    1582         uint64_t cbToReadTotal  = pObj->GetSize();
     1547        uint64_t cbToReadTotal  = cbSize;
    15831548        uint64_t cbWrittenTotal = 0;
    15841549        while (cbToReadTotal)
     
    15871552            uint32_t cbRead   = 0;
    15881553            if (cbToRead)
    1589                 rc = pObj->Read(pvBuf, cbToRead, &cbRead);
     1554                rc = DnDTransferObjectRead(pObj, pvBuf, cbToRead, &cbRead);
    15901555
    15911556            LogFlowFunc(("cbToReadTotal=%RU64, cbToRead=%RU32, cbRead=%RU32, rc=%Rrc\n",
     
    16121577            cbWrittenTotal += cbRead;
    16131578
    1614             LogFlowFunc(("%RU64/%RU64 -- %RU8%%\n", cbWrittenTotal, pObj->GetSize(), cbWrittenTotal * 100 / pObj->GetSize()));
     1579            LogFlowFunc(("%RU64/%RU64 -- %RU8%%\n", cbWrittenTotal, cbSize, cbWrittenTotal * 100 / cbSize));
    16151580        };
    16161581    }
     
    16241589/**
    16251590 * Guest -> Host
    1626  * Utility function to send an URI object from guest to the host.
    1627  *
    1628  * @returns IPRT status code.
    1629  * @param   pCtx                DnD context to use.
    1630  * @param   pObj                URI object to send from guest to the host.
    1631  */
    1632 static int vbglR3DnDGHSendURIObject(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj)
     1591 * Utility function to send a transfer object from guest to the host.
     1592 *
     1593 * @returns IPRT status code.
     1594 * @param   pCtx                DnD context to use.
     1595 * @param   pObj                Transfer object to send from guest to the host.
     1596 */
     1597static int vbglR3DnDGHSendURIObject(PVBGLR3GUESTDNDCMDCTX pCtx, PDNDTRANSFEROBJECT pObj)
    16331598{
    16341599    AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
     
    16371602    int rc;
    16381603
    1639     switch (pObj->GetType())
    1640     {
    1641         case DnDURIObject::Type_Directory:
     1604    const DNDTRANSFEROBJTYPE enmType = DnDTransferObjectGetType(pObj);
     1605
     1606    switch (enmType)
     1607    {
     1608        case DNDTRANSFEROBJTYPE_DIRECTORY:
    16421609            rc = vbglR3DnDGHSendDir(pCtx, pObj);
    16431610            break;
    16441611
    1645         case DnDURIObject::Type_File:
     1612        case DNDTRANSFEROBJTYPE_FILE:
    16461613            rc = vbglR3DnDGHSendFile(pCtx, pObj);
    16471614            break;
    16481615
    16491616        default:
    1650             AssertMsgFailed(("Object type %ld not implemented\n", pObj->GetType()));
     1617            AssertMsgFailed(("Object type %ld not implemented\n", enmType));
    16511618            rc = VERR_NOT_IMPLEMENTED;
    16521619            break;
     
    16821649/**
    16831650 * Guest -> Host
    1684  * Utility function to send URI data from guest to the host.
    1685  *
    1686  * @returns IPRT status code.
    1687  * @param   pCtx                DnD context to use.
    1688  * @param   pvData              Block to URI data to send.
    1689  * @param   cbData              Size (in bytes) of URI data to send.
    1690  */
    1691 static int vbglR3DnDGHSendURIData(PVBGLR3GUESTDNDCMDCTX pCtx, const void *pvData, size_t cbData)
    1692 {
    1693     AssertPtrReturn(pCtx,   VERR_INVALID_POINTER);
    1694     AssertPtrReturn(pvData, VERR_INVALID_POINTER);
    1695     AssertReturn(cbData,    VERR_INVALID_PARAMETER);
    1696 
    1697     RTCList<RTCString> lstPaths =
    1698         RTCString((const char *)pvData, cbData).split("\r\n");
    1699 
    1700     /** @todo Add symlink support (DNDURILIST_FLAGS_RESOLVE_SYMLINKS) here. */
    1701     /** @todo Add lazy loading (DNDURILIST_FLAGS_LAZY) here. */
    1702     uint32_t fFlags = DNDURILIST_FLAGS_KEEP_OPEN;
    1703 
    1704     DnDURIList lstURI;
    1705     int rc = lstURI.AppendURIPathsFromList(lstPaths, fFlags);
     1651 * Utility function to send transfer data from guest to the host.
     1652 *
     1653 * @returns IPRT status code.
     1654 * @param   pCtx                DnD context to use.
     1655 * @param   pTransferList       Dnd transfer list to send.
     1656 */
     1657static int vbglR3DnDGHSendTransferData(PVBGLR3GUESTDNDCMDCTX pCtx, PDNDTRANSFERLIST pTransferList)
     1658{
     1659    AssertPtrReturn(pCtx,VERR_INVALID_POINTER);
     1660    AssertPtrReturn(pTransferList, VERR_INVALID_POINTER);
     1661
     1662    /*
     1663     * Send the (meta) data; in case of URIs it's the root entries of a
     1664     * transfer list the host needs to know upfront to set up the drag'n drop operation.
     1665     */
     1666    char *pszList = NULL;
     1667    size_t cbList;
     1668    int rc = DnDTransferListGetRoots(pTransferList, DNDTRANSFERLISTFMT_URI, &pszList, &cbList);
     1669    if (RT_FAILURE(rc))
     1670        return rc;
     1671
     1672    void    *pvURIList = (void *)pszList;
     1673    uint32_t cbURLIist = (uint32_t)cbList;
     1674
     1675    /* The total size also contains the size of the meta data. */
     1676    uint64_t cbTotal  = cbURLIist;
     1677             cbTotal += DnDTransferListObjTotalBytes(pTransferList);
     1678
     1679    /* We're going to send a transfer list in text format. */
     1680    const char     szMetaFmt[] = "text/uri-list";
     1681    const uint32_t cbMetaFmt   = (uint32_t)strlen(szMetaFmt) + 1; /* Include termination. */
     1682
     1683    VBOXDNDDATAHDR dataHdr;
     1684    dataHdr.uFlags    = 0; /* Flags not used yet. */
     1685    dataHdr.cbTotal   = cbTotal;
     1686    dataHdr.cbMeta    = cbURLIist;
     1687    dataHdr.pvMetaFmt = (void *)szMetaFmt;
     1688    dataHdr.cbMetaFmt = cbMetaFmt;
     1689    dataHdr.cObjects  = DnDTransferListObjCount(pTransferList);
     1690
     1691    rc = vbglR3DnDGHSendDataInternal(pCtx, pvURIList, cbURLIist, &dataHdr);
     1692
    17061693    if (RT_SUCCESS(rc))
    17071694    {
    1708         /*
    1709          * Send the (meta) data; in case of URIs it's the (non-recursive) file/directory
    1710          * URI list the host needs to know upfront to set up the drag'n drop operation.
    1711          */
    1712         RTCString strRootDest = lstURI.GetRootEntries();
    1713         if (strRootDest.isNotEmpty())
     1695        while (DnDTransferListObjCount(pTransferList))
    17141696        {
    1715             void *pvURIList  = (void *)strRootDest.c_str(); /* URI root list. */
    1716             uint32_t cbURLIist = (uint32_t)strRootDest.length() + 1; /* Include string termination. */
    1717 
    1718             /* The total size also contains the size of the meta data. */
    1719             uint64_t cbTotal  = cbURLIist;
    1720                      cbTotal += lstURI.GetTotalBytes();
    1721 
    1722             /* We're going to send an URI list in text format. */
    1723             const char     szMetaFmt[] = "text/uri-list";
    1724             const uint32_t cbMetaFmt   = (uint32_t)strlen(szMetaFmt) + 1; /* Include termination. */
    1725 
    1726             VBOXDNDDATAHDR dataHdr;
    1727             dataHdr.uFlags    = 0; /* Flags not used yet. */
    1728             dataHdr.cbTotal   = cbTotal;
    1729             dataHdr.cbMeta    = cbURLIist;
    1730             dataHdr.pvMetaFmt = (void *)szMetaFmt;
    1731             dataHdr.cbMetaFmt = cbMetaFmt;
    1732             dataHdr.cObjects  = lstURI.GetTotalCount();
    1733 
    1734             rc = vbglR3DnDGHSendDataInternal(pCtx,
    1735                                              pvURIList, cbURLIist, &dataHdr);
    1736         }
    1737         else
    1738             rc = VERR_INVALID_PARAMETER;
    1739     }
    1740 
    1741     if (RT_SUCCESS(rc))
    1742     {
    1743         while (!lstURI.IsEmpty())
    1744         {
    1745             DnDURIObject *pNextObj = lstURI.First();
    1746 
    1747             rc = vbglR3DnDGHSendURIObject(pCtx, pNextObj);
     1697            PDNDTRANSFEROBJECT pObj = DnDTransferListObjGetFirst(pTransferList);
     1698
     1699            rc = vbglR3DnDGHSendURIObject(pCtx, pObj);
    17481700            if (RT_FAILURE(rc))
    17491701                break;
    17501702
    1751             lstURI.RemoveFirst();
     1703            DnDTransferListObjRemoveFirst(pTransferList);
    17521704        }
     1705
     1706        Assert(DnDTransferListObjCount(pTransferList) == 0);
    17531707    }
    17541708
     
    17791733    if (DnDMIMEHasFileURLs(pszFormat, strlen(pszFormat)))
    17801734    {
    1781         /* Send file data. */
    1782         rc = vbglR3DnDGHSendURIData(pCtx, pvData, cbData);
     1735        DNDTRANSFERLIST lstTransfer;
     1736        rc = DnDTransferListInit(&lstTransfer, NULL /* pcszRootPathAbs */);
     1737        if (RT_SUCCESS(rc))
     1738        {
     1739            /** @todo Add symlink support (DNDTRANSFERLIST_FLAGS_RESOLVE_SYMLINKS) here. */
     1740            /** @todo Add lazy loading (DNDTRANSFERLIST_FLAGS_LAZY) here. */
     1741            const DNDTRANSFERLISTFLAGS fFlags = DNDTRANSFERLIST_FLAGS_KEEP_OPEN;
     1742
     1743            rc = DnDTransferListAppendPathsFromBuffer(&lstTransfer, DNDTRANSFERLISTFMT_NATIVE, (const char *)pvData, cbData,
     1744                                                      DND_PATH_SEPARATOR, fFlags);
     1745            if (RT_SUCCESS(rc))
     1746                rc = vbglR3DnDGHSendTransferData(pCtx, &lstTransfer);
     1747            DnDTransferListDestroy(&lstTransfer);
     1748        }
    17831749    }
    17841750    else
  • trunk/src/VBox/Additions/x11/VBoxClient/draganddrop.cpp

    r82968 r85371  
    549549    int hgMove(uint32_t uPosX, uint32_t uPosY, VBOXDNDACTION dndActionDefault);
    550550    int hgDrop(uint32_t uPosX, uint32_t uPosY, VBOXDNDACTION dndActionDefault);
    551     int hgDataReceive(PVBGLR3GUESTDNDMETADATA pMetaData);
     551    int hgDataReceive(PVBGLR3GUESTDNDMETADATA pMeta);
    552552
    553553    /* X11 helpers. */
     
    19001900 *
    19011901 * @returns IPRT status code.
    1902  * @param   pMetaData               Pointer to meta data from host.
    1903  */
    1904 int DragInstance::hgDataReceive(PVBGLR3GUESTDNDMETADATA pMetaData)
     1902 * @param   pMeta               Pointer to meta data from host.
     1903 */
     1904int DragInstance::hgDataReceive(PVBGLR3GUESTDNDMETADATA pMeta)
    19051905{
    19061906    LogFlowThisFunc(("enmMode=%RU32, enmState=%RU32\n", m_enmMode, m_enmState));
    1907     LogFlowThisFunc(("enmMetaDataType=%RU32\n", pMetaData->enmType));
     1907    LogFlowThisFunc(("enmMetaType=%RU32\n", pMeta->enmType));
    19081908
    19091909    if (   m_enmMode  != HG
     
    19131913    }
    19141914
    1915     if (   pMetaData->pvMeta == NULL
    1916         || pMetaData->cbMeta == 0)
    1917     {
    1918         return VERR_INVALID_PARAMETER;
    1919     }
    1920 
    1921     int rc = VINF_SUCCESS;
    1922 
    1923     const void    *pvData = pMetaData->pvMeta;
    1924     const uint32_t cbData = pMetaData->cbMeta;
     1915    void  *pvData = NULL;
     1916    size_t cbData = 0;
     1917
     1918    int rc;
     1919
     1920    switch (pMeta->enmType)
     1921    {
     1922        case VBGLR3GUESTDNDMETADATATYPE_RAW:
     1923        {
     1924            AssertBreakStmt(pMeta->u.Raw.pvMeta != NULL, rc = VERR_INVALID_POINTER);
     1925            pvData = pMeta->u.Raw.pvMeta;
     1926            AssertBreakStmt(pMeta->u.Raw.cbMeta, rc = VERR_INVALID_PARAMETER);
     1927            cbData = pMeta->u.Raw.cbMeta;
     1928
     1929            rc = VINF_SUCCESS;
     1930            break;
     1931        }
     1932
     1933        case VBGLR3GUESTDNDMETADATATYPE_URI_LIST:
     1934        {
     1935            VBClLogInfo(("URI transfer root directory is '%s'\n", DnDTransferListGetRootPathAbs(&pMeta->u.URI.Transfer)));
     1936
     1937            /* Note: The transfer list already has its root set to a temporary directory, so no need to set/add a new
     1938             *       path base here. */
     1939            rc = DnDTransferListGetRootsEx(&pMeta->u.URI.Transfer, DNDTRANSFERLISTFMT_NATIVE, NULL /* pszPathBase */,
     1940                                           DND_PATH_SEPARATOR, (char **)&pvData, &cbData);
     1941            break;
     1942        }
     1943
     1944        default:
     1945            AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
     1946            break;
     1947    }
     1948
     1949    if (RT_FAILURE(rc))
     1950        return rc;
    19251951
    19261952    /*
     
    19281954     * be on the guest, so proceed working on communicating with the target window.
    19291955     */
    1930     VBClLogInfo("Received %RU32 bytes of URI list meta data from host\n", cbData);
     1956    VBClLogInfo("Received %RU32 bytes of meta data from host\n", cbData);
    19311957
    19321958    /* Destroy any old data. */
  • trunk/src/VBox/GuestHost/DragAndDrop/DnDDroppedFiles.cpp

    r85028 r85371  
    2727#include <iprt/err.h>
    2828#include <iprt/file.h>
     29#include <iprt/mem.h>
    2930#include <iprt/path.h>
    3031#include <iprt/string.h>
    3132
    32 
    3333#include <VBox/log.h>
    3434
    35 DnDDroppedFiles::DnDDroppedFiles(void)
    36     : m_fOpen(0)
    37     , m_hDir(NULL) { }
    38 
    39 DnDDroppedFiles::DnDDroppedFiles(const char *pszPath, DNDURIDROPPEDFILEFLAGS fFlags /* = DNDURIDROPPEDFILE_FLAGS_NONE */)
    40     : m_fOpen(0)
    41     , m_hDir(NULL)
    42 {
    43     OpenEx(pszPath, fFlags);
    44 }
    45 
    46 DnDDroppedFiles::~DnDDroppedFiles(void)
     35
     36/*********************************************************************************************************************************
     37*   Prototypes                                                                                                                   *
     38*********************************************************************************************************************************/
     39static int dndDroppedFilesCloseInternal(PDNDDROPPEDFILES pDF);
     40
     41
     42int DnDDroppedFilesInit(PDNDDROPPEDFILES pDF,
     43                        const char *pszPath, DNDURIDROPPEDFILEFLAGS fFlags /* = DNDURIDROPPEDFILE_FLAGS_NONE */)
     44{
     45    pDF->m_fOpen = 0;
     46    pDF->m_hDir  = NIL_RTDIR;
     47
     48    return DnDDroppedFilesOpenEx(pDF, pszPath, fFlags);
     49}
     50
     51void DnDDroppedFilesDestroy(PDNDDROPPEDFILES pDF)
    4752{
    4853    /* Only make sure to not leak any handles and stuff, don't delete any
    4954     * directories / files here. */
    50     closeInternal();
     55    dndDroppedFilesCloseInternal(pDF);
    5156}
    5257
     
    5762 * @param   pszFile             Path of file entry to add.
    5863 */
    59 int DnDDroppedFiles::AddFile(const char *pszFile)
     64int DnDDroppedFilesAddFile(PDNDDROPPEDFILES pDF, const char *pszFile)
    6065{
    6166    AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
    6267
    63     if (!this->m_lstFiles.contains(pszFile))
    64         this->m_lstFiles.append(pszFile);
    65     return VINF_SUCCESS;
     68    PDNDDROPPEDFILESENTRY pEntry = (PDNDDROPPEDFILESENTRY)RTMemAlloc(sizeof(DNDDROPPEDFILESENTRY));
     69    if (!pEntry)
     70        return VERR_NO_MEMORY;
     71
     72    pEntry->pszPath = RTStrDup(pszFile);
     73    if (pEntry->pszPath)
     74    {
     75        RTListAppend(&pDF->m_lstFiles, &pEntry->Node);
     76        return VINF_SUCCESS;
     77    }
     78
     79    RTMemFree(pEntry);
     80    return VERR_NO_MEMORY;
    6681}
    6782
     
    7388 * @param   pszDir              Path of directory entry to add.
    7489 */
    75 int DnDDroppedFiles::AddDir(const char *pszDir)
     90int DnDDroppedFilesAddDir(PDNDDROPPEDFILES pDF, const char *pszDir)
    7691{
    7792    AssertPtrReturn(pszDir, VERR_INVALID_POINTER);
    7893
    79     if (!this->m_lstDirs.contains(pszDir))
    80         this->m_lstDirs.append(pszDir);
    81     return VINF_SUCCESS;
     94    PDNDDROPPEDFILESENTRY pEntry = (PDNDDROPPEDFILESENTRY)RTMemAlloc(sizeof(DNDDROPPEDFILESENTRY));
     95    if (!pEntry)
     96        return VERR_NO_MEMORY;
     97
     98    pEntry->pszPath = RTStrDup(pszDir);
     99    if (pEntry->pszPath)
     100    {
     101        RTListAppend(&pDF->m_lstDirs, &pEntry->Node);
     102        return VINF_SUCCESS;
     103    }
     104
     105    RTMemFree(pEntry);
     106    return VERR_NO_MEMORY;
    82107}
    83108
     
    87112 * @returns VBox status code.
    88113 */
    89 int DnDDroppedFiles::closeInternal(void)
     114static int dndDroppedFilesCloseInternal(PDNDDROPPEDFILES pDF)
    90115{
    91116    int rc;
    92     if (this->m_hDir != NULL)
    93     {
    94         rc = RTDirClose(this->m_hDir);
     117    if (pDF->m_hDir != NULL)
     118    {
     119        rc = RTDirClose(pDF->m_hDir);
    95120        if (RT_SUCCESS(rc))
    96             this->m_hDir = NULL;
     121            pDF->m_hDir = NULL;
    97122    }
    98123    else
     
    108133 * @returns VBox status code.
    109134 */
    110 int DnDDroppedFiles::Close(void)
    111 {
    112     return closeInternal();
     135int DnDDroppedFilesClose(PDNDDROPPEDFILES pDF)
     136{
     137    return dndDroppedFilesCloseInternal(pDF);
    113138}
    114139
     
    118143 * @returns Pointer to absolute path of the dropped files directory.
    119144 */
    120 const char *DnDDroppedFiles::GetDirAbs(void) const
    121 {
    122     return this->m_strPathAbs.c_str();
     145const char *DnDDroppedFilesGetDirAbs(PDNDDROPPEDFILES pDF)
     146{
     147    return pDF->pszPathAbs;
    123148}
    124149
     
    128153 * @returns \c true if open, \c false if not.
    129154 */
    130 bool DnDDroppedFiles::IsOpen(void) const
    131 {
    132     return (this->m_hDir != NULL);
     155bool DnDDroppedFilesIsOpen(PDNDDROPPEDFILES pDF)
     156{
     157    return (pDF->m_hDir != NULL);
    133158}
    134159
     
    140165 * @param   fFlags              Dropped files flags to use for this directory.
    141166 */
    142 int DnDDroppedFiles::OpenEx(const char *pszPath, DNDURIDROPPEDFILEFLAGS fFlags /* = DNDURIDROPPEDFILE_FLAGS_NONE */)
     167int DnDDroppedFilesOpenEx(PDNDDROPPEDFILES pDF,
     168                          const char *pszPath, DNDURIDROPPEDFILEFLAGS fFlags /* = DNDURIDROPPEDFILE_FLAGS_NONE */)
    143169{
    144170    AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
     
    179205        }
    180206
    181         rc = DnDPathSanitize(szTime, sizeof(szTime));
     207        rc = DnDPathSanitizeFileName(szTime, sizeof(szTime));
    182208        if (RT_FAILURE(rc))
    183209            break;
     
    195221            if (RT_SUCCESS(rc))
    196222            {
    197                 this->m_hDir       = hDir;
    198                 this->m_strPathAbs = szDropDir;
    199                 this->m_fOpen      = fFlags;
     223                pDF->m_hDir     = hDir;
     224                pDF->pszPathAbs = szDropDir;
     225                pDF->m_fOpen    = fFlags;
    200226            }
    201227        }
     
    213239 * @param   fFlags              Dropped files flags to use for this directory.
    214240 */
    215 int DnDDroppedFiles::OpenTemp(DNDURIDROPPEDFILEFLAGS fFlags /* = DNDURIDROPPEDFILE_FLAGS_NONE */)
     241int DnDDroppedFilesOpenTemp(PDNDDROPPEDFILES pDF, DNDURIDROPPEDFILEFLAGS fFlags)
    216242{
    217243    AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER); /* Flags not supported yet. */
     
    225251    int rc = RTPathTemp(szTemp, sizeof(szTemp));
    226252    if (RT_SUCCESS(rc))
    227         rc = OpenEx(szTemp, fFlags);
    228 
    229     return rc;
     253        rc = DnDDroppedFilesOpenEx(pDF, szTemp, fFlags);
     254
     255    return rc;
     256}
     257
     258static void dndDroppedFilesEntryFree(PDNDDROPPEDFILESENTRY pEntry)
     259{
     260    if (!pEntry)
     261        return;
     262    RTStrFree(pEntry->pszPath);
     263    RTListNodeRemove(&pEntry->Node);
     264    RTMemFree(pEntry);
     265}
     266
     267static void dndDroppedFilesResetList(PRTLISTANCHOR pListAnchor)
     268{
     269    PDNDDROPPEDFILESENTRY pEntryCur, pEntryNext;
     270    RTListForEachSafe(pListAnchor, pEntryCur, pEntryNext, DNDDROPPEDFILESENTRY, Node)
     271        dndDroppedFilesEntryFree(pEntryCur);
     272    Assert(RTListIsEmpty(pListAnchor));
    230273}
    231274
     
    237280 *                              or just clear the internal references.
    238281 */
    239 int DnDDroppedFiles::Reset(bool fDelete)
    240 {
    241     int rc = closeInternal();
     282int DnDDroppedFilesReset(PDNDDROPPEDFILES pDF, bool fDelete)
     283{
     284    int rc = dndDroppedFilesCloseInternal(pDF);
    242285    if (RT_SUCCESS(rc))
    243286    {
    244287        if (fDelete)
    245288        {
    246             rc = Rollback();
     289            rc = DnDDroppedFilesRollback(pDF);
    247290        }
    248291        else
    249292        {
    250             this->m_lstDirs.clear();
    251             this->m_lstFiles.clear();
     293            dndDroppedFilesResetList(&pDF->m_lstDirs);
     294            dndDroppedFilesResetList(&pDF->m_lstFiles);
    252295        }
    253296    }
     
    262305 * @returns VBox status code, or VERR_NOT_FOUND if the dropped files directory has not been opened before.
    263306 */
    264 int DnDDroppedFiles::Reopen(void)
    265 {
    266     if (this->m_strPathAbs.isEmpty())
     307int DnDDroppedFilesReopen(PDNDDROPPEDFILES pDF)
     308{
     309    if (!pDF->pszPathAbs)
    267310        return VERR_NOT_FOUND;
    268311
    269     return OpenEx(this->m_strPathAbs.c_str(), this->m_fOpen);
     312    return DnDDroppedFilesOpenEx(pDF, pDF->pszPathAbs, pDF->m_fOpen);
    270313}
    271314
     
    276319 * @returns VBox status code.
    277320 */
    278 int DnDDroppedFiles::Rollback(void)
    279 {
    280     if (this->m_strPathAbs.isEmpty())
     321int DnDDroppedFilesRollback(PDNDDROPPEDFILES pDF)
     322{
     323    if (!pDF->pszPathAbs)
    281324        return VINF_SUCCESS;
    282325
     
    287330     *       anything recursive here! Steam (tm) knows best ... :-) */
    288331    int rc2;
    289     for (size_t i = 0; i < this->m_lstFiles.size(); i++)
    290     {
    291         rc2 = RTFileDelete(this->m_lstFiles.at(i).c_str());
     332    PDNDDROPPEDFILESENTRY pEntryCur, pEntryNext;
     333    RTListForEachSafe(&pDF->m_lstFiles, pEntryCur, pEntryNext, DNDDROPPEDFILESENTRY, Node)
     334    {
     335        rc2 = RTFileDelete(pEntryCur->pszPath);
    292336        if (RT_SUCCESS(rc2))
    293             this->m_lstFiles.removeAt(i);
     337            dndDroppedFilesEntryFree(pEntryCur);
    294338        else if (RT_SUCCESS(rc))
    295339           rc = rc2;
     
    297341    }
    298342
    299     for (size_t i = 0; i < this->m_lstDirs.size(); i++)
    300     {
    301         rc2 = RTDirRemove(this->m_lstDirs.at(i).c_str());
     343    RTListForEachSafe(&pDF->m_lstDirs, pEntryCur, pEntryNext, DNDDROPPEDFILESENTRY, Node)
     344    {
     345        rc2 = RTDirRemove(pEntryCur->pszPath);
    302346        if (RT_SUCCESS(rc2))
    303             this->m_lstDirs.removeAt(i);
     347            dndDroppedFilesEntryFree(pEntryCur);
    304348        else if (RT_SUCCESS(rc))
    305349            rc = rc2;
     
    309353    if (RT_SUCCESS(rc))
    310354    {
    311         Assert(this->m_lstFiles.isEmpty());
    312         Assert(this->m_lstDirs.isEmpty());
    313 
    314         rc2 = closeInternal();
     355        rc2 = dndDroppedFilesCloseInternal(pDF);
    315356        if (RT_SUCCESS(rc2))
    316357        {
    317358            /* Try to remove the empty root dropped files directory as well.
    318359             * Might return VERR_DIR_NOT_EMPTY or similar. */
    319             rc2 = RTDirRemove(this->m_strPathAbs.c_str());
     360            rc2 = RTDirRemove(pDF->pszPathAbs);
    320361        }
    321362        if (RT_SUCCESS(rc))
  • trunk/src/VBox/GuestHost/DragAndDrop/DnDMIME.cpp

    r82968 r85371  
    3535bool DnDMIMENeedsDropDir(const char *pcszFormat, size_t cchFormatMax)
    3636{
    37     bool fNeedsDropDir = false;
    38     if (!RTStrNICmp(pcszFormat, "text/uri-list", cchFormatMax)) /** @todo Add "x-special/gnome-icon-list"? */
    39         fNeedsDropDir = true;
    40 
    41     return fNeedsDropDir;
     37    return DnDMIMEHasFileURLs(pcszFormat, cchFormatMax);
    4238}
    4339
  • trunk/src/VBox/GuestHost/DragAndDrop/DnDPath.cpp

    r85028 r85371  
    2828#include <iprt/path.h>
    2929#include <iprt/string.h>
    30 
    31 
    32 /**
    33  * Sanitizes a path so that unsupported characters will be replaced by an underscore ("_").
     30#include <iprt/uri.h>
     31
     32
     33/**
     34 * Sanitizes the file name portion of a path so that unsupported characters will be replaced by an underscore ("_").
    3435 *
    3536 * @return  IPRT status code.
    36  * @param   pszPath             Path to sanitize.
    37  * @param   cbPath              Size (in bytes) of path to sanitize.
    38  */
    39 int DnDPathSanitize(char *pszPath, size_t cbPath)
    40 {
    41     if (!pszPath) /* No path given? Bail out early. */
     37 * @param   pszFileName         File name to sanitize.
     38 * @param   cbFileName          Size (in bytes) of file name to sanitize.
     39 */
     40int DnDPathSanitizeFileName(char *pszFileName, size_t cbFileName)
     41{
     42    if (!pszFileName) /* No path given? Bail out early. */
    4243        return VINF_SUCCESS;
    4344
    44     AssertReturn(cbPath, VERR_INVALID_PARAMETER);
     45    AssertReturn(cbFileName, VERR_INVALID_PARAMETER);
    4546
    4647    int rc = VINF_SUCCESS;
    4748#ifdef RT_OS_WINDOWS
    48     RT_NOREF1(cbPath);
     49    RT_NOREF1(cbFileName);
    4950    /* Replace out characters not allowed on Windows platforms, put in by RTTimeSpecToString(). */
    5051    /** @todo Use something like RTPathSanitize() if available later some time. */
     
    6263    };
    6364
    64     ssize_t cReplaced = RTStrPurgeComplementSet(pszPath, s_uszValidRangePairs, '_' /* chReplacement */);
     65    ssize_t cReplaced = RTStrPurgeComplementSet(pszFileName, s_uszValidRangePairs, '_' /* chReplacement */);
    6566    if (cReplaced < 0)
    6667        rc = VERR_INVALID_UTF8_ENCODING;
    6768#else
    68     RT_NOREF2(pszPath, cbPath);
     69    RT_NOREF2(pszFileName, cbFileName);
    6970#endif
    7071    return rc;
     
    7374/**
    7475 * Validates whether a given path matches our set of rules or not.
     76 *
     77 * Rules:
     78 * - An empty path is allowed.
     79 * - Dot components ("." or "..") are forbidden.
     80 * - If \a fMustExist is \c true, the path either has to be a file or a directory and must exist.
     81 * - Symbolic links are forbidden.
    7582 *
    7683 * @returns VBox status code.
     
    8188int DnDPathValidate(const char *pcszPath, bool fMustExist)
    8289{
     90    if (!pcszPath)
     91        return VERR_INVALID_POINTER;
     92
    8393    int rc = VINF_SUCCESS;
    84 
    85     if (!strlen(pcszPath))
    86         rc = VERR_INVALID_PARAMETER;
    8794
    8895    if (   RT_SUCCESS(rc)
     
    136143    AssertReturn(!(fFlags & ~DNDPATHCONVERT_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
    137144
    138 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    139     if (fFlags & DNDPATHCONVERT_FLAGS_TO_NATIVE)
    140         RTPathChangeToDosSlashes(pszPath, true);
     145    if (fFlags & DNDPATHCONVERT_FLAGS_TO_DOS)
     146        RTPathChangeToDosSlashes(pszPath, true /* fForce */);
    141147    else
    142 #else
    143     RT_NOREF(fFlags);
    144 #endif
    145     {
    146148        RTPathChangeToUnixSlashes(pszPath, true /* fForce */);
    147     }
    148149
    149150    return VINF_SUCCESS;
    150151}
    151152
     153/**
     154 * Rebases an absolute path from an old path base to a new path base.
     155 * Note: Does *not* do any path conversion.
     156 *
     157 * @return  IPRT status code.
     158 * @param   pcszPath            Path to rebase.
     159 * @param   strBaseOld          Old base path to rebase from. Optional and can be NULL.
     160 * @param   strBaseNew          New base path to rebase to.
     161 * @param   ppszPath            Where to store the allocated rebased path on success. Needs to be free'd with RTStrFree().
     162 */
     163int DnDPathRebase(const char *pcszPath, const char *pcszBaseOld, const char *pcszBaseNew,
     164                  char **ppszPath)
     165{
     166    AssertPtrReturn(pcszPath, VERR_INVALID_POINTER);
     167    AssertPtrReturn(pcszBaseOld, VERR_INVALID_POINTER);
     168    AssertPtrReturn(pcszBaseNew, VERR_INVALID_POINTER);
     169    AssertPtrReturn(ppszPath, VERR_INVALID_POINTER);
     170
     171    char szPath[RTPATH_MAX];
     172
     173    /* Do we need to see if the given path is part of the old base? */
     174    size_t idxBase;
     175    if (   pcszBaseOld
     176        && RTPathStartsWith(pcszPath, pcszBaseOld))
     177    {
     178        idxBase = strlen(pcszBaseOld);
     179    }
     180    else
     181        idxBase = 0;
     182
     183    int rc = RTStrCopy(szPath, sizeof(szPath), pcszBaseNew);
     184    if (RT_SUCCESS(rc))
     185    {
     186        rc = RTPathAppend(szPath, sizeof(szPath), &pcszPath[idxBase]);
     187        if (RT_SUCCESS(rc))
     188            rc = DnDPathValidate(szPath, false /* fMustExist */);
     189    }
     190
     191    if (RT_SUCCESS(rc))
     192    {
     193        char *pszPath = RTStrDup(szPath);
     194        if (pszPath)
     195            *ppszPath = pszPath;
     196        else
     197            rc = VERR_NO_MEMORY;
     198    }
     199
     200    return rc;
     201}
     202
  • trunk/src/VBox/GuestHost/DragAndDrop/DnDTransferList.cpp

    r85369 r85371  
    11/* $Id$ */
    22/** @file
    3  * DnD - URI list class.
     3 * DnD - transfer list implemenation.
    44 */
    55
     
    2727#include <iprt/file.h>
    2828#include <iprt/fs.h>
     29#include <iprt/mem.h>
    2930#include <iprt/path.h>
    3031#include <iprt/string.h>
     
    3536
    3637
    37 DnDURIList::DnDURIList(void)
    38     : m_cTotal(0)
    39     , m_cbTotal(0)
    40 {
    41 }
    42 
    43 DnDURIList::~DnDURIList(void)
    44 {
    45     Clear();
    46 }
    47 
    48 int DnDURIList::addEntry(const char *pcszSource, const char *pcszTarget, DNDURILISTFLAGS fFlags)
    49 {
    50     AssertPtrReturn(pcszSource, VERR_INVALID_POINTER);
    51     AssertPtrReturn(pcszTarget, VERR_INVALID_POINTER);
    52 
    53     LogFlowFunc(("pcszSource=%s, pcszTarget=%s, fFlags=0x%x\n", pcszSource, pcszTarget, fFlags));
    54 
    55     RTFSOBJINFO objInfo;
    56     int rc = RTPathQueryInfo(pcszSource, &objInfo, RTFSOBJATTRADD_NOTHING);
     38/*********************************************************************************************************************************
     39*   Prototypes                                                                                                                   *
     40*********************************************************************************************************************************/
     41static int dndTransferListSetRootPath(PDNDTRANSFERLIST pList, const char *pcszRootPathAbs);
     42
     43static int dndTransferListRootAdd(PDNDTRANSFERLIST pList, const char *pcszRoot);
     44static void dndTransferListRootFree(PDNDTRANSFERLIST pList, PDNDTRANSFERLISTROOT pRootObj);
     45
     46static int dndTransferListObjAdd(PDNDTRANSFERLIST pList, const char *pcszSrcAbs, RTFMODE fMode, DNDTRANSFERLISTFLAGS fFlags);
     47static void dndTransferListObjFree(PDNDTRANSFERLIST pList, PDNDTRANSFEROBJECT pLstObj);
     48
     49
     50/** The size of the directory entry buffer we're using. */
     51#define DNDTRANSFERLIST_DIRENTRY_BUF_SIZE (sizeof(RTDIRENTRYEX) + RTPATH_MAX)
     52
     53
     54/**
     55 * Initializes a transfer list.
     56 *
     57 * @returns VBox status code.
     58 * @param   pList               Transfer list to initialize.
     59 * @param   pcszRootPathAbs     Absolute root path to use for this list. Optional and can be NULL.
     60 */
     61int DnDTransferListInit(PDNDTRANSFERLIST pList, const char *pcszRootPathAbs)
     62{
     63    AssertPtrReturn(pList, VERR_INVALID_POINTER);
     64    /* pcszRootPathAbs is optional. */
     65
     66    if (!strlen(pcszRootPathAbs))
     67        return VERR_INVALID_PARAMETER;
     68
     69    if (pList->pszPathRootAbs)
     70        return VERR_WRONG_ORDER;
     71
     72    pList->pszPathRootAbs = NULL;
     73
     74    RTListInit(&pList->lstRoot);
     75    pList->cRoots = 0;
     76
     77    RTListInit(&pList->lstObj);
     78    pList->cObj = 0;
     79    pList->cbObjTotal = 0;
     80
     81    if (pcszRootPathAbs)
     82        return dndTransferListSetRootPath(pList, pcszRootPathAbs);
     83
     84    return VINF_SUCCESS;
     85}
     86
     87/**
     88 * Destroys a transfer list.
     89 *
     90 * @param   pList               Transfer list to destroy.
     91 */
     92void DnDTransferListDestroy(PDNDTRANSFERLIST pList)
     93{
     94    if (!pList)
     95        return;
     96
     97    DnDTransferListReset(pList);
     98
     99    RTStrFree(pList->pszPathRootAbs);
     100    pList->pszPathRootAbs = NULL;
     101}
     102
     103/**
     104 * Resets a transfer list.
     105 *
     106 * Note: Does *not* clear the root path!
     107 *
     108 * @param   pList               Transfer list to clear.
     109 */
     110void DnDTransferListReset(PDNDTRANSFERLIST pList)
     111{
     112    AssertPtrReturnVoid(pList);
     113
     114    /* Note: This does not clear the root path! */
     115
     116    PDNDTRANSFERLISTROOT pRootCur, pRootNext;
     117    RTListForEachSafe(&pList->lstRoot, pRootCur, pRootNext, DNDTRANSFERLISTROOT, Node)
     118        dndTransferListRootFree(pList, pRootCur);
     119    Assert(RTListIsEmpty(&pList->lstRoot));
     120
     121    PDNDTRANSFEROBJECT pObjCur, pObjNext;
     122    RTListForEachSafe(&pList->lstObj, pObjCur, pObjNext, DNDTRANSFEROBJECT, Node)
     123        dndTransferListObjFree(pList, pObjCur);
     124    Assert(RTListIsEmpty(&pList->lstObj));
     125
     126    Assert(pList->cRoots == 0);
     127    Assert(pList->cObj == 0);
     128
     129    pList->cbObjTotal = 0;
     130}
     131
     132/**
     133 * Adds a single transfer object entry to a transfer List.
     134 *
     135 * @returns VBox status code.
     136 * @param   pList               Transfer list to add entry to.
     137 * @param   pcszSrcAbs          Absolute source path (local) to use.
     138 * @param   fMode               File mode of entry to add.
     139 * @param   fFlags              Transfer list flags to use for appending.
     140 */
     141static int dndTransferListObjAdd(PDNDTRANSFERLIST pList, const char *pcszSrcAbs, RTFMODE fMode, DNDTRANSFERLISTFLAGS fFlags)
     142{
     143    AssertPtrReturn(pList, VERR_INVALID_POINTER);
     144    AssertPtrReturn(pcszSrcAbs, VERR_INVALID_POINTER);
     145
     146    LogFlowFunc(("pcszSrcAbs=%s, fMode=%#x, fFlags=0x%x\n", pcszSrcAbs, fMode, fFlags));
     147
     148    int rc = VINF_SUCCESS;
     149
     150    if (   !RTFS_IS_FILE(fMode)
     151        && !RTFS_IS_DIRECTORY(fMode))
     152        /** @todo Symlinks not allowed. */
     153    {
     154        rc = VERR_NOT_SUPPORTED;
     155    }
     156
    57157    if (RT_SUCCESS(rc))
    58158    {
    59         if (RTFS_IS_FILE(objInfo.Attr.fMode))
    60         {
    61             LogFlowFunc(("File '%s' -> '%s' (%RU64 bytes, file mode 0x%x)\n",
    62                          pcszSource, pcszTarget, (uint64_t)objInfo.cbObject, objInfo.Attr.fMode));
    63 
    64             DnDURIObject *pObjFile = new DnDURIObject(DnDURIObject::Type_File, pcszSource);
    65             if (pObjFile)
     159        /* Calculate the path to add as the destination path to our URI object. */
     160        const size_t idxPathToAdd = strlen(pList->pszPathRootAbs);
     161        AssertReturn(strlen(pcszSrcAbs) > idxPathToAdd, VERR_INVALID_PARAMETER); /* Should never happen (tm). */
     162
     163        PDNDTRANSFEROBJECT pObj = (PDNDTRANSFEROBJECT)RTMemAllocZ(sizeof(DNDTRANSFEROBJECT));
     164        if (pObj)
     165        {
     166            pObj = (PDNDTRANSFEROBJECT)RTMemAllocZ(sizeof(DNDTRANSFEROBJECT));
     167            if (pObj)
    66168            {
    67                 /** @todo Add a standard fOpen mode for this list. */
    68                 rc = pObjFile->Open(RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
     169                const bool fIsFile = RTFS_IS_FILE(fMode);
     170
     171                rc = DnDTransferObjectInit(pObj, fIsFile ? DNDTRANSFEROBJTYPE_FILE : DNDTRANSFEROBJTYPE_DIRECTORY,
     172                                           pList->pszPathRootAbs, &pcszSrcAbs[idxPathToAdd]);
    69173                if (RT_SUCCESS(rc))
    70174                {
    71                     m_lstTree.append(pObjFile);
    72 
    73                     m_cTotal++;
    74                     m_cbTotal += pObjFile->GetSize();
    75 
    76                     if (!(fFlags & DNDURILIST_FLAGS_KEEP_OPEN)) /* Shall we keep the file open while being added to this list? */
    77                         pObjFile->Close();
     175                    if (fIsFile)
     176                        rc = DnDTransferObjectOpen(pObj,
     177                                                   RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE, /** @todo Add a standard fOpen mode for this list. */
     178                                                   0 /* fMode */, DNDTRANSFEROBJECT_FLAGS_NONE);
     179                    if (RT_SUCCESS(rc))
     180                    {
     181                        RTListAppend(&pList->lstObj, &pObj->Node);
     182
     183                        pList->cObj++;
     184                        if (fIsFile)
     185                            pList->cbObjTotal += DnDTransferObjectGetSize(pObj);
     186
     187                        if (   fIsFile
     188                            && !(fFlags & DNDTRANSFERLIST_FLAGS_KEEP_OPEN)) /* Shall we keep the file open while being added to this list? */
     189                            DnDTransferObjectClose(pObj);
     190                    }
     191
     192                    if (RT_FAILURE(rc))
     193                        DnDTransferObjectDestroy(pObj);
    78194                }
    79195
    80196                if (RT_FAILURE(rc))
    81                     delete pObjFile;
     197                    RTMemFree(pObj);
    82198            }
    83199            else
    84200                rc = VERR_NO_MEMORY;
    85         }
    86         else if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
    87         {
    88             LogFlowFunc(("Directory '%s' -> '%s' (file mode 0x%x)\n", pcszSource, pcszTarget, objInfo.Attr.fMode));
    89 
    90             DnDURIObject *pObjDir = new DnDURIObject(DnDURIObject::Type_Directory, pcszSource);
    91             if (pObjDir)
     201
     202            if (RT_FAILURE(rc))
     203                RTMemFree(pObj);
     204        }
     205        else
     206            rc = VERR_NO_MEMORY;
     207    }
     208
     209    if (RT_FAILURE(rc))
     210        LogRel(("DnD: Adding entry '%s' of type %#x failed with rc=%Rrc\n", pcszSrcAbs, fMode & RTFS_TYPE_MASK, rc));
     211
     212    LogFlowFuncLeaveRC(rc);
     213    return rc;
     214}
     215
     216/**
     217 * Frees an internal DnD transfer list object.
     218 *
     219 * @param   pList               Transfer list to free object for.
     220 * @param   pLstObj             transfer list object to free. The pointer will be invalid after calling.
     221 */
     222static void dndTransferListObjFree(PDNDTRANSFERLIST pList, PDNDTRANSFEROBJECT pObj)
     223{
     224    if (!pObj)
     225        return;
     226
     227    DnDTransferObjectDestroy(pObj);
     228    RTListNodeRemove(&pObj->Node);
     229    RTMemFree(pObj);
     230
     231    AssertReturnVoid(pList->cObj);
     232    pList->cObj--;
     233}
     234
     235/**
     236 * Helper routine for handling adding sub directories.
     237 *
     238 * @return  IPRT status code.
     239 * @param   pList           transfer list to add found entries to.
     240 * @param   pszDir          Pointer to the directory buffer.
     241 * @param   cchDir          The length of pszDir in pszDir.
     242 * @param   pDirEntry       Pointer to the directory entry.
     243 * @param   fFlags          Flags of type DNDTRANSFERLISTFLAGS.
     244 */
     245static int dndTransferListAppendPathRecursiveSub(PDNDTRANSFERLIST pList,
     246                                                 char *pszDir, size_t cchDir, PRTDIRENTRYEX pDirEntry,
     247                                                 DNDTRANSFERLISTFLAGS fFlags)
     248
     249{
     250    Assert(cchDir > 0); Assert(pszDir[cchDir] == '\0');
     251
     252    /* Make sure we've got some room in the path, to save us extra work further down. */
     253    if (cchDir + 3 >= RTPATH_MAX)
     254        return VERR_BUFFER_OVERFLOW;
     255
     256    /* Open directory. */
     257    RTDIR hDir;
     258    int rc = RTDirOpen(&hDir, pszDir);
     259    if (RT_FAILURE(rc))
     260        return rc;
     261
     262    /* Ensure we've got a trailing slash (there is space for it see above). */
     263    if (!RTPATH_IS_SEP(pszDir[cchDir - 1]))
     264    {
     265        pszDir[cchDir++] = RTPATH_SLASH;
     266        pszDir[cchDir]   = '\0';
     267    }
     268
     269    LogFlowFunc(("pszDir=%s\n", pszDir));
     270
     271    /*
     272     * Process the files and subdirs.
     273     */
     274    for (;;)
     275    {
     276        /* Get the next directory. */
     277        size_t cbDirEntry = DNDTRANSFERLIST_DIRENTRY_BUF_SIZE;
     278        rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
     279        if (RT_FAILURE(rc))
     280            break;
     281
     282        /* Check length. */
     283        if (pDirEntry->cbName + cchDir + 3 >= RTPATH_MAX)
     284        {
     285            rc = VERR_BUFFER_OVERFLOW;
     286            break;
     287        }
     288
     289        switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
     290        {
     291            case RTFS_TYPE_SYMLINK:
    92292            {
    93                 m_lstTree.append(pObjDir);
    94 
    95                 /** @todo Add DNDURILIST_FLAGS_KEEP_OPEN handling? */
    96                 m_cTotal++;
     293                if (!(fFlags & DNDTRANSFERLIST_FLAGS_RESOLVE_SYMLINKS))
     294                    break;
     295                RT_FALL_THRU();
     296            }
     297            case RTFS_TYPE_DIRECTORY:
     298            {
     299                if (RTDirEntryExIsStdDotLink(pDirEntry))
     300                    continue;
     301
     302                memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);
     303                int rc2 = dndTransferListAppendPathRecursiveSub(pList, pszDir, cchDir + pDirEntry->cbName, pDirEntry, fFlags);
     304                if (RT_SUCCESS(rc))
     305                    rc = rc2;
     306                break;
     307            }
     308
     309            case RTFS_TYPE_FILE:
     310            {
     311                memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);
     312                rc = dndTransferListObjAdd(pList, pszDir, pDirEntry->Info.Attr.fMode, fFlags);
     313                break;
     314            }
     315
     316            default:
     317            {
     318
     319                break;
     320            }
     321        }
     322    }
     323
     324    if (rc == VERR_NO_MORE_FILES) /* Done reading current directory? */
     325    {
     326        rc = VINF_SUCCESS;
     327    }
     328    else if (RT_FAILURE(rc))
     329        LogRel(("DnD: Error while adding files recursively, rc=%Rrc\n", rc));
     330
     331    int rc2 = RTDirClose(hDir);
     332    if (RT_FAILURE(rc2))
     333    {
     334        if (RT_SUCCESS(rc))
     335            rc = rc2;
     336    }
     337
     338    return rc;
     339}
     340
     341/**
     342 * Appends a native system path recursively by adding these entries as transfer objects.
     343 *
     344 * @returns VBox status code.
     345 * @param   pList               Transfer list to add found entries to.
     346 * @param   pcszPathAbs         Absolute path to add.
     347 * @param   fFlags              Flags of type DNDTRANSFERLISTFLAGS.
     348 */
     349static int dndTransferListAppendPathNativeRecursive(PDNDTRANSFERLIST pList,
     350                                                    const char *pcszPathAbs, DNDTRANSFERLISTFLAGS fFlags)
     351{
     352    char szPathAbs[RTPATH_MAX];
     353    int rc = RTStrCopy(szPathAbs, sizeof(szPathAbs), pcszPathAbs);
     354    if (RT_FAILURE(rc))
     355        return rc;
     356
     357    union
     358    {
     359        uint8_t         abPadding[DNDTRANSFERLIST_DIRENTRY_BUF_SIZE];
     360        RTDIRENTRYEX    DirEntry;
     361    } uBuf;
     362    const size_t cchPathAbs = strlen(szPathAbs);
     363    if (!cchPathAbs)
     364        return VINF_SUCCESS;
     365    return dndTransferListAppendPathRecursiveSub(pList, szPathAbs, cchPathAbs, &uBuf.DirEntry, fFlags);
     366}
     367
     368static int dndTransferListAppendPathNative(PDNDTRANSFERLIST pList, const char *pcszPath, DNDTRANSFERLISTFLAGS fFlags)
     369{
     370    /* We don't want to have a relative directory here. */
     371    if (!RTPathStartsWithRoot(pcszPath))
     372        return VERR_INVALID_PARAMETER;
     373
     374    int rc = DnDPathValidate(pcszPath, false /* fMustExist */);
     375    if (RT_FAILURE(rc))
     376        return rc;
     377
     378    char szPathAbs[RTPATH_MAX];
     379    rc = RTStrCopy(szPathAbs, sizeof(szPathAbs), pcszPath);
     380    if (RT_FAILURE(rc))
     381        return rc;
     382
     383    size_t cchPathAbs = RTStrNLen(szPathAbs, sizeof(szPathAbs));
     384    AssertReturn(cchPathAbs, VERR_INVALID_PARAMETER);
     385
     386    /* Convert path to transport style. */
     387    rc = DnDPathConvert(szPathAbs, sizeof(szPathAbs), DNDPATHCONVERT_FLAGS_TRANSPORT);
     388    if (RT_SUCCESS(rc))
     389    {
     390        /* Make sure the path has the same root path as our list. */
     391        if (RTPathStartsWith(szPathAbs, pList->pszPathRootAbs))
     392        {
     393            RTDIR hDir;
     394            rc = RTDirOpen(&hDir, szPathAbs);
     395            if (RT_SUCCESS(rc))
     396            {
     397                for (;;)
     398                {
     399                    /* Get the next directory. */
     400                    RTDIRENTRYEX dirEntry;
     401                    rc = RTDirReadEx(hDir, &dirEntry, NULL, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK /** @todo No symlinks yet. */);
     402                    if (RT_SUCCESS(rc))
     403                    {
     404                        if (RTDirEntryExIsStdDotLink(&dirEntry))
     405                            continue;
     406
     407                        /* Check length. */
     408                        if (dirEntry.cbName + cchPathAbs + 3 >= sizeof(szPathAbs))
     409                        {
     410                            rc = VERR_BUFFER_OVERFLOW;
     411                            break;
     412                        }
     413
     414                        /* Append the directory entry to our absolute path. */
     415                        memcpy(&szPathAbs[cchPathAbs], dirEntry.szName, dirEntry.cbName + 1);
     416
     417                        LogFlowFunc(("szName=%s, szPathAbs=%s\n", dirEntry.szName, szPathAbs));
     418
     419                        switch (dirEntry.Info.Attr.fMode & RTFS_TYPE_MASK)
     420                        {
     421                            case RTFS_TYPE_DIRECTORY:
     422                            {
     423                                rc = dndTransferListAppendPathNativeRecursive(pList, szPathAbs, fFlags);
     424                                break;
     425                            }
     426
     427                            case RTFS_TYPE_FILE:
     428                            {
     429                                rc = dndTransferListObjAdd(pList, szPathAbs, dirEntry.Info.Attr.fMode, fFlags);
     430                                break;
     431                            }
     432
     433                            default:
     434                                rc = VERR_NOT_SUPPORTED;
     435                                break;
     436                        }
     437
     438                        if (RT_SUCCESS(rc))
     439                            rc = dndTransferListRootAdd(pList, dirEntry.szName);
     440                    }
     441                    else if (rc == VERR_NO_MORE_FILES)
     442                    {
     443                        rc = VINF_SUCCESS;
     444                        break;
     445                    }
     446                    else
     447                        break;
     448
     449                    if (RT_FAILURE(rc))
     450                        break;
     451                }
     452            }
     453        }
     454        else
     455            rc = VERR_INVALID_PARAMETER;
     456    }
     457
     458    if (RT_FAILURE(rc))
     459        LogRel(("DnD: Adding native path '%s' failed with rc=%Rrc\n", pcszPath, rc));
     460
     461    return rc;
     462}
     463
     464static int dndTransferListAppendPathURI(PDNDTRANSFERLIST pList, const char *pcszPath, DNDTRANSFERLISTFLAGS fFlags)
     465{
     466    RT_NOREF(fFlags);
     467
     468    /* Query the path component of a file URI. If this hasn't a
     469     * file scheme, NULL is returned. */
     470    char *pszFilePath;
     471    int rc = RTUriFilePathEx(pcszPath, RTPATH_STR_F_STYLE_UNIX, &pszFilePath, 0 /*cbPath*/, NULL /*pcchPath*/);
     472    if (RT_SUCCESS(rc))
     473    {
     474        LogFlowFunc(("pcszPath=%s -> pszFilePath=%s\n", pcszPath, pszFilePath));
     475        rc = dndTransferListRootAdd(pList, pszFilePath);
     476        RTStrFree(pszFilePath);
     477
     478    }
     479
     480    if (RT_FAILURE(rc))
     481        LogRel(("DnD: Adding URI path '%s' failed with rc=%Rrc\n", pcszPath, rc));
     482
     483    return rc;
     484}
     485
     486/**
     487 * Appends a single path to a transfer list.
     488 *
     489 * @returns VBox status code. VERR_NOT_SUPPORTED if the path is not supported.
     490 * @param   pList               Transfer list to append to.
     491 * @param   enmFmt              Format of \a pszPaths to append.
     492 * @param   pcszPath            Path to append. Must be part of the list's set root path.
     493 * @param   fFlags              Transfer list flags to use for appending.
     494 */
     495int DnDTransferListAppendPath(PDNDTRANSFERLIST pList,
     496                              DNDTRANSFERLISTFMT enmFmt, const char *pcszPath, DNDTRANSFERLISTFLAGS fFlags)
     497{
     498    AssertPtrReturn(pList, VERR_INVALID_POINTER);
     499    AssertPtrReturn(pcszPath, VERR_INVALID_POINTER);
     500    AssertReturn(!(fFlags & ~DNDTRANSFERLIST_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
     501    AssertReturn(!(fFlags & DNDTRANSFERLIST_FLAGS_RESOLVE_SYMLINKS), VERR_NOT_SUPPORTED);
     502
     503    int rc;
     504
     505    switch (enmFmt)
     506    {
     507        case DNDTRANSFERLISTFMT_NATIVE:
     508            rc = dndTransferListAppendPathNative(pList, pcszPath, fFlags);
     509            break;
     510
     511        case DNDTRANSFERLISTFMT_URI:
     512            rc = dndTransferListAppendPathURI(pList, pcszPath, fFlags);
     513            break;
     514
     515        default:
     516            AssertFailedStmt(rc = VERR_NOT_SUPPORTED);
     517            break; /* Never reached */
     518    }
     519
     520    return rc;
     521}
     522
     523/**
     524 * Appends native paths to a transfer list.
     525 *
     526 * @returns VBox status code.
     527 * @param   pList               Transfer list to append to.
     528 * @param   enmFmt              Format of \a pszPaths to append.
     529 * @param   pszPaths            Buffer of paths to append.
     530 * @param   cbPaths             Size (in bytes) of buffer of paths to append.
     531 * @param   pcszSeparator       Separator string to use.
     532 * @param   fFlags              Transfer list flags to use for appending.
     533 */
     534int DnDTransferListAppendPathsFromBuffer(PDNDTRANSFERLIST pList,
     535                                         DNDTRANSFERLISTFMT enmFmt, const char *pszPaths, size_t cbPaths,
     536                                         const char *pcszSeparator, DNDTRANSFERLISTFLAGS fFlags)
     537{
     538    AssertPtrReturn(pList, VERR_INVALID_POINTER);
     539    AssertPtrReturn(pszPaths, VERR_INVALID_POINTER);
     540    AssertReturn(cbPaths, VERR_INVALID_PARAMETER);
     541
     542    char **papszPaths = NULL;
     543    size_t cPaths = 0;
     544    int rc = RTStrSplit(pszPaths, cbPaths, pcszSeparator, &papszPaths, &cPaths);
     545    if (RT_SUCCESS(rc))
     546        rc = DnDTransferListAppendPathsFromArray(pList, enmFmt, papszPaths, cPaths, fFlags);
     547
     548    for (size_t i = 0; i < cPaths; ++i)
     549        RTStrFree(papszPaths[i]);
     550    RTMemFree(papszPaths);
     551
     552    return rc;
     553}
     554
     555/**
     556 * Appends paths to a transfer list.
     557 *
     558 * @returns VBox status code. Will return VERR_INVALID_PARAMETER if a common root path could not be found.
     559 * @param   pList               Transfer list to append path to.
     560 * @param   enmFmt              Format of \a papcszPaths to append.
     561 * @param   papcszPaths         Array of paths to append.
     562 * @param   cPaths              Number of paths in \a papcszPaths to append.
     563 * @param   fFlags              Transfer list flags to use for appending.
     564 */
     565int DnDTransferListAppendPathsFromArray(PDNDTRANSFERLIST pList,
     566                                        DNDTRANSFERLISTFMT enmFmt,
     567                                        const char * const *papcszPaths, size_t cPaths, DNDTRANSFERLISTFLAGS fFlags)
     568{
     569    AssertPtrReturn(pList, VERR_INVALID_POINTER);
     570    AssertPtrReturn(papcszPaths, VERR_INVALID_POINTER);
     571    AssertReturn(!(fFlags & ~DNDTRANSFERLIST_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
     572
     573    int rc = VINF_SUCCESS;
     574
     575    if (!cPaths) /* Nothing to add? Bail out. */
     576        return VINF_SUCCESS;
     577
     578    /* If we don't have a root path set, try to find the common path of all handed-in paths. */
     579    if (!pList->pszPathRootAbs)
     580    {
     581        size_t cchRootPath = RTPathFindCommon(papcszPaths, cPaths);
     582        if (cchRootPath)
     583        {
     584            /* Just use the first path in the array as the reference. */
     585            char *pszRootPath = RTStrDupN(papcszPaths[0], cchRootPath);
     586            if (pszRootPath)
     587            {
     588                LogRel2(("DnD: Determined root path is '%s'\n", pszRootPath));
     589                rc = dndTransferListSetRootPath(pList, pszRootPath);
     590                RTStrFree(pszRootPath);
    97591            }
    98592            else
    99593                rc = VERR_NO_MEMORY;
    100594        }
    101         /* Note: Symlinks already should have been resolved at this point. */
     595    }
     596
     597    /*
     598     * Go through the created list and make sure all entries have the same root path.
     599     */
     600    for (size_t i = 0; i < cPaths; i++)
     601    {
     602        rc = DnDTransferListAppendPath(pList, enmFmt, papcszPaths[i], fFlags);
     603        if (RT_FAILURE(rc))
     604            break;
     605    }
     606
     607    LogFlowFuncLeaveRC(rc);
     608    return rc;
     609}
     610
     611/**
     612 * Returns the first transfer object in a list.
     613 *
     614 * @returns Pointer to transfer object if found, or NULL if not found.
     615 * @param   pList               Transfer list to get first transfer object from.
     616 */
     617PDNDTRANSFEROBJECT DnDTransferListObjGetFirst(PDNDTRANSFERLIST pList)
     618{
     619    AssertPtrReturn(pList, NULL);
     620
     621    return RTListGetFirst(&pList->lstObj, DNDTRANSFEROBJECT, Node);
     622}
     623
     624/**
     625 * Removes the first DnD transfer object from a transfer list.
     626 *
     627 * @param   pList               Transfer list to remove first entry for.
     628 */
     629void DnDTransferListObjRemoveFirst(PDNDTRANSFERLIST pList)
     630{
     631    AssertPtrReturnVoid(pList);
     632
     633    if (!pList->cObj)
     634        return;
     635
     636    PDNDTRANSFEROBJECT pObj = RTListGetFirst(&pList->lstObj, DNDTRANSFEROBJECT, Node);
     637    AssertPtr(pObj);
     638
     639    uint64_t cbSize = DnDTransferObjectGetSize(pObj);
     640    Assert(pList->cbObjTotal >= cbSize);
     641    pList->cbObjTotal -= cbSize; /* Adjust total size. */
     642
     643    dndTransferListObjFree(pList, pObj);
     644}
     645
     646/**
     647 * Returns all root entries of a transfer list as a string.
     648 *
     649 * @returns VBox status code.
     650 * @param   pList               Transfer list to return root paths for.
     651 * @param   pcszPathBase        Root path to use as a base path. If NULL, the list's absolute root path will be used (if any).
     652 * @param   pcszSeparator       Separator to use for separating the root entries.
     653 * @param   ppszBuffer          Where to return the allocated string on success. Needs to be free'd with RTStrFree().
     654 * @param   pcbBuffer           Where to return the size (in bytes) of the allocated string on success, including terminator.
     655 */
     656int DnDTransferListGetRootsEx(PDNDTRANSFERLIST pList,
     657                              DNDTRANSFERLISTFMT enmFmt, const char *pcszPathBase, const char *pcszSeparator,
     658                              char **ppszBuffer, size_t *pcbBuffer)
     659{
     660    AssertPtrReturn(pList, VERR_INVALID_POINTER);
     661    /* pcszPathBase can be NULL. */
     662    AssertPtrReturn(pcszSeparator, VERR_INVALID_POINTER);
     663    AssertPtrReturn(ppszBuffer, VERR_INVALID_POINTER);
     664    AssertPtrReturn(pcbBuffer, VERR_INVALID_POINTER);
     665
     666    char *pszString = NULL;
     667
     668    /* Find out which root path to use. */
     669    const char *pcszPathRootTmp = pcszPathBase ? pcszPathBase : pList->pszPathRootAbs;
     670    /* pcszPathRootTmp can be NULL*/
     671
     672    LogFlowFunc(("Using root path '%s'\n", pcszPathRootTmp ? pcszPathRootTmp : "<None>"));
     673
     674    int rc = DnDPathValidate(pcszPathRootTmp, false /* fMustExist */);
     675    if (RT_FAILURE(rc))
     676        return rc;
     677
     678    char szPath[RTPATH_MAX];
     679
     680    PDNDTRANSFERLISTROOT pRoot;
     681    RTListForEach(&pList->lstRoot, pRoot, DNDTRANSFERLISTROOT, Node)
     682    {
     683        if (pcszPathRootTmp)
     684        {
     685            rc = RTStrCopy(szPath, sizeof(szPath), pcszPathRootTmp);
     686            AssertRCBreak(rc);
     687        }
     688
     689        rc = RTPathAppend(szPath, sizeof(szPath), pRoot->pszPathRoot);
     690        AssertRCBreak(rc);
     691
     692        if (enmFmt == DNDTRANSFERLISTFMT_URI)
     693        {
     694            char *pszPathURI = RTUriFileCreate(szPath);
     695            AssertPtrBreakStmt(pszPathURI, rc = VERR_NO_MEMORY);
     696
     697            rc = RTStrAAppend(&pszString, pszPathURI);
     698            RTStrFree(pszPathURI);
     699            AssertRCBreak(rc);
     700        }
    102701        else
    103             rc = VERR_NOT_SUPPORTED;
    104     }
    105 
    106     LogFlowFuncLeaveRC(rc);
    107     return rc;
    108 }
    109 
    110 int DnDURIList::appendPathRecursive(const char *pcszSrcPath,
    111                                     const char *pcszDstPath, const char *pcszDstBase, size_t cchDstBase,
    112                                     DNDURILISTFLAGS fFlags)
    113 {
    114     AssertPtrReturn(pcszSrcPath, VERR_INVALID_POINTER);
    115     AssertPtrReturn(pcszDstBase, VERR_INVALID_POINTER);
    116     AssertPtrReturn(pcszDstPath, VERR_INVALID_POINTER);
    117 
    118     LogFlowFunc(("pcszSrcPath=%s, pcszDstPath=%s, pcszDstBase=%s, cchDstBase=%zu, fFlags=0x%x\n",
    119                  pcszSrcPath, pcszDstPath, pcszDstBase, cchDstBase, fFlags));
    120 
    121     RTFSOBJINFO objInfo;
    122     int rc = RTPathQueryInfo(pcszSrcPath, &objInfo, RTFSOBJATTRADD_NOTHING);
     702        {
     703            rc = RTStrAAppend(&pszString, szPath);
     704            AssertRCBreak(rc);
     705        }
     706
     707        rc = RTStrAAppend(&pszString, pcszSeparator);
     708        AssertRCBreak(rc);
     709    }
     710
    123711    if (RT_SUCCESS(rc))
    124712    {
    125         if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
    126         {
    127             rc = addEntry(pcszSrcPath, &pcszDstPath[cchDstBase], fFlags);
     713        *ppszBuffer = pszString;
     714        *pcbBuffer  = pszString ? strlen(pszString) + 1 /* Include termination */ : 0;
     715    }
     716    else
     717        RTStrFree(pszString);
     718    return rc;
     719}
     720
     721int DnDTransferListGetRoots(PDNDTRANSFERLIST pList,
     722                            DNDTRANSFERLISTFMT enmFmt, char **ppszBuffer, size_t *pcbBuffer)
     723{
     724    return DnDTransferListGetRootsEx(pList, enmFmt, "" /* pcszPathRoot */, DND_PATH_SEPARATOR,
     725                                     ppszBuffer, pcbBuffer);
     726}
     727
     728/**
     729 * Returns the total root entries count for a DnD transfer list.
     730 *
     731 * @returns Total number of root entries.
     732 * @param   pList               Transfer list to return total number of root entries for.
     733 */
     734uint64_t DnDTransferListGetRootCount(PDNDTRANSFERLIST pList)
     735{
     736    AssertPtrReturn(pList, 0);
     737    return pList->cRoots;
     738}
     739
     740/**
     741 * Returns the absolute root path for a DnD transfer list.
     742 *
     743 * @returns Pointer to the root path.
     744 * @param   pList               Transfer list to return root path for.
     745 */
     746const char *DnDTransferListGetRootPathAbs(PDNDTRANSFERLIST pList)
     747{
     748    AssertPtrReturn(pList, NULL);
     749    return pList->pszPathRootAbs;
     750}
     751
     752/**
     753 * Returns the total transfer object count for a DnD transfer list.
     754 *
     755 * @returns Total number of transfer objects.
     756 * @param   pList               Transfer list to return total number of transfer objects for.
     757 */
     758uint64_t DnDTransferListObjCount(PDNDTRANSFERLIST pList)
     759{
     760    AssertPtrReturn(pList, 0);
     761    return pList->cObj;
     762}
     763
     764/**
     765 * Returns the total bytes of all handled transfer objects for a DnD transfer list.
     766 *
     767 * @returns VBox status code.
     768 * @param   pList               Transfer list to return total bytes for.
     769 */
     770uint64_t DnDTransferListObjTotalBytes(PDNDTRANSFERLIST pList)
     771{
     772    AssertPtrReturn(pList, 0);
     773    return pList->cbObjTotal;
     774}
     775
     776/**
     777 * Sets the absolute root path of a transfer list.
     778 *
     779 * @returns VBox status code.
     780 * @param   pList               Transfer list to set root path for.
     781 * @param   pcszRootPathAbs     Absolute root path to set.
     782 */
     783static int dndTransferListSetRootPath(PDNDTRANSFERLIST pList, const char *pcszRootPathAbs)
     784{
     785    AssertPtrReturn(pList, VERR_INVALID_POINTER);
     786    AssertPtrReturn(pcszRootPathAbs, VERR_INVALID_POINTER);
     787    AssertReturn(pList->pszPathRootAbs == NULL, VERR_WRONG_ORDER); /* Already initialized? */
     788
     789    LogFlowFunc(("pcszRootPathAbs=%s\n", pcszRootPathAbs));
     790
     791    char szRootPath[RTPATH_MAX];
     792    int rc = RTStrCopy(szRootPath, sizeof(szRootPath), pcszRootPathAbs);
     793    if (RT_FAILURE(rc))
     794        return rc;
     795
     796    /* Note: The list's root path is kept in native style, so no conversion needed here. */
     797
     798    RTPathEnsureTrailingSeparatorEx(szRootPath, sizeof(szRootPath), RTPATH_STR_F_STYLE_HOST);
     799
     800    pList->pszPathRootAbs = RTStrDup(szRootPath);
     801    if (pList->pszPathRootAbs)
     802    {
     803        LogFlowFunc(("Root path is '%s'\n", pList->pszPathRootAbs));
     804    }
     805    else
     806        rc = VERR_NO_MEMORY;
     807
     808    return rc;
     809}
     810
     811static int dndTransferListRootAdd(PDNDTRANSFERLIST pList, const char *pcszRoot)
     812{
     813    int rc;
     814
     815    PDNDTRANSFERLISTROOT pRoot = (PDNDTRANSFERLISTROOT)RTMemAllocZ(sizeof(DNDTRANSFERLISTROOT));
     816    if (pRoot)
     817    {
     818        pRoot->pszPathRoot = RTStrDup(pcszRoot);
     819        if (pRoot->pszPathRoot)
     820        {
     821            RTListAppend(&pList->lstRoot, &pRoot->Node);
     822            pList->cRoots++;
     823
     824            rc = VINF_SUCCESS;
     825        }
     826        else
     827            rc = VERR_NO_MEMORY;
     828
     829        if (RT_FAILURE(rc))
     830        {
     831            RTMemFree(pRoot);
     832            pRoot = NULL;
     833        }
     834    }
     835    else
     836        rc = VERR_NO_MEMORY;
     837
     838    return rc;
     839}
     840
     841/**
     842 * Frees an internal DnD transfer root.
     843 *
     844 * @param   pList               Transfer list to free root for.
     845 * @param   pRootObj            Transfer list root to free. The pointer will be invalid after calling.
     846 */
     847static void dndTransferListRootFree(PDNDTRANSFERLIST pList, PDNDTRANSFERLISTROOT pRootObj)
     848{
     849    if (!pRootObj)
     850        return;
     851
     852    RTStrFree(pRootObj->pszPathRoot);
     853
     854    RTListNodeRemove(&pRootObj->Node);
     855    RTMemFree(pRootObj);
     856
     857    AssertReturnVoid(pList->cRoots);
     858    pList->cRoots--;
     859}
     860
     861
     862#if 0
     863/**
     864 * Appends a single URI path to a transfer list.
     865 *
     866 * @returns VBox status code.
     867 * @param   pList               Transfer list to append URI path to.
     868 * @param   pszURIPath          URI path to append.
     869 * @param   fFlags              Transfer list flags to use for appending.
     870 */
     871int DnDTransferListURIAppendPath(PDNDTRANSFERLIST pList, const char *pszURIPath, DNDTRANSFERLISTFLAGS fFlags)
     872{
     873    AssertPtrReturn(pList, VERR_INVALID_POINTER);
     874    AssertPtrReturn(pszURIPath, VERR_INVALID_POINTER);
     875    AssertReturn(!(fFlags & ~DNDTRANSFERLIST_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
     876
     877    in rc;
     878
     879    /* Query the path component of a file URI. If this hasn't a
     880     * file scheme, NULL is returned. */
     881    char *pszFilePath = RTUriFilePathEx(pszURIPath, RTPATH_STR_F_STYLE_UNIX, &pszFilePath, 0 /*cbPath*/, NULL /*pcchPath*/);
     882    LogFlowFunc(("pszPath=%s, pszFilePath=%s\n", pszFilePath));
     883    if (pszFilePath)
     884    {
     885        rc = DnDPathValidate(pszFilePath, false /* fMustExist */);
     886        if (RT_SUCCESS(rc))
     887        {
     888            uint32_t fPathConvert = DNDPATHCONVERT_FLAGS_TRANSPORT;
     889#ifdef RT_OS_WINDOWS
     890            fPathConvert |= DNDPATHCONVERT_FLAGS_TO_DOS;
     891#endif
     892            rc = DnDPathConvert(pszFilePath, strlen(pszFilePath) + 1, fPathConvert);
    128893            if (RT_SUCCESS(rc))
    129894            {
    130                 RTDIR hDir;
    131                 rc = RTDirOpen(&hDir, pcszSrcPath);
    132                 if (RT_SUCCESS(rc))
     895                LogRel2(("DnD: Got URI data item '%s'\n", pszFilePath));
     896
     897                PDNDTRANSFERLISTROOT pRoot = (PDNDTRANSFERLISTROOT)RTMemAlloc(sizeof(DNDTRANSFERLISTROOT));
     898                if (pRoot)
    133899                {
    134                     size_t        cbDirEntry = 0;
    135                     PRTDIRENTRYEX pDirEntry  = NULL;
    136                     do
    137                     {
    138                         /* Retrieve the next directory entry. */
    139                         rc = RTDirReadExA(hDir, &pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
    140                         if (RT_FAILURE(rc))
    141                         {
    142                             if (rc == VERR_NO_MORE_FILES)
    143                                 rc = VINF_SUCCESS;
    144                             break;
    145                         }
    146 
    147                         switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
    148                         {
    149                             case RTFS_TYPE_DIRECTORY:
    150                             {
    151                                 /* Skip "." and ".." entries. */
    152                                 if (RTDirEntryExIsStdDotLink(pDirEntry))
    153                                     break;
    154 
    155                                 char *pszSrc = RTPathJoinA(pcszSrcPath, pDirEntry->szName);
    156                                 if (pszSrc)
    157                                 {
    158                                     char *pszDst = RTPathJoinA(pcszDstPath, pDirEntry->szName);
    159                                     if (pszDst)
    160                                     {
    161                                         rc = appendPathRecursive(pszSrc, pszDst, pcszDstBase, cchDstBase, fFlags);
    162                                         RTStrFree(pszDst);
    163                                     }
    164                                     else
    165                                         rc = VERR_NO_MEMORY;
    166 
    167                                     RTStrFree(pszSrc);
    168                                 }
    169                                 else
    170                                     rc = VERR_NO_MEMORY;
    171                                 break;
    172                             }
    173 
    174                             case RTFS_TYPE_FILE:
    175                             {
    176                                 char *pszSrc = RTPathJoinA(pcszSrcPath, pDirEntry->szName);
    177                                 if (pszSrc)
    178                                 {
    179                                     char *pszDst = RTPathJoinA(pcszDstPath, pDirEntry->szName);
    180                                     if (pszDst)
    181                                     {
    182                                         rc = addEntry(pszSrc, &pszDst[cchDstBase], fFlags);
    183                                         RTStrFree(pszDst);
    184                                     }
    185                                     else
    186                                         rc = VERR_NO_MEMORY;
    187                                     RTStrFree(pszSrc);
    188                                 }
    189                                 else
    190                                     rc = VERR_NO_MEMORY;
    191                                 break;
    192                             }
    193                             case RTFS_TYPE_SYMLINK:
    194                             {
    195                                 if (fFlags & DNDURILIST_FLAGS_RESOLVE_SYMLINKS)
    196                                 {
    197                                     char *pszSrc = RTPathRealDup(pcszDstBase);
    198                                     if (pszSrc)
    199                                     {
    200                                         rc = RTPathQueryInfo(pszSrc, &objInfo, RTFSOBJATTRADD_NOTHING);
    201                                         if (RT_SUCCESS(rc))
    202                                         {
    203                                             if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
    204                                             {
    205                                                 LogFlowFunc(("Directory entry is symlink to directory\n"));
    206                                                 rc = appendPathRecursive(pszSrc, pcszDstPath, pcszDstBase, cchDstBase, fFlags);
    207                                             }
    208                                             else if (RTFS_IS_FILE(objInfo.Attr.fMode))
    209                                             {
    210                                                 LogFlowFunc(("Directory entry is symlink to file\n"));
    211                                                 rc = addEntry(pszSrc, &pcszDstPath[cchDstBase], fFlags);
    212                                             }
    213                                             else
    214                                                 rc = VERR_NOT_SUPPORTED;
    215                                         }
    216 
    217                                         RTStrFree(pszSrc);
    218                                     }
    219                                     else
    220                                         rc = VERR_NO_MEMORY;
    221                                 }
    222                                 break;
    223                             }
    224 
    225                             default:
    226                                 break;
    227                         }
    228 
    229                     } while (RT_SUCCESS(rc));
    230 
    231                     RTDirReadExAFree(&pDirEntry, &cbDirEntry);
    232                     RTDirClose(hDir);
    233                 }
    234             }
    235         }
    236         else if (RTFS_IS_FILE(objInfo.Attr.fMode))
    237         {
    238             rc = addEntry(pcszSrcPath, &pcszDstPath[cchDstBase], fFlags);
    239         }
    240         else if (RTFS_IS_SYMLINK(objInfo.Attr.fMode))
    241         {
    242             if (fFlags & DNDURILIST_FLAGS_RESOLVE_SYMLINKS)
    243             {
    244                 char *pszSrc = RTPathRealDup(pcszSrcPath);
    245                 if (pszSrc)
    246                 {
    247                     rc = RTPathQueryInfo(pszSrc, &objInfo, RTFSOBJATTRADD_NOTHING);
    248                     if (RT_SUCCESS(rc))
    249                     {
    250                         if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
    251                         {
    252                             LogFlowFunc(("Symlink to directory\n"));
    253                             rc = appendPathRecursive(pszSrc, pcszDstPath, pcszDstBase, cchDstBase, fFlags);
    254                         }
    255                         else if (RTFS_IS_FILE(objInfo.Attr.fMode))
    256                         {
    257                             LogFlowFunc(("Symlink to file\n"));
    258                             rc = addEntry(pszSrc, &pcszDstPath[cchDstBase], fFlags);
    259                         }
    260                         else
    261                             rc = VERR_NOT_SUPPORTED;
    262                     }
    263 
    264                     RTStrFree(pszSrc);
     900                    pRoot->pszPathRoot = pszFilePath;
     901
     902                    RTListAppend(&pList->lstRoot, &pRoot->Node);
     903                    pList->cRoots++;
     904
    265905                }
    266906                else
    267907                    rc = VERR_NO_MEMORY;
    268908            }
     909            else
     910                LogRel(("DnD: Path conversion of URI data item '%s' failed with %Rrc\n", pszFilePath, rc));
    269911        }
    270912        else
    271             rc = VERR_NOT_SUPPORTED;
     913            LogRel(("DnD: Path validation for URI data item '%s' failed with %Rrc\n", pszFilePath, rc));
     914
     915        if (RT_FAILURE(rc))
     916            RTStrFree(pszFilePath);
    272917    }
    273918
     
    276921}
    277922
    278 int DnDURIList::AppendNativePath(const char *pszPath, DNDURILISTFLAGS fFlags)
    279 {
    280     AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
    281 
    282     int rc;
    283     char *pszPathNative = RTStrDup(pszPath);
    284     if (pszPathNative)
    285     {
    286         RTPathChangeToUnixSlashes(pszPathNative, true /* fForce */);
    287 
    288         char *pszPathURI = RTUriCreate("file" /* pszScheme */, NULL /* pszAuthority */,
    289                                        pszPathNative, NULL /* pszQuery */, NULL /* pszFragment */);
    290         if (pszPathURI)
    291         {
    292             rc = AppendURIPath(pszPathURI, fFlags);
    293             RTStrFree(pszPathURI);
    294         }
    295         else
    296             rc = VERR_INVALID_PARAMETER;
    297 
    298         RTStrFree(pszPathNative);
    299     }
    300     else
    301         rc = VERR_NO_MEMORY;
    302 
    303     return rc;
    304 }
    305 
    306 int DnDURIList::AppendNativePathsFromList(const char *pszNativePaths, size_t cbNativePaths,
    307                                           DNDURILISTFLAGS fFlags)
    308 {
    309     AssertPtrReturn(pszNativePaths, VERR_INVALID_POINTER);
    310     AssertReturn(cbNativePaths, VERR_INVALID_PARAMETER);
    311 
    312     RTCList<RTCString> lstPaths
    313         = RTCString(pszNativePaths, cbNativePaths - 1).split("\r\n");
    314     return AppendNativePathsFromList(lstPaths, fFlags);
    315 }
    316 
    317 int DnDURIList::AppendNativePathsFromList(const RTCList<RTCString> &lstNativePaths,
    318                                           DNDURILISTFLAGS fFlags)
    319 {
    320     int rc = VINF_SUCCESS;
    321 
    322     for (size_t i = 0; i < lstNativePaths.size(); i++)
    323     {
    324         const RTCString &strPath = lstNativePaths.at(i);
    325         rc = AppendNativePath(strPath.c_str(), fFlags);
    326         if (RT_FAILURE(rc))
    327             break;
    328     }
    329 
    330     LogFlowFuncLeaveRC(rc);
    331     return rc;
    332 }
    333 
    334 int DnDURIList::AppendURIPath(const char *pszURI, DNDURILISTFLAGS fFlags)
    335 {
    336     AssertPtrReturn(pszURI, VERR_INVALID_POINTER);
    337     AssertReturn(!(fFlags & ~DNDURILIST_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
    338     /** @todo Check for string termination?  */
    339 
    340     RTURIPARSED Parsed;
    341     int rc = RTUriParse(pszURI, &Parsed);
    342     if (RT_FAILURE(rc))
    343         return rc;
    344 
    345     char *pszSrcPath = NULL;
    346 
    347     /* file://host.example.com/path/to/file.txt */
    348     const char *pszParsedAuthority = RTUriParsedAuthority(pszURI, &Parsed);
    349     if (   pszParsedAuthority
    350         && pszParsedAuthority[0] != '\0') /* Authority present? */
    351     {
    352         const char *pszParsedPath = RTUriParsedPath(pszURI, &Parsed);
    353         if (pszParsedPath)
    354         {
    355             /* Always use UNIXy paths internally. */
    356             if (RTStrAPrintf(&pszSrcPath,  "//%s%s", pszParsedAuthority, pszParsedPath) == -1)
    357                 rc = VERR_NO_MEMORY;
    358         }
    359         else
    360             rc = VERR_INVALID_PARAMETER;
    361     }
    362     else
    363     {
    364         pszSrcPath = RTUriFilePath(pszURI);
    365         if (!pszSrcPath)
    366             rc = VERR_INVALID_PARAMETER;
    367     }
    368 
    369     LogFlowFunc(("pszURI=%s, fFlags=0x%x -> pszParsedAuthority=%s, pszSrcPath=%s, rc=%Rrc\n",
    370                  pszURI, fFlags, pszParsedAuthority ? pszParsedAuthority : "<None>", pszSrcPath, rc));
    371 
    372     if (RT_SUCCESS(rc))
    373     {
    374         /* Add the path to our internal file list (recursive in
    375          * the case of a directory). */
    376         size_t cbPathLen = RTPathStripTrailingSlash(pszSrcPath);
    377         if (cbPathLen)
    378         {
    379             char *pszFileName = RTPathFilename(pszSrcPath);
    380             if (pszFileName)
    381             {
    382                 Assert(pszFileName >= pszSrcPath);
    383                 size_t cchDstBase = (fFlags & DNDURILIST_FLAGS_ABSOLUTE_PATHS)
    384                                   ? 0 /* Use start of path as root. */
    385                                   : pszFileName - pszSrcPath;
    386                 char *pszDstPath = &pszSrcPath[cchDstBase];
    387                 rc = DnDPathConvert(pszDstPath, strlen(pszDstPath), DNDPATHCONVERT_FLAGS_NONE);
    388                 if (RT_SUCCESS(rc))
    389                 {
    390                     m_lstRoot.append(pszDstPath);
    391 
    392                     LogFlowFunc(("pszSrcPath=%s, pszFileName=%s, pszDstPath=%s\n",
    393                                  pszSrcPath, pszFileName, pszDstPath));
    394 
    395                     rc = appendPathRecursive(pszSrcPath, pszSrcPath, pszSrcPath, cchDstBase, fFlags);
    396                 }
    397             }
    398             else
    399                 rc = VERR_PATH_NOT_FOUND;
    400         }
    401         else
    402             rc = VERR_INVALID_PARAMETER;
    403     }
    404 
    405     RTStrFree(pszSrcPath);
    406 
    407     LogFlowFuncLeaveRC(rc);
    408     return rc;
    409 }
    410 
    411 int DnDURIList::AppendURIPathsFromList(const char *pszURIPaths, size_t cbURIPaths,
    412                                        DNDURILISTFLAGS fFlags)
    413 {
     923/**
     924 * Appends transfer list items from an URI string buffer.
     925 *
     926 * @returns VBox status code.
     927 * @param   pList               Transfer list to append list data to.
     928 * @param   pszURIPaths         String list to append.
     929 * @param   cbURIPaths          Size (in bytes) of string list to append.
     930 * @param   pcszSeparator       Separator string to use for separating strings of \a pszURIPathsAbs.
     931 * @param   fFlags              Transfer list flags to use for appending.
     932 */
     933int DnDTransferListURIAppendFromBuffer(PDNDTRANSFERLIST pList,
     934                                       const char *pszURIPaths, size_t cbURIPaths,
     935                                       const char *pcszSeparator, DNDTRANSFERLISTFLAGS fFlags)
     936{
     937    AssertPtrReturn(pList, VERR_INVALID_POINTER);
    414938    AssertPtrReturn(pszURIPaths, VERR_INVALID_POINTER);
    415939    AssertReturn(cbURIPaths, VERR_INVALID_PARAMETER);
    416 
    417     RTCList<RTCString> lstPaths
    418         = RTCString(pszURIPaths, cbURIPaths - 1).split("\r\n");
    419     return AppendURIPathsFromList(lstPaths, fFlags);
    420 }
    421 
    422 int DnDURIList::AppendURIPathsFromList(const RTCList<RTCString> &lstURI,
    423                                        DNDURILISTFLAGS fFlags)
    424 {
    425     int rc = VINF_SUCCESS;
    426 
    427     for (size_t i = 0; i < lstURI.size(); i++)
    428     {
    429         RTCString strURI = lstURI.at(i);
    430         rc = AppendURIPath(strURI.c_str(), fFlags);
    431 
    432         if (RT_FAILURE(rc))
    433             break;
     940    AssertReturn(!(fFlags & ~DNDTRANSFERLIST_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
     941
     942    char **papszPaths = NULL;
     943    size_t cPaths = 0;
     944    int rc = RTStrSplit(pszURIPaths, cbURIPaths, pcszSeparator, &papszPaths, &cPaths);
     945    if (RT_SUCCESS(rc))
     946    {
     947        for (size_t i = 0; i < cPaths; i++)
     948        {
     949            rc = DnDTransferListURIAppendPath(pList, papszPaths[i], fFlags);
     950            if (RT_FAILURE(rc))
     951                break;
     952        }
     953
     954        for (size_t i = 0; i < cPaths; ++i)
     955            RTStrFree(papszPaths[i]);
     956        RTMemFree(papszPaths);
    434957    }
    435958
     
    437960    return rc;
    438961}
    439 
    440 void DnDURIList::Clear(void)
    441 {
    442     m_lstRoot.clear();
    443 
    444     for (size_t i = 0; i < m_lstTree.size(); i++)
    445     {
    446         DnDURIObject *pCurObj = m_lstTree.at(i);
    447         AssertPtr(pCurObj);
    448         delete pCurObj;
    449     }
    450     m_lstTree.clear();
    451 
    452     m_cTotal  = 0;
    453     m_cbTotal = 0;
    454 }
    455 
    456 void DnDURIList::RemoveFirst(void)
    457 {
    458     if (m_lstTree.isEmpty())
    459         return;
    460 
    461     DnDURIObject *pCurObj = m_lstTree.first();
    462     AssertPtr(pCurObj);
    463 
    464     uint64_t cbSize = pCurObj->GetSize();
    465     Assert(m_cbTotal >= cbSize);
    466     m_cbTotal -= cbSize; /* Adjust total size. */
    467 
    468     pCurObj->Close();
    469     delete pCurObj;
    470 
    471     m_lstTree.removeFirst();
    472 }
    473 
    474 int DnDURIList::SetFromURIData(const void *pvData, size_t cbData, DNDURILISTFLAGS fFlags)
    475 {
    476     Assert(fFlags == 0); RT_NOREF1(fFlags);
    477     AssertPtrReturn(pvData, VERR_INVALID_POINTER);
    478     AssertReturn(cbData, VERR_INVALID_PARAMETER);
    479 
    480     if (!RTStrIsValidEncoding(static_cast<const char *>(pvData)))
    481         return VERR_INVALID_PARAMETER;
    482 
    483     RTCList<RTCString> lstURI =
    484         RTCString(static_cast<const char *>(pvData), cbData - 1).split("\r\n");
    485     if (lstURI.isEmpty())
    486         return VINF_SUCCESS;
    487 
    488     int rc = VINF_SUCCESS;
    489 
    490     for (size_t i = 0; i < lstURI.size(); ++i)
    491     {
    492         /* Query the path component of a file URI. If this hasn't a
    493          * file scheme, NULL is returned. */
    494         const char *pszURI = lstURI.at(i).c_str();
    495         char *pszFilePath = RTUriFilePath(pszURI);
    496 #ifdef DEBUG_andy
    497         LogFlowFunc(("pszURI=%s, pszFilePath=%s\n", pszURI, pszFilePath));
    498962#endif
    499         if (pszFilePath)
    500         {
    501             rc = DnDPathConvert(pszFilePath, strlen(pszFilePath), DNDPATHCONVERT_FLAGS_NONE);
    502             if (RT_SUCCESS(rc))
    503             {
    504                 m_lstRoot.append(pszFilePath);
    505                 m_cTotal++;
    506             }
    507 
    508             RTStrFree(pszFilePath);
    509         }
    510         else
    511             rc = VERR_INVALID_PARAMETER;
    512 
    513         if (RT_FAILURE(rc))
    514             break;
    515     }
    516 
    517     return rc;
    518 }
    519 
    520 RTCString DnDURIList::GetRootEntries(const RTCString &strPathBase /* = "" */,
    521                                    const RTCString &strSeparator /* = "\r\n" */) const
    522 {
    523     RTCString strRet;
    524     for (size_t i = 0; i < m_lstRoot.size(); i++)
    525     {
    526         const char *pszCurRoot = m_lstRoot.at(i).c_str();
    527 #ifdef DEBUG_andy
    528         LogFlowFunc(("pszCurRoot=%s\n", pszCurRoot));
    529 #endif
    530         if (strPathBase.isNotEmpty())
    531         {
    532             char *pszPath = RTPathJoinA(strPathBase.c_str(), pszCurRoot);
    533             if (pszPath)
    534             {
    535                 char *pszPathURI = RTUriFileCreate(pszPath);
    536                 if (pszPathURI)
    537                 {
    538                     strRet += RTCString(pszPathURI) + strSeparator;
    539                     LogFlowFunc(("URI (Base): %s\n", strRet.c_str()));
    540                     RTStrFree(pszPathURI);
    541                 }
    542 
    543                 RTStrFree(pszPath);
    544 
    545                 if (!pszPathURI)
    546                     break;
    547             }
    548             else
    549                 break;
    550         }
    551         else
    552         {
    553             char *pszPathURI = RTUriFileCreate(pszCurRoot);
    554             if (pszPathURI)
    555             {
    556                 strRet += RTCString(pszPathURI) + strSeparator;
    557                 LogFlowFunc(("URI: %s\n", strRet.c_str()));
    558                 RTStrFree(pszPathURI);
    559             }
    560             else
    561                 break;
    562         }
    563     }
    564 
    565     return strRet;
    566 }
    567 
  • trunk/src/VBox/GuestHost/DragAndDrop/DnDTransferObject.cpp

    r85369 r85371  
    11/* $Id$ */
    22/** @file
    3  * DnD - URI object class. For handling creation/reading/writing to files and directories on host or guest side.
     3 * DnD - Transfer object implemenation for handling creation/reading/writing to files and directories on host or guest side.
    44 */
    55
     
    2828#include <iprt/fs.h>
    2929#include <iprt/path.h>
     30#include <iprt/string.h>
    3031#include <iprt/uri.h>
    3132
    3233#include <VBox/log.h>
    3334
    34 
    35 DnDURIObject::DnDURIObject(Type enmType /* = Type_Unknown */, const RTCString &strPathAbs /* = "" */)
    36     : m_enmType(Type_Unknown)
    37 {
    38     if (   enmType != Type_Unknown
    39         && strPathAbs.isNotEmpty())
    40     {
    41         int rc2 = Init(enmType, strPathAbs);
    42         AssertRC(rc2);
    43     }
    44 }
    45 
    46 DnDURIObject::~DnDURIObject(void)
    47 {
    48     closeInternal();
     35/*********************************************************************************************************************************
     36*   Prototypes                                                                                                                   *
     37*********************************************************************************************************************************/
     38static void dndTransferObjectCloseInternal(PDNDTRANSFEROBJECT pObj);
     39static int dndTransferObjectQueryInfoInternal(PDNDTRANSFEROBJECT pObj);
     40
     41
     42/**
     43 * Initializes the object with an expected object type and file path.
     44 *
     45 * @returns VBox status code.
     46 * @param   pObj                DnD transfer object to initialize.
     47 * @param   enmType             Type we expect this object to be.
     48 * @param   pcszPathSrcAbs      Absolute source (local) path of file this object represents. Can be empty (e.g. for root stuff).
     49 * @param   pcszPathDst         Relative path of file this object represents at the destination.
     50 *                              Together with \a pcszPathSrcAbs this represents the complete absolute local path.
     51 */
     52int DnDTransferObjectInit(PDNDTRANSFEROBJECT pObj, DNDTRANSFEROBJTYPE enmType, const char *pcszPathSrcAbs, const char *pcszPathDst)
     53{
     54    AssertPtrReturn(pObj, VERR_INVALID_POINTER);
     55    AssertReturn(pObj->enmType == DNDTRANSFEROBJTYPE_UNKNOWN, VERR_WRONG_ORDER); /* Already initialized? */
     56    /* pcszPathSrcAbs can be empty. */
     57    AssertPtrReturn(pcszPathDst, VERR_INVALID_POINTER);
     58
     59    switch (enmType)
     60    {
     61        case DNDTRANSFEROBJTYPE_FILE:
     62        {
     63            pObj->u.File.hFile = NIL_RTFILE;
     64            break;
     65        }
     66
     67        case DNDTRANSFEROBJTYPE_DIRECTORY:
     68        {
     69            pObj->u.Dir.hDir = NIL_RTDIR;
     70            break;
     71        }
     72
     73        default:
     74            AssertFailedReturn(VERR_NOT_IMPLEMENTED);
     75            break; /* Never reached */
     76    }
     77
     78    int rc = DnDPathValidate(pcszPathDst, false /* Does not need to exist */);
     79    if (RT_FAILURE(rc))
     80        return rc;
     81
     82    char szPath[RTPATH_MAX + 1];
     83
     84    /* Save the index (in characters) where the first destination segment starts. */
     85    if (   pcszPathSrcAbs
     86        && RTStrNLen(pcszPathSrcAbs, RTSTR_MAX))
     87    {
     88        rc = DnDPathValidate(pcszPathSrcAbs, false /* Does not need to exist */);
     89        if (RT_FAILURE(rc))
     90            return rc;
     91
     92        rc = RTStrCopy(szPath, sizeof(szPath), pcszPathSrcAbs);
     93        if (RT_SUCCESS(rc))
     94            rc = RTPathEnsureTrailingSeparator(szPath, sizeof(szPath)) == 0 ? VERR_BUFFER_OVERFLOW : VINF_SUCCESS;
     95
     96        /* Save the index (in characters) where the destination part starts. */
     97        pObj->idxDst = (uint16_t)RTStrNLen(szPath, RTSTR_MAX);
     98        AssertReturn(pObj->idxDst != RTSTR_MAX, VERR_INVALID_PARAMETER);
     99    }
     100    else
     101    {
     102        szPath[0]    = '\0'; /* Init empty string. */
     103        pObj->idxDst = 0;
     104    }
     105
     106    if (RT_FAILURE(rc))
     107        return rc;
     108
     109    /* Append the destination part. */
     110    rc = RTPathAppend(szPath, sizeof(szPath), pcszPathDst);
     111    if (   RT_SUCCESS(rc)
     112        && enmType == DNDTRANSFEROBJTYPE_DIRECTORY)
     113        rc = RTPathEnsureTrailingSeparator(szPath, sizeof(szPath)) == 0 ? VERR_BUFFER_OVERFLOW : VINF_SUCCESS;
     114
     115    if (RT_FAILURE(rc))
     116        return rc;
     117
     118    pObj->pszPath = RTStrDup(szPath);
     119    if (!pObj->pszPath)
     120        return VERR_NO_MEMORY;
     121
     122    /* Convert paths into transport format. */
     123    rc = DnDPathConvert(pObj->pszPath, strlen(pObj->pszPath), DNDPATHCONVERT_FLAGS_TRANSPORT);
     124    if (RT_FAILURE(rc))
     125    {
     126        RTStrFree(pObj->pszPath);
     127        pObj->pszPath = NULL;
     128        return rc;
     129    }
     130
     131    LogFlowFunc(("enmType=%RU32, pcszPathSrcAbs=%s, pcszPathDst=%s -> pszPath=%s\n",
     132                 enmType, pcszPathSrcAbs, pcszPathDst, pObj->pszPath));
     133
     134    pObj->enmType = enmType;
     135
     136    return VINF_SUCCESS;
     137}
     138
     139/**
     140 * Destroys a DnD transfer object.
     141 *
     142 * @param   pObj                DnD transfer object to destroy.
     143 */
     144void DnDTransferObjectDestroy(PDNDTRANSFEROBJECT pObj)
     145{
     146    if (!pObj)
     147        return;
     148
     149    DnDTransferObjectReset(pObj);
    49150}
    50151
    51152/**
    52153 * Closes the object's internal handles (to files / ...).
    53  */
    54 void DnDURIObject::closeInternal(void)
    55 {
    56     LogFlowThisFuncEnter();
     154 *
     155 * @param   pObj                DnD transfer object to close internally.
     156 */
     157static void dndTransferObjectCloseInternal(PDNDTRANSFEROBJECT pObj)
     158{
     159    AssertPtrReturnVoid(pObj);
    57160
    58161    int rc;
    59162
    60     switch (m_enmType)
    61     {
    62         case Type_File:
    63         {
    64             if (RTFileIsValid(u.File.hFile))
     163    LogRel2(("DnD: Closing '%s'\n", pObj->pszPath));
     164
     165    switch (pObj->enmType)
     166    {
     167        case DNDTRANSFEROBJTYPE_FILE:
     168        {
     169            if (RTFileIsValid(pObj->u.File.hFile))
    65170            {
    66                 LogRel2(("DnD: Closing file '%s'\n", m_strPathAbs.c_str()));
    67 
    68                 rc = RTFileClose(u.File.hFile);
     171                rc = RTFileClose(pObj->u.File.hFile);
    69172                if (RT_SUCCESS(rc))
    70173                {
    71                     u.File.hFile = NIL_RTFILE;
    72                     RT_ZERO(u.File.objInfo);
     174                    pObj->u.File.hFile = NIL_RTFILE;
     175                    RT_ZERO(pObj->u.File.objInfo);
    73176                }
    74177                else
    75                     LogRel(("DnD: Closing file '%s' failed with %Rrc\n", m_strPathAbs.c_str(), rc));
     178                    LogRel(("DnD: Closing file '%s' failed with %Rrc\n", pObj->pszPath, rc));
    76179            }
    77180            break;
    78181        }
    79182
    80         case Type_Directory:
    81         {
    82             if (RTDirIsValid(u.Dir.hDir))
     183        case DNDTRANSFEROBJTYPE_DIRECTORY:
     184        {
     185            if (RTDirIsValid(pObj->u.Dir.hDir))
    83186            {
    84                 LogRel2(("DnD: Closing directory '%s'\n", m_strPathAbs.c_str()));
    85 
    86                 rc = RTDirClose(u.Dir.hDir);
     187                rc = RTDirClose(pObj->u.Dir.hDir);
    87188                if (RT_SUCCESS(rc))
    88189                {
    89                     u.Dir.hDir = NIL_RTDIR;
    90                     RT_ZERO(u.Dir.objInfo);
     190                    pObj->u.Dir.hDir = NIL_RTDIR;
     191                    RT_ZERO(pObj->u.Dir.objInfo);
    91192                }
    92193                else
    93                     LogRel(("DnD: Closing directory '%s' failed with %Rrc\n", m_strPathAbs.c_str(), rc));
     194                    LogRel(("DnD: Closing directory '%s' failed with %Rrc\n", pObj->pszPath, rc));
    94195            }
    95196            break;
     
    106207 * Closes the object.
    107208 * This also closes the internal handles associated with the object (to files / ...).
    108  */
    109 void DnDURIObject::Close(void)
    110 {
    111     closeInternal();
     209 *
     210 * @param   pObj                DnD transfer object to close.
     211 */
     212void DnDTransferObjectClose(PDNDTRANSFEROBJECT pObj)
     213{
     214    AssertPtrReturnVoid(pObj);
     215
     216    dndTransferObjectCloseInternal(pObj);
     217}
     218
     219/**
     220 * Returns the absolute source path of the object.
     221 *
     222 * @return  Absolute source path of the object.
     223 * @param   pObj                DnD transfer object to get source path for.
     224 */
     225const char *DnDTransferObjectGetSourcePath(PDNDTRANSFEROBJECT pObj)
     226{
     227    AssertPtrReturn(pObj, NULL);
     228    return pObj->pszPath;
     229}
     230
     231/**
     232 * Returns the (relative) destination path of the object, in transport style.
     233 *
     234 * @return  Relative destination path of the object, or NULL if not set.
     235 * @param   pObj                DnD transfer object to get destination path for.
     236 */
     237const char *DnDTransferObjectGetDestPath(PDNDTRANSFEROBJECT pObj)
     238{
     239    AssertPtrReturn(pObj, NULL);
     240
     241    if (!pObj->pszPath)
     242        return NULL;
     243
     244    AssertReturn(strlen(pObj->pszPath) >= pObj->idxDst, NULL);
     245
     246    return &pObj->pszPath[pObj->idxDst];
     247}
     248
     249/**
     250 * Returns the (relative) destination path of the object, extended version.
     251 *
     252 * @return  VBox status code, or VERR_NOT_FOUND if not initialized yet.
     253 * @param   pObj                DnD transfer object to get destination path for.
     254 * @param   enmStyle            Which path style to return.
     255 * @param   pszBuf              Where to store the path.
     256 * @param   cbBuf               Size (in bytes) where to store the path.
     257 */
     258int DnDTransferObjectGetDestPathEx(PDNDTRANSFEROBJECT pObj, DNDTRANSFEROBJPATHSTYLE enmStyle, char *pszBuf, size_t cbBuf)
     259{
     260    AssertPtrReturn(pObj, VERR_INVALID_POINTER);
     261    AssertPtrReturn(pszBuf, VERR_INVALID_POINTER);
     262    AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
     263
     264    if (!pObj->pszPath)
     265        return VERR_NOT_FOUND;
     266
     267    AssertReturn(strlen(pObj->pszPath) >= pObj->idxDst, VERR_INTERNAL_ERROR);
     268
     269    int rc = RTStrCopy(pszBuf, cbBuf, &pObj->pszPath[pObj->idxDst]);
     270    if (   RT_SUCCESS(rc)
     271        && enmStyle == DNDTRANSFEROBJPATHSTYLE_DOS)
     272        rc = DnDPathConvert(pszBuf, cbBuf, DNDPATHCONVERT_FLAGS_TO_DOS);
     273
     274    return rc;
    112275}
    113276
     
    116279 *
    117280 * @return  File / directory mode.
    118  */
    119 RTFMODE DnDURIObject::GetMode(void) const
    120 {
    121     switch (m_enmType)
    122     {
    123         case Type_File:
    124             return u.File.objInfo.Attr.fMode;
    125 
    126         case Type_Directory:
    127             return u.Dir.objInfo.Attr.fMode;
    128 
    129         default:
    130             break;
    131     }
    132 
    133     AssertFailed();
     281 * @param   pObj                DnD transfer object to get directory / file mode for.
     282 */
     283RTFMODE DnDTransferObjectGetMode(PDNDTRANSFEROBJECT pObj)
     284{
     285    AssertPtrReturn(pObj, 0);
     286
     287    switch (pObj->enmType)
     288    {
     289        case DNDTRANSFEROBJTYPE_FILE:
     290            return pObj->u.File.objInfo.Attr.fMode;
     291
     292        case DNDTRANSFEROBJTYPE_DIRECTORY:
     293            return pObj->u.Dir.objInfo.Attr.fMode;
     294
     295        default:
     296            break;
     297    }
     298
    134299    return 0;
    135300}
     
    138303 * Returns the bytes already processed (read / written).
    139304 *
    140  * Note: Only applies if the object is of type DnDURIObject::Type_File.
     305 * Note: Only applies if the object is of type DnDTransferObjectType_File.
    141306 *
    142307 * @return  Bytes already processed (read / written).
    143  */
    144 uint64_t DnDURIObject::GetProcessed(void) const
    145 {
    146     if (m_enmType == Type_File)
    147         return u.File.cbProcessed;
     308 * @param   pObj                DnD transfer object to get processed bytes for.
     309 */
     310uint64_t DnDTransferObjectGetProcessed(PDNDTRANSFEROBJECT pObj)
     311{
     312    if (pObj->enmType == DNDTRANSFEROBJTYPE_FILE)
     313        return pObj->u.File.cbProcessed;
    148314
    149315    return 0;
     
    153319 * Returns the file's logical size (in bytes).
    154320 *
    155  * Note: Only applies if the object is of type DnDURIObject::Type_File.
     321 * Note: Only applies if the object is of type DnDTransferObjectType_File.
    156322 *
    157323 * @return  The file's logical size (in bytes).
    158  */
    159 uint64_t DnDURIObject::GetSize(void) const
    160 {
    161     if (m_enmType == Type_File)
    162         return u.File.cbToProcess;
     324 * @param   pObj                DnD transfer object to get size for.
     325 */
     326uint64_t DnDTransferObjectGetSize(PDNDTRANSFEROBJECT pObj)
     327{
     328    if (pObj->enmType == DNDTRANSFEROBJTYPE_FILE)
     329        return pObj->u.File.cbToProcess;
    163330
    164331    return 0;
     
    166333
    167334/**
    168  * Initializes the object with an expected object type and file path.
    169  *
    170  * @returns VBox status code.
    171  * @param   enmType             Type we expect this object to be.
    172  * @param   strPathAbs          Absolute path of file this object represents. Optional.
    173  */
    174 int DnDURIObject::Init(Type enmType, const RTCString &strPathAbs /* = */)
    175 {
    176     AssertReturn(m_enmType == Type_Unknown, VERR_WRONG_ORDER);
    177 
    178     int rc;
    179 
    180     switch (enmType)
    181     {
    182         case Type_File:
    183         {
    184             u.File.hFile = NIL_RTFILE;
    185             break;
    186         }
    187 
    188         case Type_Directory:
    189         {
    190             u.Dir.hDir = NIL_RTDIR;
    191             break;
    192         }
    193 
    194         default:
    195             break;
    196     }
    197 
    198     if (enmType != Type_Unknown)
    199     {
    200         AssertReturn(strPathAbs.isNotEmpty(), VERR_INVALID_PARAMETER);
    201         RTCString strPathAbsCopy = strPathAbs;
    202         rc = DnDPathConvert(strPathAbsCopy.mutableRaw(), strPathAbsCopy.capacity(), DNDPATHCONVERT_FLAGS_TO_NATIVE);
    203         if (RT_SUCCESS(rc))
    204         {
    205             m_enmType    = enmType;
    206             m_strPathAbs = strPathAbsCopy;
    207         }
    208         else
    209             LogRel2(("DnD: Absolute file path for guest file on the host is now '%s'\n", strPathAbs.c_str()));
    210     }
    211     else
    212         rc = VERR_INVALID_PARAMETER;
    213 
    214     return rc;
     335 * Returns the object's type.
     336 *
     337 * @return  The object's type.
     338 * @param   pObj                DnD transfer object to get type for.
     339 */
     340DNDTRANSFEROBJTYPE DnDTransferObjectGetType(PDNDTRANSFEROBJECT pObj)
     341{
     342    return pObj->enmType;
    215343}
    216344
     
    220348 *
    221349 * @return  True if complete, False if not.
    222  */
    223 bool DnDURIObject::IsComplete(void) const
     350 * @param   pObj                DnD transfer object to get completion status for.
     351 */
     352bool DnDTransferObjectIsComplete(PDNDTRANSFEROBJECT pObj)
    224353{
    225354    bool fComplete;
    226355
    227     switch (m_enmType)
    228     {
    229         case Type_File:
    230             Assert(u.File.cbProcessed <= u.File.cbToProcess);
    231             fComplete = u.File.cbProcessed == u.File.cbToProcess;
    232             break;
    233 
    234         case Type_Directory:
     356    switch (pObj->enmType)
     357    {
     358        case DNDTRANSFEROBJTYPE_FILE:
     359            Assert(pObj->u.File.cbProcessed <= pObj->u.File.cbToProcess);
     360            fComplete = pObj->u.File.cbProcessed == pObj->u.File.cbToProcess;
     361            break;
     362
     363        case DNDTRANSFEROBJTYPE_DIRECTORY:
    235364            fComplete = true;
    236365            break;
     
    246375/**
    247376 * Returns whether the object is in an open state or not.
    248  */
    249 bool DnDURIObject::IsOpen(void) const
    250 {
    251     switch (m_enmType)
    252     {
    253         case Type_File:      return RTFileIsValid(u.File.hFile);
    254         case Type_Directory: return RTDirIsValid(u.Dir.hDir);
    255         default:             break;
     377 * @param   pObj                DnD transfer object to get open status for.
     378 */
     379bool DnDTransferObjectIsOpen(PDNDTRANSFEROBJECT pObj)
     380{
     381    switch (pObj->enmType)
     382    {
     383        case DNDTRANSFEROBJTYPE_FILE:      return RTFileIsValid(pObj->u.File.hFile);
     384        case DNDTRANSFEROBJTYPE_DIRECTORY: return RTDirIsValid(pObj->u.Dir.hDir);
     385        default:                           break;
    256386    }
    257387
     
    263393 *
    264394 * @return  IPRT status code.
     395 * @param   pObj                DnD transfer object to open.
    265396 * @param   fOpen               Open mode to use; only valid for file objects.
    266397 * @param   fMode               File mode to set; only valid for file objects. Depends on fOpen and and can be 0.
    267  * @param   fFlags              Additional DnD URI object flags.
    268  */
    269 int DnDURIObject::Open(uint64_t fOpen, RTFMODE fMode /* = 0 */,
    270                        DNDURIOBJECTFLAGS fFlags /* = DNDURIOBJECT_FLAGS_NONE */)
    271 {
     398 * @param   fFlags              Additional DnD transfer object flags.
     399 */
     400int DnDTransferObjectOpen(PDNDTRANSFEROBJECT pObj, uint64_t fOpen, RTFMODE fMode, DNDTRANSFEROBJECTFLAGS fFlags)
     401{
     402    AssertPtrReturn(pObj, VERR_INVALID_POINTER);
    272403    AssertReturn(fOpen, VERR_INVALID_FLAGS);
    273404    /* fMode is optional. */
    274     AssertReturn(!(fFlags & ~DNDURIOBJECT_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
     405    AssertReturn(!(fFlags & ~DNDTRANSFEROBJECT_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
    275406    RT_NOREF1(fFlags);
    276407
    277408    int rc = VINF_SUCCESS;
    278409
    279     if (fOpen) /* Opening mode specified? */
    280     {
    281         LogFlowThisFunc(("strPath=%s, fOpen=0x%x, fMode=0x%x, fFlags=0x%x\n",
    282                          m_strPathAbs.c_str(), fOpen, fMode, fFlags));
    283         switch (m_enmType)
    284         {
    285             case Type_File:
     410    LogFlowFunc(("pszPath=%s, fOpen=0x%x, fMode=0x%x, fFlags=0x%x\n", pObj->pszPath, fOpen, fMode, fFlags));
     411
     412    switch (pObj->enmType)
     413    {
     414        case DNDTRANSFEROBJTYPE_FILE:
     415        {
     416            LogRel2(("DnD: Opening file '%s'\n", pObj->pszPath));
     417
     418            /*
     419             * Open files on the source with RTFILE_O_DENY_WRITE to prevent races
     420             * where the OS writes to the file while the destination side transfers
     421             * it over.
     422             */
     423            rc = RTFileOpen(&pObj->u.File.hFile, pObj->pszPath, fOpen);
     424            if (RT_SUCCESS(rc))
    286425            {
    287                 LogRel2(("DnD: Opening file '%s'\n", m_strPathAbs.c_str()));
    288 
    289                 /*
    290                  * Open files on the source with RTFILE_O_DENY_WRITE to prevent races
    291                  * where the OS writes to the file while the destination side transfers
    292                  * it over.
    293                  */
    294                 rc = RTFileOpen(&u.File.hFile, m_strPathAbs.c_str(), fOpen);
    295                 if (RT_SUCCESS(rc))
     426                if (   (fOpen & RTFILE_O_WRITE) /* Only set the file mode on write. */
     427                    &&  fMode                   /* Some file mode to set specified? */)
    296428                {
    297                     if (   (fOpen & RTFILE_O_WRITE) /* Only set the file mode on write. */
    298                         &&  fMode                   /* Some file mode to set specified? */)
    299                     {
    300                         rc = RTFileSetMode(u.File.hFile, fMode);
    301                         if (RT_FAILURE(rc))
    302                             LogRel(("DnD: Setting mode %#x for file '%s' failed with %Rrc\n", fMode, m_strPathAbs.c_str(), rc));
    303                     }
    304                     else if (fOpen & RTFILE_O_READ)
    305                     {
    306                         rc = queryInfoInternal();
    307                     }
     429                    rc = RTFileSetMode(pObj->u.File.hFile, fMode);
     430                    if (RT_FAILURE(rc))
     431                        LogRel(("DnD: Setting mode %#x for file '%s' failed with %Rrc\n", fMode, pObj->pszPath, rc));
    308432                }
    309                 else
    310                     LogRel(("DnD: Opening file '%s' failed with %Rrc\n", m_strPathAbs.c_str(), rc));
    311 
    312                 if (RT_SUCCESS(rc))
     433                else if (fOpen & RTFILE_O_READ)
    313434                {
    314                     LogFlowThisFunc(("File cbObject=%RU64, fMode=0x%x\n",
    315                                      u.File.objInfo.cbObject, u.File.objInfo.Attr.fMode));
    316                     u.File.cbToProcess = u.File.objInfo.cbObject;
    317                     u.File.cbProcessed = 0;
     435                    rc = dndTransferObjectQueryInfoInternal(pObj);
    318436                }
    319 
    320                 break;
    321437            }
    322 
    323             case Type_Directory:
     438            else
     439                LogRel(("DnD: Opening file '%s' failed with %Rrc\n", pObj->pszPath, rc));
     440
     441            if (RT_SUCCESS(rc))
    324442            {
    325                 LogRel2(("DnD: Opening directory '%s'\n", m_strPathAbs.c_str()));
    326 
    327                 rc = RTDirOpen(&u.Dir.hDir, m_strPathAbs.c_str());
    328                 if (RT_SUCCESS(rc))
    329                 {
    330                     rc = queryInfoInternal();
    331                 }
    332                 else
    333                     LogRel(("DnD: Opening directory '%s' failed with %Rrc\n", m_strPathAbs.c_str(), rc));
    334                 break;
     443                LogFlowFunc(("File cbObject=%RU64, fMode=0x%x\n",
     444                             pObj->u.File.objInfo.cbObject, pObj->u.File.objInfo.Attr.fMode));
     445                pObj->u.File.cbToProcess = pObj->u.File.objInfo.cbObject;
     446                pObj->u.File.cbProcessed = 0;
    335447            }
    336448
    337             default:
    338                 rc = VERR_NOT_IMPLEMENTED;
    339                 break;
    340         }
     449            break;
     450        }
     451
     452        case DNDTRANSFEROBJTYPE_DIRECTORY:
     453        {
     454            LogRel2(("DnD: Opening directory '%s'\n", pObj->pszPath));
     455
     456            rc = RTDirOpen(&pObj->u.Dir.hDir, pObj->pszPath);
     457            if (RT_SUCCESS(rc))
     458            {
     459                rc = dndTransferObjectQueryInfoInternal(pObj);
     460            }
     461            else
     462                LogRel(("DnD: Opening directory '%s' failed with %Rrc\n", pObj->pszPath, rc));
     463            break;
     464        }
     465
     466        default:
     467            rc = VERR_NOT_IMPLEMENTED;
     468            break;
    341469    }
    342470
     
    349477 *
    350478 * @return  IPRT status code.
    351  */
    352 int DnDURIObject::queryInfoInternal(void)
     479 * @param   pObj                DnD transfer object to query info for.
     480 */
     481static int dndTransferObjectQueryInfoInternal(PDNDTRANSFEROBJECT pObj)
    353482{
    354483    int rc;
    355484
    356     switch (m_enmType)
    357     {
    358         case Type_File:
    359             AssertMsgReturn(RTFileIsValid(u.File.hFile), ("Object has invalid file handle\n"), VERR_INVALID_STATE);
    360             rc = RTFileQueryInfo(u.File.hFile, &u.File.objInfo, RTFSOBJATTRADD_NOTHING);
    361             break;
    362 
    363         case Type_Directory:
    364             AssertMsgReturn(RTDirIsValid(u.Dir.hDir), ("Object has invalid directory handle\n"), VERR_INVALID_STATE);
    365             rc = RTDirQueryInfo(u.Dir.hDir, &u.Dir.objInfo, RTFSOBJATTRADD_NOTHING);
     485    switch (pObj->enmType)
     486    {
     487        case DNDTRANSFEROBJTYPE_FILE:
     488            AssertMsgReturn(RTFileIsValid(pObj->u.File.hFile), ("Object has invalid file handle\n"), VERR_INVALID_STATE);
     489            rc = RTFileQueryInfo(pObj->u.File.hFile, &pObj->u.File.objInfo, RTFSOBJATTRADD_NOTHING);
     490            break;
     491
     492        case DNDTRANSFEROBJTYPE_DIRECTORY:
     493            AssertMsgReturn(RTDirIsValid(pObj->u.Dir.hDir), ("Object has invalid directory handle\n"), VERR_INVALID_STATE);
     494            rc = RTDirQueryInfo(pObj->u.Dir.hDir, &pObj->u.Dir.objInfo, RTFSOBJATTRADD_NOTHING);
    366495            break;
    367496
     
    372501
    373502    if (RT_FAILURE(rc))
    374         LogRel(("DnD: Querying information for '%s' failed with %Rrc\n", m_strPathAbs.c_str(), rc));
     503        LogRel(("DnD: Querying information for '%s' failed with %Rrc\n", pObj->pszPath, rc));
    375504
    376505    return rc;
     
    381510 *
    382511 * @return  IPRT status code.
    383  */
    384 int DnDURIObject::QueryInfo(void)
    385 {
    386     return queryInfoInternal();
    387 }
    388 
    389 /**
    390  * Rebases an absolute URI path from an old path base to a new path base.
    391  * This function is needed in order to transform path from the source side to the target side.
     512 * @param   pObj                DnD transfer object to query info for.
     513 */
     514int DnDTransferObjectQueryInfo(PDNDTRANSFEROBJECT pObj)
     515{
     516    AssertPtrReturn(pObj, VERR_INVALID_POINTER);
     517    return dndTransferObjectQueryInfoInternal(pObj);
     518}
     519
     520/**
     521 * Reads data from the object. Only applies to files objects.
    392522 *
    393523 * @return  IPRT status code.
    394  * @param   strPathAbs          Absolute URI path to rebase.
    395  * @param   strBaseOld          Old base path to rebase from.
    396  * @param   strBaseNew          New base path to rebase to.
    397  *
    398  ** @todo Put this into an own class like DnDURIPath : public RTCString?
    399  */
    400 /* static */
    401 int DnDURIObject::RebaseURIPath(RTCString &strPathAbs,
    402                                 const RTCString &strBaseOld /* = "" */,
    403                                 const RTCString &strBaseNew /* = "" */)
    404 {
    405     char *pszPath = RTUriFilePath(strPathAbs.c_str());
    406     if (!pszPath) /* No URI? */
    407          pszPath = RTStrDup(strPathAbs.c_str());
    408 
    409     int rc;
    410 
    411     if (pszPath)
    412     {
    413         const char *pszPathStart = pszPath;
    414         const char *pszBaseOld = strBaseOld.c_str();
    415         if (   pszBaseOld
    416             && RTPathStartsWith(pszPath, pszBaseOld))
    417         {
    418             pszPathStart += strlen(pszBaseOld);
    419         }
    420 
    421         rc = VINF_SUCCESS;
    422 
    423         if (RT_SUCCESS(rc))
    424         {
    425             char *pszPathNew = RTPathJoinA(strBaseNew.c_str(), pszPathStart);
    426             if (pszPathNew)
    427             {
    428                 rc = DnDPathValidate(pszPathNew, false /* fMustExist */);
    429                 if (RT_SUCCESS(rc))
    430                 {
    431                     char *pszPathURI = RTUriCreate("file" /* pszScheme */, "/" /* pszAuthority */,
    432                                                    pszPathNew /* pszPath */,
    433                                                    NULL /* pszQuery */, NULL /* pszFragment */);
    434                     if (pszPathURI)
    435                     {
    436                         LogFlowFunc(("Rebasing \"%s\" to \"%s\"\n", strPathAbs.c_str(), pszPathURI));
    437 
    438                         strPathAbs = RTCString(pszPathURI) + "\r\n";
    439                         RTStrFree(pszPathURI);
    440                     }
    441                     else
    442                         rc = VERR_INVALID_PARAMETER;
    443                 }
    444                 else
    445                     LogRel(("DnD: Path validation for '%s' failed with %Rrc\n", pszPathNew, rc));
    446 
    447                 RTStrFree(pszPathNew);
    448             }
    449             else
    450                 rc = VERR_NO_MEMORY;
    451         }
    452 
    453         RTStrFree(pszPath);
    454     }
    455     else
    456         rc = VERR_NO_MEMORY;
    457 
    458     if (RT_FAILURE(rc))
    459         LogRel(("DnD: Rebasing absolute path '%s' (baseOld=%s, baseNew=%s) failed with %Rrc\n",
    460                 strPathAbs.c_str(), strBaseOld.c_str(), strBaseNew.c_str(), rc));
    461 
    462     return rc;
    463 }
    464 
    465 /**
    466  * Reads data from the object. Only applies to files objects.
    467  *
    468  * @return  IPRT status code.
     524 * @param   pObj                DnD transfer object to read data from.
    469525 * @param   pvBuf               Buffer where to store the read data.
    470526 * @param   cbBuf               Size (in bytes) of the buffer.
    471527 * @param   pcbRead             Pointer where to store how many bytes were read. Optional.
    472528 */
    473 int DnDURIObject::Read(void *pvBuf, size_t cbBuf, uint32_t *pcbRead)
    474 {
     529int DnDTransferObjectRead(PDNDTRANSFEROBJECT pObj, void *pvBuf, size_t cbBuf, uint32_t *pcbRead)
     530{
     531    AssertPtrReturn(pObj, VERR_INVALID_POINTER);
    475532    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
    476533    AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
     
    480537
    481538    int rc;
    482     switch (m_enmType)
    483     {
    484         case Type_File:
    485         {
    486             rc = RTFileRead(u.File.hFile, pvBuf, cbBuf, &cbRead);
     539    switch (pObj->enmType)
     540    {
     541        case DNDTRANSFEROBJTYPE_FILE:
     542        {
     543            rc = RTFileRead(pObj->u.File.hFile, pvBuf, cbBuf, &cbRead);
    487544            if (RT_SUCCESS(rc))
    488545            {
    489                 u.File.cbProcessed += cbRead;
    490                 Assert(u.File.cbProcessed <= u.File.cbToProcess);
     546                pObj->u.File.cbProcessed += cbRead;
     547                Assert(pObj->u.File.cbProcessed <= pObj->u.File.cbToProcess);
    491548
    492549                /* End of file reached or error occurred? */
    493                 if (   u.File.cbToProcess
    494                     && u.File.cbProcessed == u.File.cbToProcess)
     550                if (   pObj->u.File.cbToProcess
     551                    && pObj->u.File.cbProcessed == pObj->u.File.cbToProcess)
    495552                {
    496553                    rc = VINF_EOF;
     
    498555            }
    499556            else
    500                 LogRel(("DnD: Reading from file '%s' failed with %Rrc\n", m_strPathAbs.c_str(), rc));
    501             break;
    502         }
    503 
    504         case Type_Directory:
     557                LogRel(("DnD: Reading from file '%s' failed with %Rrc\n", pObj->pszPath, rc));
     558            break;
     559        }
     560
     561        case DNDTRANSFEROBJTYPE_DIRECTORY:
    505562        {
    506563            rc = VINF_SUCCESS;
     
    519576    }
    520577
    521     LogFlowFunc(("Returning strSourcePath=%s, cbRead=%zu, rc=%Rrc\n", m_strPathAbs.c_str(), cbRead, rc));
     578    LogFlowFunc(("Returning cbRead=%zu, rc=%Rrc\n", cbRead, rc));
    522579    return rc;
    523580}
     
    525582/**
    526583 * Resets the object's state and closes all related handles.
    527  */
    528 void DnDURIObject::Reset(void)
    529 {
    530     LogFlowThisFuncEnter();
    531 
    532     Close();
    533 
    534     m_enmType    = Type_Unknown;
    535     m_strPathAbs = "";
    536 
    537     RT_ZERO(u);
     584 *
     585 * @param   pObj                DnD transfer object to reset.
     586 */
     587void DnDTransferObjectReset(PDNDTRANSFEROBJECT pObj)
     588{
     589    AssertPtrReturnVoid(pObj);
     590
     591    LogFlowFuncEnter();
     592
     593    dndTransferObjectCloseInternal(pObj);
     594
     595    pObj->enmType    = DNDTRANSFEROBJTYPE_UNKNOWN;
     596    pObj->idxDst     = 0;
     597
     598    RTStrFree(pObj->pszPath);
     599    pObj->pszPath = NULL;
     600
     601    RT_ZERO(pObj->u);
    538602}
    539603
     
    541605 * Sets the bytes to process by the object.
    542606 *
    543  * Note: Only applies if the object is of type DnDURIObject::Type_File.
     607 * Note: Only applies if the object is of type DnDTransferObjectType_File.
    544608 *
    545609 * @return  IPRT return code.
    546  * @param   cbSize          Size (in bytes) to process.
    547  */
    548 int DnDURIObject::SetSize(uint64_t cbSize)
    549 {
    550     AssertReturn(m_enmType == Type_File, VERR_INVALID_PARAMETER);
     610 * @param   pObj                DnD transfer object to set size for.
     611 * @param   cbSize              Size (in bytes) to process.
     612 */
     613int DnDTransferObjectSetSize(PDNDTRANSFEROBJECT pObj, uint64_t cbSize)
     614{
     615    AssertPtrReturn(pObj, VERR_INVALID_POINTER);
     616    AssertReturn(pObj->enmType == DNDTRANSFEROBJTYPE_FILE, VERR_INVALID_PARAMETER);
    551617
    552618    /** @todo Implement sparse file support here. */
    553619
    554     u.File.cbToProcess = cbSize;
     620    pObj->u.File.cbToProcess = cbSize;
    555621    return VINF_SUCCESS;
    556622}
     
    560626 *
    561627 * @return  IPRT status code.
     628 * @param   pObj                DnD transfer object to write to.
    562629 * @param   pvBuf               Buffer of data to write.
    563630 * @param   cbBuf               Size (in bytes) of data to write.
    564631 * @param   pcbWritten          Pointer where to store how many bytes were written. Optional.
    565632 */
    566 int DnDURIObject::Write(const void *pvBuf, size_t cbBuf, uint32_t *pcbWritten)
    567 {
     633int DnDTransferObjectWrite(PDNDTRANSFEROBJECT pObj, const void *pvBuf, size_t cbBuf, uint32_t *pcbWritten)
     634{
     635    AssertPtrReturn(pObj, VERR_INVALID_POINTER);
    568636    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
    569637    AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
     
    573641
    574642    int rc;
    575     switch (m_enmType)
    576     {
    577         case Type_File:
    578         {
    579             rc = RTFileWrite(u.File.hFile, pvBuf, cbBuf, &cbWritten);
     643    switch (pObj->enmType)
     644    {
     645        case DNDTRANSFEROBJTYPE_FILE:
     646        {
     647            rc = RTFileWrite(pObj->u.File.hFile, pvBuf, cbBuf, &cbWritten);
    580648            if (RT_SUCCESS(rc))
    581649            {
    582                 u.File.cbProcessed += cbWritten;
     650                pObj->u.File.cbProcessed += cbWritten;
    583651            }
    584652            else
    585                 LogRel(("DnD: Writing to file '%s' failed with %Rrc\n", m_strPathAbs.c_str(), rc));
    586             break;
    587         }
    588 
    589         case Type_Directory:
     653                LogRel(("DnD: Writing to file '%s' failed with %Rrc\n", pObj->pszPath, rc));
     654            break;
     655        }
     656
     657        case DNDTRANSFEROBJTYPE_DIRECTORY:
    590658        {
    591659            rc = VINF_SUCCESS;
     
    604672    }
    605673
    606     LogFlowThisFunc(("Returning strSourcePathAbs=%s, cbWritten=%zu, rc=%Rrc\n", m_strPathAbs.c_str(), cbWritten, rc));
     674    LogFlowFunc(("Returning cbWritten=%zu, rc=%Rrc\n", cbWritten, rc));
    607675    return rc;
    608676}
  • trunk/src/VBox/GuestHost/DragAndDrop/Makefile.kmk

    r82968 r85371  
    1919include $(KBUILD_PATH)/subheader.kmk
    2020
     21ifdef VBOX_WITH_TESTCASES
     22 include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk
     23endif
     24
    2125VBOX_DND_GUESTHOST_FILES := \
    2226        DnDDroppedFiles.cpp \
    2327        DnDMIME.cpp \
    2428        DnDPath.cpp \
    25         DnDURIList.cpp \
    26         DnDURIObject.cpp
     29        DnDTransferObject.cpp \
     30        DnDTransferList.cpp
    2731
    2832#
  • trunk/src/VBox/Main/include/GuestDnDPrivate.h

    r85121 r85371  
    8282
    8383/**
    84  * Class for handling the (raw) meta data.
    85  */
    86 class GuestDnDMetaData
    87 {
    88 public:
    89 
     84 * Struct for handling the (raw) meta data.
     85 */
     86struct GuestDnDMetaData
     87{
    9088    GuestDnDMetaData(void)
    9189        : pvData(NULL)
    9290        , cbData(0)
    93         , cbDataUsed(0) { }
     91        , cbAllocated(0) { }
    9492
    9593    virtual ~GuestDnDMetaData(void)
     
    9795        reset();
    9896    }
    99 
    100 public:
    10197
    10298    uint32_t add(const void *pvDataAdd, uint32_t cbDataAdd)
     
    108104        AssertPtrReturn(pvDataAdd, 0);
    109105
    110         int rc = resize(cbData + cbDataAdd);
     106        int rc = resize(cbAllocated + cbDataAdd);
    111107        if (RT_FAILURE(rc))
    112108            return 0;
    113109
    114         Assert(cbData >= cbDataUsed + cbDataAdd);
    115         memcpy((uint8_t *)pvData + cbDataUsed, pvDataAdd, cbDataAdd);
    116 
    117         cbDataUsed += cbDataAdd;
    118 
    119         return cbDataAdd;
     110        Assert(cbAllocated >= cbData + cbDataAdd);
     111        memcpy((uint8_t *)pvData + cbData, pvDataAdd, cbDataAdd);
     112
     113        cbData += cbDataAdd;
     114
     115        return cbData;
    120116    }
    121117
     
    133129    void reset(void)
    134130    {
     131        strFmt = "";
     132
    135133        if (pvData)
    136134        {
    137             Assert(cbData);
     135            Assert(cbAllocated);
    138136            RTMemFree(pvData);
    139137            pvData = NULL;
    140138        }
    141139
    142         cbData     = 0;
    143         cbDataUsed = 0;
    144     }
    145 
    146     const void *getData(void) const { return pvData; }
    147 
    148     void *getDataMutable(void) { return pvData; }
    149 
    150     uint32_t getSize(void) const { return cbDataUsed; }
    151 
    152 public:
    153 
    154     int fromString(const RTCString &strData)
    155     {
    156         int rc = VINF_SUCCESS;
    157 
    158         if (strData.isNotEmpty())
    159         {
    160             const uint32_t cbStrData = (uint32_t)strData.length() + 1; /* Include terminating zero. */
    161             rc = resize(cbStrData);
    162             if (RT_SUCCESS(rc))
    163                 memcpy(pvData, strData.c_str(), cbStrData);
    164         }
    165 
    166         return rc;
    167     }
    168 
    169     int fromURIList(const DnDURIList &lstURI)
    170     {
    171         return fromString(lstURI.GetRootEntries());
    172     }
    173 
    174 protected:
     140        cbAllocated = 0;
     141        cbData      = 0;
     142    }
    175143
    176144    int resize(uint32_t cbSize)
     
    182150        }
    183151
    184         if (cbSize == cbData)
     152        if (cbSize == cbAllocated)
    185153            return VINF_SUCCESS;
    186154
     
    189157
    190158        void *pvTmp = NULL;
    191         if (!cbData)
    192         {
    193             Assert(cbDataUsed == 0);
     159        if (!cbAllocated)
     160        {
     161            Assert(cbData == 0);
    194162            pvTmp = RTMemAllocZ(cbSize);
    195163        }
     
    203171        if (pvTmp)
    204172        {
    205             pvData = pvTmp;
    206             cbData = cbSize;
     173            pvData      = pvTmp;
     174            cbAllocated = cbSize;
    207175            return VINF_SUCCESS;
    208176        }
     
    211179    }
    212180
    213 protected:
    214 
    215     void     *pvData;
    216     uint32_t  cbData;
    217     uint32_t  cbDataUsed;
    218 };
    219 
    220 /**
    221  * Class for keeping drag and drop (meta) data
    222  * to be sent/received.
    223  */
    224 class GuestDnDData
    225 {
    226 public:
    227 
     181    /** Format string of this meta data. */
     182    com::Utf8Str strFmt;
     183    /** Pointer to allocated meta data. */
     184    void        *pvData;
     185    /** Used bytes of meta data. Must not exceed cbAllocated. */
     186    uint32_t     cbData;
     187    /** Size (in bytes) of allocated meta data. */
     188    uint32_t     cbAllocated;
     189};
     190
     191/**
     192 * Struct for accounting shared DnD data to be sent/received.
     193 */
     194struct GuestDnDData
     195{
    228196    GuestDnDData(void)
    229         : cbEstTotal(0)
    230         , cbEstMeta(0)
    231         , cbProcessed(0)
    232     {
    233         RT_ZERO(dataHdr);
    234     }
     197        : cbExtra(0)
     198        , cbProcessed(0) { }
    235199
    236200    virtual ~GuestDnDData(void)
     
    239203    }
    240204
    241 public:
    242 
    243205    uint64_t addProcessed(uint32_t cbDataAdd)
    244206    {
    245         const uint64_t cbTotal = getTotal(); NOREF(cbTotal);
     207        const uint64_t cbTotal = Meta.cbData + cbExtra;
    246208        Assert(cbProcessed + cbDataAdd <= cbTotal);
    247209        cbProcessed += cbDataAdd;
     
    251213    bool isComplete(void) const
    252214    {
    253         const uint64_t cbTotal = getTotal();
     215        const uint64_t cbTotal = Meta.cbData + cbExtra;
    254216        LogFlowFunc(("cbProcessed=%RU64, cbTotal=%RU64\n", cbProcessed, cbTotal));
    255217        Assert(cbProcessed <= cbTotal);
     
    257219    }
    258220
    259     void *getChkSumMutable(void) { return dataHdr.pvChecksum; }
    260 
    261     uint32_t getChkSumSize(void) const { return dataHdr.cbChecksum; }
    262 
    263     void *getFmtMutable(void) { return dataHdr.pvMetaFmt; }
    264 
    265     uint32_t getFmtSize(void) const { return dataHdr.cbMetaFmt; }
    266 
    267     GuestDnDMetaData &getMeta(void) { return dataMeta; }
    268 
    269221    uint8_t getPercentComplete(void) const
    270222    {
    271         int64_t cbTotal = RT_MAX(getTotal(), 1);
    272         return (uint8_t)(cbProcessed * 100 / cbTotal);
    273     }
    274 
    275     uint64_t getProcessed(void) const { return cbProcessed; }
     223        const uint64_t cbTotal = Meta.cbData + cbExtra;
     224        return (uint8_t)(cbProcessed * 100 / RT_MAX(cbTotal, 1));
     225    }
    276226
    277227    uint64_t getRemaining(void) const
    278228    {
    279         const uint64_t cbTotal = getTotal();
    280         Assert(cbProcessed <= cbTotal);
     229        const uint64_t cbTotal = Meta.cbData + cbExtra;
     230        AssertReturn(cbProcessed <= cbTotal, 0);
    281231        return cbTotal - cbProcessed;
    282232    }
    283233
    284     uint64_t getTotal(void) const { return cbEstTotal; }
     234    uint64_t getTotal(void) const
     235    {
     236        return Meta.cbData + cbExtra;
     237    }
    285238
    286239    void reset(void)
    287240    {
    288         clearFmt();
    289         clearChkSum();
    290 
    291         RT_ZERO(dataHdr);
    292 
    293         dataMeta.reset();
    294 
    295         cbEstTotal  = 0;
    296         cbEstMeta   = 0;
     241        Meta.reset();
     242
     243        cbExtra     = 0;
    297244        cbProcessed = 0;
    298245    }
    299246
    300     int setFmt(const void *pvFmt, uint32_t cbFmt)
    301     {
    302         if (cbFmt)
    303         {
    304             AssertPtrReturn(pvFmt, VERR_INVALID_POINTER);
    305             void *pvFmtTmp = RTMemAlloc(cbFmt);
    306             if (!pvFmtTmp)
    307                 return VERR_NO_MEMORY;
    308 
    309             clearFmt();
    310 
    311             memcpy(pvFmtTmp, pvFmt, cbFmt);
    312 
    313             dataHdr.pvMetaFmt = pvFmtTmp;
    314             dataHdr.cbMetaFmt = cbFmt;
    315         }
    316         else
    317             clearFmt();
    318 
    319         return VINF_SUCCESS;
    320     }
    321 
    322     void setEstimatedSize(uint64_t cbTotal, uint32_t cbMeta)
    323     {
    324         Assert(cbMeta <= cbTotal);
    325 
    326         LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32\n", cbTotal, cbMeta));
    327 
    328         cbEstTotal = cbTotal;
    329         cbEstMeta  = cbMeta;
    330     }
    331 
    332 protected:
    333 
    334     void clearChkSum(void)
    335     {
    336         if (dataHdr.pvChecksum)
    337         {
    338             Assert(dataHdr.cbChecksum);
    339             RTMemFree(dataHdr.pvChecksum);
    340             dataHdr.pvChecksum = NULL;
    341         }
    342 
    343         dataHdr.cbChecksum = 0;
    344     }
    345 
    346     void clearFmt(void)
    347     {
    348         if (dataHdr.pvMetaFmt)
    349         {
    350             Assert(dataHdr.cbMetaFmt);
    351             RTMemFree(dataHdr.pvMetaFmt);
    352             dataHdr.pvMetaFmt = NULL;
    353         }
    354 
    355         dataHdr.cbMetaFmt = 0;
    356     }
    357 
    358 protected:
    359 
    360     /** The data header. */
    361     VBOXDNDDATAHDR    dataHdr;
    362247    /** For storing the actual meta data.
    363248     *  This might be an URI list or just plain raw data,
    364249     *  according to the format being sent. */
    365     GuestDnDMetaData  dataMeta;
    366     /** Estimated total data size when receiving data. */
    367     uint64_t          cbEstTotal;
    368     /** Estimated meta data size when receiving data. */
    369     uint32_t          cbEstMeta;
     250    GuestDnDMetaData  Meta;
     251    /** Extra data to send/receive (in bytes). Can be 0 for raw data.
     252     *  For (file) transfers this is the total size for all files. */
     253    uint64_t          cbExtra;
    370254    /** Overall size (in bytes) of processed data. */
    371255    uint64_t          cbProcessed;
     
    373257
    374258/** Initial object context state / no state set. */
    375 #define DND_OBJCTX_STATE_NONE           0
     259#define DND_OBJ_STATE_NONE           0
    376260/** The header was received / sent. */
    377 #define DND_OBJCTX_STATE_HAS_HDR        RT_BIT(0)
     261#define DND_OBJ_STATE_HAS_HDR        RT_BIT(0)
    378262/** Validation mask for object context state. */
    379 #define DND_OBJCTX_STATE_VALID_MASK     UINT32_C(0x00000001)
    380 
    381 /**
    382  * Structure for keeping a DnDURIObject context around.
    383  */
    384 class GuestDnDURIObjCtx
    385 {
    386 public:
    387 
    388     GuestDnDURIObjCtx(void)
    389         : pObjURI(NULL)
    390         , fIntermediate(false)
    391         , fState(DND_OBJCTX_STATE_NONE) { }
    392 
    393     virtual ~GuestDnDURIObjCtx(void)
    394     {
    395         destroy();
    396     }
    397 
    398 public:
    399 
    400     int createIntermediate(DnDURIObject::Type enmType)
    401     {
    402         reset();
    403 
    404         int rc;
    405 
    406         try
    407         {
    408             pObjURI       = new DnDURIObject(enmType);
    409             fIntermediate = true;
    410 
    411             rc = VINF_SUCCESS;
    412         }
    413         catch (std::bad_alloc &)
    414         {
    415             rc = VERR_NO_MEMORY;
    416         }
    417 
    418         LogThisFunc(("Returning %Rrc\n", rc));
    419         return rc;
    420     }
    421 
    422     void destroy(void)
    423     {
    424         LogFlowThisFuncEnter();
    425 
    426         if (   pObjURI
    427             && fIntermediate)
    428         {
    429             delete pObjURI;
    430         }
    431 
    432         pObjURI       = NULL;
    433         fIntermediate = false;
    434     }
    435 
    436     DnDURIObject *getObj(void) const { return pObjURI; }
    437 
    438     bool isIntermediate(void) { return fIntermediate; }
    439 
    440     bool isValid(void) const { return (pObjURI != NULL); }
    441 
    442     /** Returns the current state. */
    443     uint32_t getState(void) const { return fState; }
    444 
    445     void reset(void)
    446     {
    447         LogFlowThisFuncEnter();
    448 
    449         destroy();
    450 
    451         fIntermediate = false;
    452         fState        = DND_OBJCTX_STATE_NONE;
    453     }
    454 
    455     void setObj(DnDURIObject *pObj)
    456     {
    457         LogFlowThisFunc(("%p\n", pObj));
    458 
    459         destroy();
    460 
    461         pObjURI = pObj;
    462     }
    463 
    464     /**
    465      * Sets the new state.
    466      *
    467      * @returns The new state, if set.
    468      * @param   fStateNew       New state to set.
    469      */
    470     uint32_t setState(uint32_t fStateNew)
    471     {
    472         AssertReturn(!(fStateNew & ~DND_OBJCTX_STATE_VALID_MASK), fState /* Return old state */);
    473         fState = fStateNew;
    474         return fState;
    475     }
    476 
    477 protected:
    478 
    479     /** Pointer to current object being handled. */
    480     DnDURIObject             *pObjURI;
    481     /** Flag whether pObjURI needs deletion after use. */
    482     bool                      fIntermediate;
    483     /** Internal context state, corresponding to DND_OBJCTX_STATE_XXX. */
    484     uint32_t                  fState;
    485     /** @todo Add more statistics / information here. */
    486 };
    487 
    488 /**
    489  * Structure for keeping around an URI (data) transfer.
    490  */
    491 class GuestDnDURIData
    492 {
    493 
    494 public:
    495 
    496     GuestDnDURIData(void)
     263#define DND_OBJ_STATE_VALID_MASK     UINT32_C(0x00000001)
     264
     265/**
     266 * Class for keeping around DnD (file) transfer data.
     267 */
     268struct GuestDnDTransferData
     269{
     270
     271public:
     272
     273    GuestDnDTransferData(void)
    497274        : cObjToProcess(0)
    498275        , cObjProcessed(0)
     
    500277        , cbScratchBuf(0) { }
    501278
    502     virtual ~GuestDnDURIData(void)
     279    virtual ~GuestDnDTransferData(void)
     280    {
     281        destroy();
     282    }
     283
     284    int init(size_t cbBuf = _64K)
     285    {
     286        reset();
     287
     288        pvScratchBuf = RTMemAlloc(cbBuf);
     289        if (!pvScratchBuf)
     290            return VERR_NO_MEMORY;
     291
     292        cbScratchBuf = cbBuf;
     293        return VINF_SUCCESS;
     294    }
     295
     296    void destroy(void)
    503297    {
    504298        reset();
     
    513307    }
    514308
    515     DnDDroppedFiles &getDroppedFiles(void) { return droppedFiles; }
    516 
    517     DnDURIList &getURIList(void) { return lstURI; }
    518 
    519     int init(size_t cbBuf = _64K)
    520     {
    521         reset();
    522 
    523         pvScratchBuf = RTMemAlloc(cbBuf);
    524         if (!pvScratchBuf)
    525             return VERR_NO_MEMORY;
    526 
    527         cbScratchBuf = cbBuf;
    528         return VINF_SUCCESS;
    529     }
    530 
    531     bool isComplete(void) const
    532     {
    533         LogFlowFunc(("cObjProcessed=%RU64, cObjToProcess=%RU64\n", cObjProcessed, cObjToProcess));
    534 
    535         if (!cObjToProcess) /* Always return true if we don't have an object count. */
    536             return true;
    537 
    538         Assert(cObjProcessed <= cObjToProcess);
    539         return (cObjProcessed == cObjToProcess);
    540     }
    541 
    542     const void *getBuffer(void) const { return pvScratchBuf; }
    543 
    544     void *getBufferMutable(void) { return pvScratchBuf; }
    545 
    546     size_t getBufferSize(void) const { return cbScratchBuf; }
    547 
    548     GuestDnDURIObjCtx &getObj(uint64_t uID = 0)
    549     {
    550         RT_NOREF(uID);
    551         AssertMsg(uID == 0, ("Other objects than object 0 is not supported yet\n"));
    552         return objCtx;
    553     }
    554 
    555     GuestDnDURIObjCtx &getObjCurrent(void)
    556     {
    557         DnDURIObject *pCurObj = lstURI.First();
    558         if (   !lstURI.IsEmpty()
    559             && pCurObj)
    560         {
    561             /* Point the context object to the current DnDURIObject to process. */
    562             objCtx.setObj(pCurObj);
    563         }
    564         else
    565             objCtx.reset();
    566 
    567         return objCtx;
    568     }
    569 
    570     uint64_t getObjToProcess(void) const { return cObjToProcess; }
    571 
    572     uint64_t getObjProcessed(void) const { return cObjProcessed; }
    573 
    574     int processObject(const DnDURIObject &Obj)
    575     {
    576         int rc;
    577 
    578         /** @todo Find objct in lstURI first! */
    579         switch (Obj.GetType())
    580         {
    581             case DnDURIObject::Type_Directory:
    582             case DnDURIObject::Type_File:
    583                 rc = VINF_SUCCESS;
    584                 break;
    585 
    586             default:
    587                 rc = VERR_NOT_IMPLEMENTED;
    588                 break;
    589         }
    590 
    591         if (RT_SUCCESS(rc))
    592         {
    593             if (cObjToProcess)
    594             {
    595                 cObjProcessed++;
    596                 Assert(cObjProcessed <= cObjToProcess);
    597             }
    598         }
    599 
    600         return rc;
    601     }
    602 
    603     void removeObjCurrent(void)
    604     {
    605         if (cObjToProcess)
    606         {
    607             cObjProcessed++;
    608             Assert(cObjProcessed <= cObjToProcess);
    609         }
    610 
    611         lstURI.RemoveFirst();
    612         objCtx.reset();
    613     }
    614 
    615309    void reset(void)
    616310    {
     
    619313        cObjToProcess = 0;
    620314        cObjProcessed = 0;
    621 
    622         droppedFiles.Close();
    623 
    624         lstURI.Clear();
    625         objCtx.reset();
    626     }
    627 
    628     void setEstimatedObjects(uint64_t cObjs)
    629     {
    630         Assert(cObjToProcess == 0);
    631         cObjToProcess = cObjs;
    632         LogFlowFunc(("cObjToProcess=%RU64\n", cObjs));
    633     }
    634 
    635 public:
    636 
    637     int fromLocalMetaData(const GuestDnDMetaData &Data)
    638     {
    639         reset();
    640 
    641         if (!Data.getSize())
    642             return VINF_SUCCESS;
    643 
    644         char *pszList;
    645         int rc = RTStrCurrentCPToUtf8(&pszList, (const char *)Data.getData());
    646         if (RT_FAILURE(rc))
    647         {
    648             LogFlowThisFunc(("String conversion failed with rc=%Rrc\n", rc));
    649             return rc;
    650         }
    651 
    652         const size_t cbList = Data.getSize();
    653         LogFlowThisFunc(("metaData=%p, cbList=%zu\n", &Data, cbList));
    654 
    655         if (cbList)
    656         {
    657             RTCList<RTCString> lstURIOrg = RTCString(pszList, cbList).split("\r\n");
    658             if (!lstURIOrg.isEmpty())
    659             {
    660                 /* Note: All files to be transferred will be kept open during the entire DnD
    661                  *       operation, also to keep the accounting right. */
    662                 rc = lstURI.AppendURIPathsFromList(lstURIOrg, DNDURILIST_FLAGS_KEEP_OPEN);
    663                 if (RT_SUCCESS(rc))
    664                     cObjToProcess = lstURI.GetTotalCount();
    665             }
    666         }
    667 
    668         RTStrFree(pszList);
    669         return rc;
    670     }
    671 
    672     int fromRemoteMetaData(const GuestDnDMetaData &Data)
    673     {
    674         LogFlowFuncEnter();
    675 
    676         int rc = lstURI.SetFromURIData(Data.getData(), Data.getSize(), 0 /* uFlags */);
    677         if (RT_SUCCESS(rc))
    678         {
    679             const size_t cRootCount = lstURI.GetRootCount();
    680             LogFlowFunc(("cRootCount=%zu, cObjToProcess=%RU64\n", cRootCount, cObjToProcess));
    681             if (cRootCount > cObjToProcess)
    682                 rc = VERR_INVALID_PARAMETER;
    683         }
    684 
    685         return rc;
    686     }
    687 
    688     int toMetaData(std::vector<BYTE> &vecData)
    689     {
    690         const char *pszDroppedFilesDir = droppedFiles.GetDirAbs();
    691 
    692         Utf8Str strURIs = lstURI.GetRootEntries(RTCString(pszDroppedFilesDir));
    693         size_t cbData = strURIs.length();
    694 
    695         LogFlowFunc(("%zu root URIs (%zu bytes)\n", lstURI.GetRootCount(), cbData));
    696 
    697         int rc;
    698 
    699         try
    700         {
    701             vecData.resize(cbData + 1 /* Include termination */);
    702             memcpy(&vecData.front(), strURIs.c_str(), cbData);
    703 
    704             rc = VINF_SUCCESS;
    705         }
    706         catch (std::bad_alloc &)
    707         {
    708             rc = VERR_NO_MEMORY;
    709         }
    710 
    711         return rc;
    712     }
    713 
    714 protected:
    715 
    716     int processDirectory(const char *pszPath, uint32_t fMode)
    717     {
    718         /** @todo Find directory in lstURI first! */
    719         int rc;
    720 
    721         const char *pszDroppedFilesDir = droppedFiles.GetDirAbs();
    722         char *pszDir = RTPathJoinA(pszDroppedFilesDir, pszPath);
    723         if (pszDir)
    724         {
    725             rc = RTDirCreateFullPath(pszDir, fMode);
    726             if (cObjToProcess)
    727             {
    728                 cObjProcessed++;
    729                 Assert(cObjProcessed <= cObjToProcess);
    730             }
    731 
    732             RTStrFree(pszDir);
    733         }
    734         else
    735              rc = VERR_NO_MEMORY;
    736 
    737         return rc;
    738     }
    739 
    740 protected:
     315    }
     316
     317    bool isComplete(void) const
     318    {
     319        return (cObjProcessed == cObjToProcess);
     320    }
    741321
    742322    /** Number of objects to process. */
    743     uint64_t                        cObjToProcess;
     323    uint64_t cObjToProcess;
    744324    /** Number of objects already processed. */
    745     uint64_t                        cObjProcessed;
    746     /** Handles all drop files for this operation. */
    747     DnDDroppedFiles                 droppedFiles;
    748     /** (Non-recursive) List of URI objects to handle. */
    749     DnDURIList                      lstURI;
    750     /** Context to current object being handled.
    751      *  As we currently do all transfers one after another we
    752      *  only have one context at a time. */
    753     GuestDnDURIObjCtx               objCtx;
     325    uint64_t cObjProcessed;
    754326    /** Pointer to an optional scratch buffer to use for
    755327     *  doing the actual chunk transfers. */
    756     void                           *pvScratchBuf;
     328    void    *pvScratchBuf;
    757329    /** Size (in bytes) of scratch buffer. */
    758     size_t                          cbScratchBuf;
     330    size_t   cbScratchBuf;
     331};
     332
     333struct GuestDnDTransferSendData : public GuestDnDTransferData
     334{
     335    GuestDnDTransferSendData()
     336        : mfObjState(0) { }
     337
     338    virtual ~GuestDnDTransferSendData()
     339    {
     340        destroy();
     341    }
     342
     343    void destroy(void)
     344    {
     345        DnDTransferListDestroy(&mList);
     346    }
     347
     348    void reset(void)
     349    {
     350        DnDTransferListReset(&mList);
     351        mfObjState = 0;
     352
     353        GuestDnDTransferData::reset();
     354    }
     355
     356    /** Transfer List to handle. */
     357    DNDTRANSFERLIST                     mList;
     358    /** Current state of object in transfer.
     359     *  This is needed for keeping compatibility to old(er) DnD HGCM protocols.
     360     *
     361     *  At the moment we only support transferring one object at a time. */
     362    uint32_t                            mfObjState;
    759363};
    760364
     
    762366 * Context structure for sending data to the guest.
    763367 */
    764 struct GuestDnDSendCtx
     368struct GuestDnDSendCtx : public GuestDnDData
    765369{
    766370    /** Pointer to guest target class this context belongs to. */
     
    775379    /** Drag'n drop format requested by the guest. */
    776380    com::Utf8Str                        mFmtReq;
    777     /** Drag'n drop data to send.
    778      *  This can be arbitrary data or an URI list. */
    779     GuestDnDData                        mData;
    780     /** URI data structure. */
    781     GuestDnDURIData                     mURI;
     381    /** Transfer data structure. */
     382    GuestDnDTransferSendData            mTransfer;
    782383    /** Callback event to use. */
    783384    GuestDnDCallbackEvent               mCBEvent;
    784385};
    785386
     387struct GuestDnDTransferRecvData : public GuestDnDTransferData
     388{
     389    GuestDnDTransferRecvData()
     390    {
     391        RT_ZERO(mDroppedFiles);
     392        RT_ZERO(mList);
     393        RT_ZERO(mObj);
     394    }
     395
     396    virtual ~GuestDnDTransferRecvData()
     397    {
     398        destroy();
     399    }
     400
     401    void destroy(void)
     402    {
     403        DnDTransferListDestroy(&mList);
     404    }
     405
     406    void reset(void)
     407    {
     408        DnDDroppedFilesClose(&mDroppedFiles);
     409        DnDTransferListReset(&mList);
     410        DnDTransferObjectReset(&mObj);
     411
     412        GuestDnDTransferData::reset();
     413    }
     414
     415    /** The "VirtualBox Dropped Files" directory on the host we're going
     416     *  to utilize for transferring files from guest to the host. */
     417    DNDDROPPEDFILES                     mDroppedFiles;
     418    /** Transfer List to handle.
     419     *  Currently we only support one transfer list at a time. */
     420    DNDTRANSFERLIST                     mList;
     421    /** Current transfer object being handled.
     422     *  Currently we only support one transfer object at a time. */
     423    DNDTRANSFEROBJECT                   mObj;
     424};
     425
    786426/**
    787427 * Context structure for receiving data from the guest.
    788428 */
    789 struct GuestDnDRecvCtx
     429struct GuestDnDRecvCtx : public GuestDnDData
    790430{
    791431    /** Pointer to guest source class this context belongs to. */
     
    813453     *  deleted e.g. when moving instead of copying. */
    814454    VBOXDNDACTION                       mAction;
    815     /** Drag'n drop received from the guest.
    816      *  This can be arbitrary data or an URI list. */
    817     GuestDnDData                        mData;
    818     /** URI data structure. */
    819     GuestDnDURIData                     mURI;
     455    /** Transfer data structure. */
     456    GuestDnDTransferRecvData            mTransfer;
    820457    /** Callback event to use. */
    821458    GuestDnDCallbackEvent               mCBEvent;
  • trunk/src/VBox/Main/include/GuestDnDSourceImpl.h

    r85020 r85371  
    101101    int i_receiveData(GuestDnDRecvCtx *pCtx, RTMSINTERVAL msTimeout);
    102102    int i_receiveRawData(GuestDnDRecvCtx *pCtx, RTMSINTERVAL msTimeout);
    103     int i_receiveURIData(GuestDnDRecvCtx *pCtx, RTMSINTERVAL msTimeout);
     103    int i_receiveTransferData(GuestDnDRecvCtx *pCtx, RTMSINTERVAL msTimeout);
    104104
    105105protected:
  • trunk/src/VBox/Main/include/GuestDnDTargetImpl.h

    r85020 r85371  
    8787
    8888    int i_sendData(GuestDnDSendCtx *pCtx, RTMSINTERVAL msTimeout);
    89     int i_sendDataBody(GuestDnDSendCtx *pCtx, GuestDnDData *pData);
    90     int i_sendDataHeader(GuestDnDSendCtx *pCtx, GuestDnDData *pData, GuestDnDURIData *pURIData /* = NULL */);
    91     int i_sendDirectory(GuestDnDSendCtx *pCtx, GuestDnDURIObjCtx *pObjCtx, GuestDnDMsg *pMsg);
    92     int i_sendFile(GuestDnDSendCtx *pCtx, GuestDnDURIObjCtx *pObjCtx, GuestDnDMsg *pMsg);
    93     int i_sendFileData(GuestDnDSendCtx *pCtx, GuestDnDURIObjCtx *pObjCtx, GuestDnDMsg *pMsg);
     89
     90    int i_sendMetaDataBody(GuestDnDSendCtx *pCtx);
     91    int i_sendMetaDataHeader(GuestDnDSendCtx *pCtx);
     92
     93    int i_sendTransferData(GuestDnDSendCtx *pCtx, RTMSINTERVAL msTimeout);
     94    int i_sendTransferDataLoop(GuestDnDSendCtx *pCtx, GuestDnDMsg *pMsg);
     95
     96    int i_sendDirectory(GuestDnDSendCtx *pCtx, PDNDTRANSFEROBJECT pObj, GuestDnDMsg *pMsg);
     97    int i_sendFile(GuestDnDSendCtx *pCtx, PDNDTRANSFEROBJECT pObj, GuestDnDMsg *pMsg);
     98    int i_sendFileData(GuestDnDSendCtx *pCtx, PDNDTRANSFEROBJECT pObj, GuestDnDMsg *pMsg);
     99
    94100    int i_sendRawData(GuestDnDSendCtx *pCtx, RTMSINTERVAL msTimeout);
    95     int i_sendURIData(GuestDnDSendCtx *pCtx, RTMSINTERVAL msTimeout);
    96     int i_sendURIDataLoop(GuestDnDSendCtx *pCtx, GuestDnDMsg *pMsg);
    97101
    98102protected:
     
    100104    struct
    101105    {
     106        /** Flag indicating whether a data transfer currently is active. */
    102107        bool     mfTransferIsPending;
    103108        /** Maximum data block size (in bytes) the target can handle. */
  • trunk/src/VBox/Main/src-client/GuestDnDPrivate.cpp

    r82968 r85371  
    939939    /* cbDataAdd is optional. */
    940940
    941     LogFlowFunc(("cbTotal=%RU64, cbProcessed=%RU64, cbRemaining=%RU64, cbDataAdd=%RU32\n",
    942                  pData->getTotal(), pData->getProcessed(), pData->getRemaining(), cbDataAdd));
     941    LogFlowFunc(("cbExtra=%RU64, cbProcessed=%RU64, cbRemaining=%RU64, cbDataAdd=%RU32\n",
     942                 pData->cbExtra, pData->cbProcessed, pData->getRemaining(), cbDataAdd));
    943943
    944944    if (!pResp)
  • trunk/src/VBox/Main/src-client/GuestDnDSourceImpl.cpp

    r85020 r85371  
    420420    LogFlowThisFunc(("cTransfersPending=%RU32\n", mDataBase.m_cTransfersPending));
    421421
    422     /* Don't allow receiving the actual data until our transfer actually is complete. */
     422    /* Don't allow receiving the actual data until our current transfer is complete. */
    423423    if (mDataBase.m_cTransfersPending)
    424424        return setError(E_FAIL, tr("Current drop operation still in progress"));
    425425
    426     GuestDnDRecvCtx *pCtx = &mData.mRecvCtx;
    427426    HRESULT hr = S_OK;
    428427
    429428    try
    430429    {
    431         bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFmtRecv.c_str(), pCtx->mFmtRecv.length());
    432         if (fHasURIList)
    433         {
    434             LogRel2(("DnD: Drop directory is: %s\n", pCtx->mURI.getDroppedFiles().GetDirAbs()));
    435             int rc2 = pCtx->mURI.toMetaData(aData);
    436             if (RT_FAILURE(rc2))
    437                 hr = E_OUTOFMEMORY;
     430        GuestDnDRecvCtx *pCtx = &mData.mRecvCtx;
     431        if (DnDMIMENeedsDropDir(pCtx->mFmtRecv.c_str(), pCtx->mFmtRecv.length()))
     432        {
     433            PDNDDROPPEDFILES pDF = &pCtx->mTransfer.mDroppedFiles;
     434
     435            const char *pcszDropDirAbs = DnDDroppedFilesGetDirAbs(pDF);
     436            AssertPtr(pcszDropDirAbs);
     437
     438            LogRel2(("DnD: Using drop directory '%s'\n", pcszDropDirAbs));
     439
     440            char  *pszBuf;
     441            size_t cbBuf;
     442            int rc = DnDTransferListGetRootsEx(&pCtx->mTransfer.mList, DNDTRANSFERLISTFMT_NATIVE,
     443                                               pcszDropDirAbs, "\n" /* On all platforms */, &pszBuf, &cbBuf);
     444            if (RT_SUCCESS(rc))
     445            {
     446                aData.resize(cbBuf);
     447                memcpy(&aData.front(), pszBuf, cbBuf);
     448                RTStrFree(pszBuf);
     449            }
     450            else
     451                LogRel(("DnD: Unable to build source root list, rc=%Rrc\n", rc));
    438452        }
    439453        else
    440454        {
    441             const size_t cbData = pCtx->mData.getMeta().getSize();
    442             LogFlowFunc(("cbData=%zu\n", cbData));
    443             if (cbData)
     455            if (pCtx->Meta.cbData)
    444456            {
    445457                /* Copy the data into a safe array of bytes. */
    446                 aData.resize(cbData);
    447                 memcpy(&aData.front(), pCtx->mData.getMeta().getData(), cbData);
     458                aData.resize(pCtx->Meta.cbData);
     459                memcpy(&aData.front(), pCtx->Meta.pvData, pCtx->Meta.cbData);
    448460            }
    449461            else
     
    541553
    542554#ifdef VBOX_WITH_DRAG_AND_DROP_GH
     555/**
     556 * Handles receiving a send data header from the guest.
     557 *
     558 * @returns VBox status code.
     559 * @param   pCtx                Receive context to use.
     560 * @param   pDataHdr            Pointer to send data header from the guest.
     561 */
    543562int GuestDnDSource::i_onReceiveDataHdr(GuestDnDRecvCtx *pCtx, PVBOXDNDSNDDATAHDR pDataHdr)
    544563{
    545     AssertPtrReturn(pCtx,  VERR_INVALID_POINTER);
    546     AssertReturn(pDataHdr, VERR_INVALID_POINTER);
    547 
    548     pCtx->mData.setEstimatedSize(pDataHdr->cbTotal, pDataHdr->cbMeta);
    549 
    550     Assert(pCtx->mURI.getObjToProcess() == 0);
    551     pCtx->mURI.reset();
    552     pCtx->mURI.setEstimatedObjects(pDataHdr->cObjects);
     564    AssertPtrReturn(pCtx,     VERR_INVALID_POINTER);
     565    AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
     566
     567    LogRel2(("DnD: Receiving %RU64 bytes total data (%RU64 bytes meta data, %RU64 objects) from guest ...\n",
     568             pDataHdr->cbTotal, pDataHdr->cbMeta, pDataHdr->cObjects));
     569
     570    AssertReturn(pDataHdr->cbTotal >= pDataHdr->cbMeta, VERR_INVALID_PARAMETER);
     571
     572    int rc = pCtx->Meta.resize(pDataHdr->cbMeta);
     573    AssertRCReturn(rc, rc);
     574
     575    pCtx->cbExtra = pDataHdr->cbTotal - pDataHdr->cbMeta;
     576
     577    Assert(pCtx->mTransfer.cObjToProcess == 0); /* Sanity. */
     578    Assert(pCtx->mTransfer.cObjProcessed == 0);
     579
     580    pCtx->mTransfer.reset();
     581
     582    pCtx->mTransfer.cObjToProcess = pDataHdr->cObjects;
    553583
    554584    /** @todo Handle compression type. */
     
    559589}
    560590
     591/**
     592 * Handles receiving a send data block from the guest.
     593 *
     594 * @returns VBox status code.
     595 * @param   pCtx                Receive context to use.
     596 * @param   pSndData            Pointer to send data block from the guest.
     597 */
    561598int GuestDnDSource::i_onReceiveData(GuestDnDRecvCtx *pCtx, PVBOXDNDSNDDATA pSndData)
    562599{
     
    568605    try
    569606    {
    570         GuestDnDData    *pData = &pCtx->mData;
    571         GuestDnDURIData *pURI  = &pCtx->mURI;
     607        GuestDnDTransferRecvData *pTransfer = &pCtx->mTransfer;
    572608
    573609        uint32_t cbData;
     
    593629            pvData  = pSndData->u.v3.pvData;
    594630
    595             /* Note: Data sizes get updated in i_onReceiveDataHdr(). */
    596             cbTotal = pData->getTotal();
    597             cbMeta  = pData->getMeta().getSize();
    598         }
    599         Assert(cbTotal);
     631            /* Note: Data sizes get initialized in i_onReceiveDataHdr().
     632             *       So just use the set values here. */
     633            cbTotal = pCtx->getTotal();
     634            cbMeta  = pCtx->Meta.cbData;
     635        }
    600636
    601637        if (   cbData == 0
    602             || cbData >  cbTotal /* Paranoia */)
    603         {
    604             LogFlowFunc(("Incoming data size invalid: cbData=%RU32, cbToProcess=%RU64\n", cbData, pData->getTotal()));
     638            || cbData > cbTotal /* Paranoia */)
     639        {
     640            LogFlowFunc(("Incoming data size invalid: cbData=%RU32, cbToProcess=%RU64\n", cbData, cbTotal));
    605641            rc = VERR_INVALID_PARAMETER;
    606642        }
    607         else if (cbTotal < cbMeta)
     643        else if (   cbTotal == 0
     644                 || cbTotal  < cbMeta)
    608645        {
    609646            AssertMsgFailed(("cbTotal (%RU64) is smaller than cbMeta (%RU32)\n", cbTotal, cbMeta));
     
    611648        }
    612649
    613         if (RT_SUCCESS(rc))
    614         {
    615             cbMeta = pData->getMeta().add(pvData, cbData);
    616             LogFlowThisFunc(("cbMetaSize=%zu, cbData=%RU32, cbMeta=%RU32, cbTotal=%RU64\n",
    617                              pData->getMeta().getSize(), cbData, cbMeta, cbTotal));
    618         }
    619 
    620         if (RT_SUCCESS(rc))
    621         {
    622             /*
    623              * (Meta) Data transfer complete?
    624              */
    625             Assert(cbMeta <= pData->getMeta().getSize());
    626             if (cbMeta == pData->getMeta().getSize())
     650        if (RT_FAILURE(rc))
     651            return rc;
     652
     653        cbMeta = pCtx->Meta.add(pvData, cbData);
     654
     655        LogFlowThisFunc(("cbData=%RU32, cbMeta=%RU32, cbTotal=%RU64\n", cbData, cbMeta, cbTotal));
     656
     657        /*
     658         * (Meta) Data transfer complete?
     659         */
     660        Assert(cbMeta <= pCtx->Meta.cbAllocated);
     661        if (cbMeta == pCtx->Meta.cbAllocated)
     662        {
     663            LogRel2(("DnD: Receiving meta data complete\n"));
     664
     665            if (DnDMIMENeedsDropDir(pCtx->mFmtRecv.c_str(), pCtx->mFmtRecv.length()))
    627666            {
    628                 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFmtRecv.c_str(), pCtx->mFmtRecv.length());
    629                 LogFlowThisFunc(("fHasURIList=%RTbool\n", fHasURIList));
    630                 if (fHasURIList)
     667                LogRel2(("DnD: Building transfer list from meta data ...\n"));
     668
     669                rc = DnDTransferListAppendPathsFromBuffer(&pTransfer->mList, DNDTRANSFERLISTFMT_URI,
     670                                                          (const char *)pCtx->Meta.pvData, pCtx->Meta.cbData, DND_PATH_SEPARATOR,
     671                                                          DNDTRANSFERLIST_FLAGS_NONE);
     672                /* Validation. */
     673                if (RT_SUCCESS(rc))
    631674                {
    632                     /* Try parsing the data as URI list. */
    633                     rc = pURI->fromRemoteMetaData(pData->getMeta());
    634                     if (RT_SUCCESS(rc))
    635                     {
    636                         if (mDataBase.m_uProtocolVersion < 3)
    637                             pData->setEstimatedSize(cbTotal, cbMeta);
    638 
    639                         /*
    640                          * Update our process with the data we already received.
    641                          * Note: The total size will consist of the meta data (in pVecData) and
    642                          *       the actual accumulated file/directory data from the guest.
    643                          */
    644                         rc = updateProgress(pData, pCtx->mpResp, (uint32_t)pData->getMeta().getSize());
    645                     }
     675                    uint64_t cRoots = DnDTransferListGetRootCount(&pTransfer->mList);
     676                    if (   cRoots == 0
     677                        || cRoots > pTransfer->cObjToProcess)
     678                        rc = VERR_INVALID_PARAMETER;
    646679                }
    647                 else /* Raw data. */
    648                     rc = updateProgress(pData, pCtx->mpResp, cbData);
     680
     681                if (RT_SUCCESS(rc))
     682                {
     683                    /* Update our process with the data we already received. */
     684                    rc = updateProgress(pCtx, pCtx->mpResp, cbMeta);
     685                    AssertRC(rc);
     686                }
     687
     688                if (RT_FAILURE(rc))
     689                    LogRel(("DnD: Error building transfer list, rc=%Rrc\n", rc));
    649690            }
     691            else /* Raw data. */
     692            {
     693                rc = updateProgress(pCtx, pCtx->mpResp, cbData);
     694                AssertRC(rc);
     695            }
     696
     697            if (RT_FAILURE(rc))
     698                LogRel(("DnD: Error receiving meta data, rc=%Rrc\n", rc));
    650699        }
    651700    }
     
    667716    LogFlowFunc(("pszPath=%s, cbPath=%RU32, fMode=0x%x\n", pszPath, cbPath, fMode));
    668717
    669     /*
    670      * Sanity checking.
    671      */
    672     if (   !cbPath
    673         || cbPath > RTPATH_MAX)
    674     {
    675         LogFlowFunc(("Path length invalid, bailing out\n"));
    676         return VERR_INVALID_PARAMETER;
    677     }
    678 
    679     int rc = RTStrValidateEncodingEx(pszPath, RTSTR_MAX, 0);
     718    AssertMsgReturn(pCtx->isComplete() == false,
     719                    ("Data transfer already complete, bailing out\n"), VERR_INVALID_PARAMETER);
     720
     721    const PDNDTRANSFEROBJECT pObj = &pCtx->mTransfer.mObj;
     722    const PDNDDROPPEDFILES   pDF  = &pCtx->mTransfer.mDroppedFiles;
     723
     724    int rc = DnDTransferObjectInit(pObj, DNDTRANSFEROBJTYPE_DIRECTORY,
     725                                   DnDDroppedFilesGetDirAbs(pDF), pszPath);
     726    if (RT_SUCCESS(rc))
     727    {
     728        const char *pcszPathAbs = DnDTransferObjectGetSourcePath(pObj);
     729        AssertPtr(pcszPathAbs);
     730
     731        rc = RTDirCreateFullPath(pcszPathAbs, fMode);
     732        if (RT_SUCCESS(rc))
     733        {
     734            pCtx->mTransfer.cObjProcessed++;
     735            if (pCtx->mTransfer.cObjProcessed <= pCtx->mTransfer.cObjToProcess)
     736            {
     737                rc = DnDDroppedFilesAddDir(pDF, pcszPathAbs);
     738            }
     739            else
     740                rc = VERR_TOO_MUCH_DATA;
     741
     742            DnDTransferObjectDestroy(pObj);
     743
     744            if (RT_SUCCESS(rc))
     745                LogRel2(("DnD: Created guest directory '%s' on host\n", pcszPathAbs));
     746        }
     747        else
     748            LogRel(("DnD: Error creating guest directory '%s' on host, rc=%Rrc\n", pcszPathAbs, rc));
     749    }
     750
    680751    if (RT_FAILURE(rc))
    681     {
    682         LogFlowFunc(("Path validation failed with %Rrc, bailing out\n", rc));
    683         return VERR_INVALID_PARAMETER;
    684     }
    685 
    686     if (pCtx->mURI.isComplete())
    687     {
    688         LogFlowFunc(("Data transfer already complete, bailing out\n"));
    689         return VERR_INVALID_PARAMETER;
    690     }
    691 
    692     GuestDnDURIObjCtx &objCtx = pCtx->mURI.getObj(0); /** @todo Fill in context ID. */
    693 
    694     rc = objCtx.createIntermediate(DnDURIObject::Type_Directory);
    695     if (RT_FAILURE(rc))
    696         return rc;
    697 
    698     DnDURIObject *pObj = objCtx.getObj();
    699     AssertPtr(pObj);
    700 
    701     const char *pszDroppedFilesDir = pCtx->mURI.getDroppedFiles().GetDirAbs();
    702     char *pszDir = RTPathJoinA(pszDroppedFilesDir, pszPath);
    703     if (pszDir)
    704     {
    705 #ifdef RT_OS_WINDOWS
    706         RTPathChangeToDosSlashes(pszDir, true /* fForce */);
    707 #else
    708         RTPathChangeToDosSlashes(pszDir, true /* fForce */);
    709 #endif
    710         rc = RTDirCreateFullPath(pszDir, fMode);
    711         if (RT_SUCCESS(rc))
    712         {
    713             pCtx->mURI.processObject(*pObj);
    714 
    715             /* Add for having a proper rollback. */
    716             int rc2 = pCtx->mURI.getDroppedFiles().AddDir(pszDir);
    717             AssertRC(rc2);
    718 
    719             objCtx.reset();
    720             LogRel2(("DnD: Created guest directory '%s' on host\n", pszDir));
    721         }
    722         else
    723             LogRel(("DnD: Error creating guest directory '%s' on host, rc=%Rrc\n", pszDir, rc));
    724 
    725         RTStrFree(pszDir);
    726     }
    727     else
    728          rc = VERR_NO_MEMORY;
     752        LogRel(("DnD: Receiving guest directory '%s' failed with rc=%Rrc\n", pszPath, rc));
    729753
    730754    LogFlowFuncLeaveRC(rc);
     
    732756}
    733757
     758/**
     759 * Receives a file header from the guest.
     760 *
     761 * @returns VBox status code.
     762 * @param   pCtx                Receive context to use.
     763 * @param   pszPath             File path of file to use.
     764 * @param   cbPath              Size (in bytes, including terminator) of file path.
     765 * @param   cbSize              File size (in bytes) to receive.
     766 * @param   fMode               File mode to use.
     767 * @param   fFlags              Additional receive flags; not used yet.
     768 */
    734769int GuestDnDSource::i_onReceiveFileHdr(GuestDnDRecvCtx *pCtx, const char *pszPath, uint32_t cbPath,
    735770                                       uint64_t cbSize, uint32_t fMode, uint32_t fFlags)
    736771{
    737     RT_NOREF(fFlags);
    738772    AssertPtrReturn(pCtx,    VERR_INVALID_POINTER);
    739773    AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
     
    742776    /* fFlags are optional. */
    743777
     778    RT_NOREF(fFlags);
     779
    744780    LogFlowFunc(("pszPath=%s, cbPath=%RU32, cbSize=%RU64, fMode=0x%x, fFlags=0x%x\n", pszPath, cbPath, cbSize, fMode, fFlags));
    745781
    746     /*
    747      * Sanity checking.
    748      */
    749     if (   !cbPath
    750         || cbPath > RTPATH_MAX)
    751     {
    752         return VERR_INVALID_PARAMETER;
    753     }
    754 
    755     if (!RTStrIsValidEncoding(pszPath))
    756         return VERR_INVALID_PARAMETER;
    757 
    758     if (cbSize > pCtx->mData.getTotal())
    759     {
    760         AssertMsgFailed(("File size (%RU64) exceeds total size to transfer (%RU64)\n", cbSize, pCtx->mData.getTotal()));
    761         return VERR_INVALID_PARAMETER;
    762     }
    763 
    764     if (pCtx->mURI.getObjToProcess() && pCtx->mURI.isComplete())
    765         return VERR_INVALID_PARAMETER;
     782    AssertMsgReturn(cbSize <= pCtx->cbExtra,
     783                    ("File size (%RU64) exceeds extra size to transfer (%RU64)\n", cbSize, pCtx->cbExtra), VERR_INVALID_PARAMETER);
     784    AssertMsgReturn(   pCtx->isComplete() == false
     785                    && pCtx->mTransfer.cObjToProcess,
     786                    ("Data transfer already complete, bailing out\n"), VERR_INVALID_PARAMETER);
    766787
    767788    int rc = VINF_SUCCESS;
     
    769790    do
    770791    {
    771         GuestDnDURIObjCtx &objCtx = pCtx->mURI.getObj(0); /** @todo Fill in context ID. */
    772         DnDURIObject *pObj        = objCtx.getObj();
    773 
    774         /*
    775          * Sanity checking.
    776          */
    777         if (pObj)
    778         {
    779             if (    pObj->IsOpen()
    780                 && !pObj->IsComplete())
     792        const PDNDTRANSFEROBJECT pObj = &pCtx->mTransfer.mObj;
     793
     794        if (    DnDTransferObjectIsOpen(pObj)
     795            && !DnDTransferObjectIsComplete(pObj))
     796        {
     797            AssertMsgFailed(("Object '%s' not complete yet\n", DnDTransferObjectGetSourcePath(pObj)));
     798            rc = VERR_WRONG_ORDER;
     799            break;
     800        }
     801
     802        const PDNDDROPPEDFILES pDF = &pCtx->mTransfer.mDroppedFiles;
     803
     804        rc = DnDTransferObjectInit(pObj, DNDTRANSFEROBJTYPE_FILE, DnDDroppedFilesGetDirAbs(pDF), pszPath);
     805        AssertRCBreak(rc);
     806
     807        const char *pcszSource = DnDTransferObjectGetSourcePath(pObj);
     808        AssertPtrBreakStmt(pcszSource, VERR_INVALID_POINTER);
     809
     810        /** @todo Add sparse file support based on fFlags? (Use Open(..., fFlags | SPARSE). */
     811        rc = DnDTransferObjectOpen(pObj, RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE,
     812                                   (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR, DNDTRANSFEROBJECT_FLAGS_NONE);
     813        if (RT_FAILURE(rc))
     814        {
     815            LogRel(("DnD: Error opening/creating guest file '%s' on host, rc=%Rrc\n", pcszSource, rc));
     816            break;
     817        }
     818
     819        /* Note: Protocol v1 does not send any file sizes, so always 0. */
     820        if (mDataBase.m_uProtocolVersion >= 2)
     821            rc = DnDTransferObjectSetSize(pObj, cbSize);
     822
     823        /** @todo Unescape path before printing. */
     824        LogRel2(("DnD: Transferring guest file '%s' to host (%RU64 bytes, mode %#x)\n",
     825                 pcszSource, DnDTransferObjectGetSize(pObj), DnDTransferObjectGetMode(pObj)));
     826
     827        /** @todo Set progress object title to current file being transferred? */
     828
     829        if (DnDTransferObjectIsComplete(pObj)) /* 0-byte file? We're done already. */
     830        {
     831            LogRel2(("DnD: Transferring guest file '%s' (0 bytes) to host complete\n", pcszSource));
     832
     833            pCtx->mTransfer.cObjProcessed++;
     834            if (pCtx->mTransfer.cObjProcessed <= pCtx->mTransfer.cObjToProcess)
    781835            {
    782                 AssertMsgFailed(("Object '%s' not complete yet\n", pObj->GetPath().c_str()));
    783                 rc = VERR_WRONG_ORDER;
    784                 break;
     836                /* Add for having a proper rollback. */
     837                rc = DnDDroppedFilesAddFile(pDF, pcszSource);
    785838            }
    786 
    787             if (pObj->IsOpen()) /* File already opened? */
    788             {
    789                 AssertMsgFailed(("Current opened object is '%s', close this first\n", pObj->GetPath().c_str()));
    790                 rc = VERR_WRONG_ORDER;
    791                 break;
    792             }
    793         }
    794         else
    795         {
    796             /*
    797              * Create new intermediate object to work with.
    798              */
    799             rc = objCtx.createIntermediate(DnDURIObject::Type_File);
    800         }
    801 
    802         if (RT_SUCCESS(rc))
    803         {
    804             pObj = objCtx.getObj();
    805             AssertPtr(pObj);
    806 
    807             const char *pszDroppedFilesDir = pCtx->mURI.getDroppedFiles().GetDirAbs();
    808             AssertPtr(pszDroppedFilesDir);
    809 
    810             char pszPathAbs[RTPATH_MAX];
    811             rc = RTPathJoin(pszPathAbs, sizeof(pszPathAbs), pszDroppedFilesDir, pszPath);
    812             if (RT_FAILURE(rc))
    813             {
    814                 LogFlowFunc(("Warning: Rebasing current file failed with rc=%Rrc\n", rc));
    815                 break;
    816             }
    817 
    818             rc = pObj->Init(DnDURIObject::Type_File, pszPathAbs);
    819             if (RT_SUCCESS(rc))
    820             {
    821                 /** @todo Add sparse file support based on fFlags? (Use Open(..., fFlags | SPARSE). */
    822                 rc = pObj->Open(RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE,
    823                                 (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR);
    824                 if (RT_SUCCESS(rc))
    825                 {
    826                     /* Add for having a proper rollback. */
    827                     int rc2 = pCtx->mURI.getDroppedFiles().AddFile(pszPathAbs);
    828                     AssertRC(rc2);
    829                 }
    830             }
    831 
    832             if (RT_FAILURE(rc))
    833                 LogRel(("DnD: Error opening/creating guest file '%s' on host, rc=%Rrc\n", pszPathAbs, rc));
    834         }
    835 
    836         if (RT_SUCCESS(rc))
    837         {
    838             /* Note: Protocol v1 does not send any file sizes, so always 0. */
    839             if (mDataBase.m_uProtocolVersion >= 2)
    840                 rc = pObj->SetSize(cbSize);
    841 
    842             /** @todo Unescape path before printing. */
    843             LogRel2(("DnD: Transferring guest file '%s' to host (%RU64 bytes, mode 0x%x)\n",
    844                      pObj->GetPath().c_str(), pObj->GetSize(), pObj->GetMode()));
    845 
    846             /** @todo Set progress object title to current file being transferred? */
    847 
    848             if (pObj->IsComplete()) /* 0-byte file? We're done already. */
    849             {
    850                 LogRel2(("DnD: Transferring guest file '%s' (0 bytes) to host complete\n", pObj->GetPath().c_str()));
    851 
    852                 pCtx->mURI.processObject(*pObj);
    853                 pObj->Close();
    854 
    855                 objCtx.reset();
    856             }
     839            else
     840                rc = VERR_TOO_MUCH_DATA;
     841
     842            DnDTransferObjectDestroy(pObj);
    857843        }
    858844
     
    884870    do
    885871    {
    886         GuestDnDURIObjCtx &objCtx = pCtx->mURI.getObj(0); /** @todo Fill in context ID. */
    887         DnDURIObject *pObj        = objCtx.getObj();
    888 
    889         if (!pObj)
    890         {
    891             LogFlowFunc(("Warning: No current object set\n"));
     872        const PDNDTRANSFEROBJECT pObj = &pCtx->mTransfer.mObj;
     873
     874        if (    DnDTransferObjectIsOpen(pObj)
     875            && !DnDTransferObjectIsComplete(pObj))
     876        {
     877            AssertMsgFailed(("Object '%s' not complete yet\n", DnDTransferObjectGetSourcePath(pObj)));
    892878            rc = VERR_WRONG_ORDER;
    893879            break;
    894880        }
    895881
    896         if (pObj->IsComplete())
    897         {
    898             LogFlowFunc(("Warning: Object '%s' already completed\n", pObj->GetPath().c_str()));
    899             rc = VERR_WRONG_ORDER;
    900             break;
    901         }
    902 
    903         if (!pObj->IsOpen()) /* File opened on host? */
    904         {
    905             LogFlowFunc(("Warning: Object '%s' not opened\n", pObj->GetPath().c_str()));
    906             rc = VERR_WRONG_ORDER;
    907             break;
    908         }
     882        const char *pcszSource = DnDTransferObjectGetSourcePath(pObj);
     883        AssertPtrBreakStmt(pcszSource, VERR_INVALID_POINTER);
    909884
    910885        uint32_t cbWritten;
    911         rc = pObj->Write(pvData, cbData, &cbWritten);
    912         if (RT_SUCCESS(rc))
    913         {
    914             Assert(cbWritten <= cbData);
    915             if (cbWritten < cbData)
    916             {
    917                 LogRel(("DnD: Only written %RU32 of %RU32 bytes of guest file '%s' -- disk full?\n",
    918                         cbWritten, cbData, pObj->GetPath().c_str()));
    919                 rc = VERR_IO_GEN_FAILURE; /** @todo Find a better rc. */
    920             }
    921 
    922             if (RT_SUCCESS(rc))
    923                 rc = updateProgress(&pCtx->mData, pCtx->mpResp, cbWritten);
    924         }
    925         else
    926             LogRel(("DnD: Error writing guest file data for '%s', rc=%Rrc\n", pObj->GetPath().c_str(), rc));
    927 
    928         if (RT_SUCCESS(rc))
    929         {
    930             if (pObj->IsComplete())
    931             {
    932                 /** @todo Sanitize path. */
    933                 LogRel2(("DnD: Transferring guest file '%s' to host complete\n", pObj->GetPath().c_str()));
    934                 pCtx->mURI.processObject(*pObj);
    935                 objCtx.reset();
    936             }
     886        rc = DnDTransferObjectWrite(pObj, pvData, cbData, &cbWritten);
     887        if (RT_FAILURE(rc))
     888            LogRel(("DnD: Error writing guest file data for '%s', rc=%Rrc\n", pcszSource, rc));
     889
     890        Assert(cbWritten <= cbData);
     891        if (cbWritten < cbData)
     892        {
     893            LogRel(("DnD: Only written %RU32 of %RU32 bytes of guest file '%s' -- disk full?\n",
     894                    cbWritten, cbData, pcszSource));
     895            rc = VERR_IO_GEN_FAILURE; /** @todo Find a better rc. */
     896            break;
     897        }
     898
     899        rc = updateProgress(pCtx, pCtx->mpResp, cbWritten);
     900        AssertRCBreak(rc);
     901
     902        if (DnDTransferObjectIsComplete(pObj))
     903        {
     904            LogRel2(("DnD: Transferring guest file '%s' to host complete\n", pcszSource));
     905
     906            pCtx->mTransfer.cObjProcessed++;
     907            if (pCtx->mTransfer.cObjProcessed > pCtx->mTransfer.cObjToProcess)
     908                rc = VERR_TOO_MUCH_DATA;
     909
     910            DnDTransferObjectDestroy(pObj);
    937911        }
    938912
     
    974948     * Reset any old data.
    975949     */
    976     pCtx->mData.reset();
    977     pCtx->mURI.reset();
     950    pCtx->reset();
     951    pCtx->mTransfer.reset();
    978952    pResp->reset();
    979953
     
    10291003        if (fURIData)
    10301004        {
    1031             rc = i_receiveURIData(pCtx, msTimeout);
     1005            rc = i_receiveTransferData(pCtx, msTimeout);
    10321006        }
    10331007        else
     
    11841158}
    11851159
    1186 int GuestDnDSource::i_receiveURIData(GuestDnDRecvCtx *pCtx, RTMSINTERVAL msTimeout)
     1160int GuestDnDSource::i_receiveTransferData(GuestDnDRecvCtx *pCtx, RTMSINTERVAL msTimeout)
    11871161{
    11881162    AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
     
    12271201    REGISTER_CALLBACK(GUEST_DND_GH_SND_FILE_DATA);
    12281202
    1229     DnDDroppedFiles &droppedFiles = pCtx->mURI.getDroppedFiles();
     1203    const PDNDDROPPEDFILES pDF = &pCtx->mTransfer.mDroppedFiles;
    12301204
    12311205    do
    12321206    {
    1233         rc = droppedFiles.OpenTemp(0 /* fFlags */);
     1207        rc = DnDDroppedFilesOpenTemp(pDF, 0 /* fFlags */);
    12341208        if (RT_FAILURE(rc))
    12351209        {
    1236             LogRel(("DnD: Opening dropped files directory '%s' on the host failed with rc=%Rrc\n", droppedFiles.GetDirAbs(), rc));
     1210            LogRel(("DnD: Opening dropped files directory '%s' on the host failed with rc=%Rrc\n",
     1211                    DnDDroppedFilesGetDirAbs(pDF), rc));
    12371212            break;
    12381213        }
    12391214
    12401215        /*
    1241          * Receive the URI list.
     1216         * Receive the transfer list.
    12421217         */
    12431218        GuestDnDMsg Msg;
     
    12821257    if (RT_FAILURE(rc))
    12831258    {
    1284         int rc2 = droppedFiles.Rollback();
     1259        int rc2 = DnDDroppedFilesRollback(pDF);
    12851260        if (RT_FAILURE(rc2))
    12861261            LogRel(("DnD: Deleting left over temporary files failed (%Rrc), please remove directory '%s' manually\n",
    1287                     rc2, droppedFiles.GetDirAbs()));
     1262                    rc2, DnDDroppedFilesGetDirAbs(pDF)));
    12881263
    12891264        if (rc == VERR_CANCELLED)
     
    13101285    }
    13111286
    1312     droppedFiles.Close();
     1287    DnDDroppedFilesClose(pDF);
    13131288
    13141289    LogFlowFuncLeaveRC(rc);
     
    14291404
    14301405    /* All data processed? */
    1431     if (pCtx->mData.isComplete())
     1406    if (pCtx->isComplete())
    14321407        fNotify = true;
    14331408
    1434     LogFlowFunc(("cbProcessed=%RU64, cbToProcess=%RU64, fNotify=%RTbool, rcCallback=%Rrc, rc=%Rrc\n",
    1435                  pCtx->mData.getProcessed(), pCtx->mData.getTotal(), fNotify, rcCallback, rc));
     1409    LogFlowFunc(("cbProcessed=%RU64, cbExtra=%RU64, fNotify=%RTbool, rcCallback=%Rrc, rc=%Rrc\n",
     1410                 pCtx->cbProcessed, pCtx->cbExtra, fNotify, rcCallback, rc));
    14361411
    14371412    if (fNotify)
     
    16041579
    16051580    /* All data processed? */
    1606     if (   pCtx->mURI.isComplete()
    1607         && pCtx->mData.isComplete())
     1581    if (   pCtx->mTransfer.isComplete()
     1582        && pCtx->isComplete())
    16081583    {
    16091584        fNotify = true;
    16101585    }
    16111586
    1612     LogFlowFunc(("cbProcessed=%RU64, cbToProcess=%RU64, fNotify=%RTbool, rcCallback=%Rrc, rc=%Rrc\n",
    1613                  pCtx->mData.getProcessed(), pCtx->mData.getTotal(), fNotify, rcCallback, rc));
     1587    LogFlowFunc(("cbProcessed=%RU64, cbExtra=%RU64, fNotify=%RTbool, rcCallback=%Rrc, rc=%Rrc\n",
     1588                 pCtx->cbProcessed, pCtx->cbExtra, fNotify, rcCallback, rc));
    16141589
    16151590    if (fNotify)
  • trunk/src/VBox/Main/src-client/GuestDnDTargetImpl.cpp

    r85020 r85371  
    563563}
    564564
     565/**
     566 * Thread handler function for sending data to the guest.
     567 *
     568 * @param   pTask               Thread task this handler is associated with.
     569 */
    565570/* static */
    566571void GuestDnDTarget::i_sendDataThreadTask(GuestDnDSendDataTask *pTask)
     
    593598
    594599/**
    595  * Initiates a data transfer from the host to the guest. The source is the host whereas the target is the
    596  * guest in this case.
     600 * Initiates a data transfer from the host to the guest.
     601 *
     602 * The source is the host, whereas the target is the guest.
    597603 *
    598604 * @return  HRESULT
    599  * @param   aScreenId
    600  * @param   aFormat
    601  * @param   aData
    602  * @param   aProgress
     605 * @param   aScreenId           Screen ID where this data transfer was initiated from.
     606 * @param   aFormat             Format of data to send. MIME-style.
     607 * @param   aData               Actual data to send.
     608 * @param   aProgress           Where to return the progress object on success.
    603609 */
    604610HRESULT GuestDnDTarget::sendData(ULONG aScreenId, const com::Utf8Str &aFormat, const std::vector<BYTE> &aData,
     
    632638        return hr;
    633639
    634     GuestDnDSendDataTask *pTask = NULL;
    635     GuestDnDSendCtx *pSendCtx = NULL;
     640    GuestDnDSendDataTask *pTask    = NULL;
     641    GuestDnDSendCtx      *pSendCtx = NULL;
    636642
    637643    try
    638644    {
    639         //pSendCtx is passed into SendDataTask where one is deleted in destructor
     645        /* pSendCtx is passed into SendDataTask where one is deleted in destructor. */
    640646        pSendCtx = new GuestDnDSendCtx();
    641         pSendCtx->mpTarget      = this;
    642         pSendCtx->mpResp        = pResp;
    643         pSendCtx->mScreenID     = aScreenId;
    644         pSendCtx->mFmtReq       = aFormat;
    645         pSendCtx->mData.getMeta().add(aData);
     647        pSendCtx->mpTarget  = this;
     648        pSendCtx->mpResp    = pResp;
     649        pSendCtx->mScreenID = aScreenId;
     650        pSendCtx->mFmtReq   = aFormat;
     651
     652        pSendCtx->Meta.add(aData);
    646653
    647654        /* pTask is responsible for deletion of pSendCtx after creating */
     
    757764
    758765/**
    759  * @returns VBox status code that the caller ignores. Not sure if that's
    760  *          intentional or not.
     766 * Main function for sending DnD host data to the guest.
     767 *
     768 * @returns VBox status code.
     769 * @param   pCtx                Send context to use.
     770 * @param   msTimeout           Timeout (in ms) to wait for getting the data sent.
    761771 */
    762772int GuestDnDTarget::i_sendData(GuestDnDSendCtx *pCtx, RTMSINTERVAL msTimeout)
     
    776786     * Note: The decision whether we need to build up a file tree and sending
    777787     *       actual file data only depends on the actual formats offered by this target.
    778      *       If the guest does not want an URI list ("text/uri-list") but text ("TEXT" and
     788     *       If the guest does not want a transfer list ("text/uri-list") but text ("TEXT" and
    779789     *       friends) instead, still send the data over to the guest -- the file as such still
    780790     *       is needed on the guest in this case, as the guest then just wants a simple path
    781      *       instead of an URI list (pointing to a file on the guest itself).
     791     *       instead of a transfer list (pointing to a file on the guest itself).
    782792     *
    783793     ** @todo Support more than one format; add a format<->function handler concept. Later. */
     
    787797    if (fHasURIList)
    788798    {
    789         rc = i_sendURIData(pCtx, msTimeout);
     799        rc = i_sendTransferData(pCtx, msTimeout);
    790800    }
    791801    else
     
    800810}
    801811
    802 int GuestDnDTarget::i_sendDataBody(GuestDnDSendCtx *pCtx, GuestDnDData *pData)
    803 {
    804     AssertPtrReturn(pCtx,  VERR_INVALID_POINTER);
    805     AssertPtrReturn(pData, VERR_INVALID_POINTER);
     812/**
     813 * Sends the common meta data body to the guest.
     814 *
     815 * @returns VBox status code.
     816 * @param   pCtx                Send context to use.
     817 */
     818int GuestDnDTarget::i_sendMetaDataBody(GuestDnDSendCtx *pCtx)
     819{
     820    AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
    806821
    807822    /** @todo Add support for multiple HOST_DND_HG_SND_DATA messages in case of more than 64K data! */
    808     if (pData->getMeta().getSize() > _64K)
     823    if (pCtx->Meta.cbData > _64K)
    809824        return VERR_NOT_IMPLEMENTED;
    810825
     826    const uint32_t cbFmt = (uint32_t)pCtx->Meta.strFmt.length() + 1; /* Include terminator. */
     827
     828    LogFlowFunc(("cbFmt=%RU32, cbMeta=%RU32\n", cbFmt, pCtx->Meta.cbData));
     829
    811830    GuestDnDMsg Msg;
    812 
    813     LogFlowFunc(("cbFmt=%RU32, cbMeta=%RU32, cbChksum=%RU32\n",
    814                  pData->getFmtSize(), pData->getMeta().getSize(), pData->getChkSumSize()));
    815 
    816831    Msg.setType(HOST_DND_HG_SND_DATA);
    817832    if (mDataBase.m_uProtocolVersion < 3)
    818833    {
    819         Msg.setNextUInt32(pCtx->mScreenID);                                                /* uScreenId */
    820         Msg.setNextPointer(pData->getFmtMutable(), pData->getFmtSize());                   /* pvFormat */
    821         Msg.setNextUInt32(pData->getFmtSize());                                            /* cbFormat */
    822         Msg.setNextPointer(pData->getMeta().getDataMutable(), pData->getMeta().getSize()); /* pvData */
     834        Msg.setNextUInt32(pCtx->mScreenID);                                 /* uScreenId */
     835        Msg.setNextPointer(pCtx->Meta.strFmt.mutableRaw(), cbFmt);          /* pvFormat */
     836        Msg.setNextUInt32(cbFmt);                                           /* cbFormat */
     837        Msg.setNextPointer(pCtx->Meta.pvData, (uint32_t)pCtx->Meta.cbData); /* pvData */
    823838        /* Fill in the current data block size to send.
    824839         * Note: Only supports uint32_t. */
    825         Msg.setNextUInt32((uint32_t)pData->getMeta().getSize());                           /* cbData */
     840        Msg.setNextUInt32((uint32_t)pCtx->Meta.cbData);                     /* cbData */
    826841    }
    827842    else
    828843    {
    829844        Msg.setNextUInt32(0); /** @todo ContextID not used yet. */
    830         Msg.setNextPointer(pData->getMeta().getDataMutable(), pData->getMeta().getSize()); /* pvData */
    831         Msg.setNextUInt32(pData->getMeta().getSize());                                     /* cbData */
    832         Msg.setNextPointer(pData->getChkSumMutable(), pData->getChkSumSize());             /** @todo pvChecksum; not used yet. */
    833         Msg.setNextUInt32(pData->getChkSumSize());                                         /** @todo cbChecksum; not used yet. */
     845        Msg.setNextPointer(pCtx->Meta.pvData, (uint32_t)pCtx->Meta.cbData); /* pvData */
     846        Msg.setNextUInt32((uint32_t)pCtx->Meta.cbData);                     /* cbData */
     847        Msg.setNextPointer(NULL, 0);                                        /** @todo pvChecksum; not used yet. */
     848        Msg.setNextUInt32(0);                                               /** @todo cbChecksum; not used yet. */
    834849    }
    835850
    836851    int rc = GUESTDNDINST()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
    837852    if (RT_SUCCESS(rc))
    838         rc = updateProgress(pData, pCtx->mpResp, pData->getMeta().getSize());
     853    {
     854        rc = updateProgress(pCtx, pCtx->mpResp, pCtx->Meta.cbData);
     855        AssertRC(rc);
     856    }
    839857
    840858    LogFlowFuncLeaveRC(rc);
     
    842860}
    843861
    844 int GuestDnDTarget::i_sendDataHeader(GuestDnDSendCtx *pCtx, GuestDnDData *pData, GuestDnDURIData *pURIData /* = NULL */)
    845 {
    846     AssertPtrReturn(pCtx,  VERR_INVALID_POINTER);
    847     AssertPtrReturn(pData, VERR_INVALID_POINTER);
    848     /* pURIData is optional. */
     862/**
     863 * Sends the common meta data header to the guest.
     864 *
     865 * @returns VBox status code.
     866 * @param   pCtx                Send context to use.
     867 */
     868int GuestDnDTarget::i_sendMetaDataHeader(GuestDnDSendCtx *pCtx)
     869{
     870    AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
     871
     872    if (mDataBase.m_uProtocolVersion < 3) /* Protocol < v3 did not support this, skip. */
     873        return VINF_SUCCESS;
    849874
    850875    GuestDnDMsg Msg;
     
    855880    Msg.setNextUInt32(0);                                                /** @todo uFlags; not used yet. */
    856881    Msg.setNextUInt32(pCtx->mScreenID);                                  /* uScreen */
    857     Msg.setNextUInt64(pData->getTotal());                                /* cbTotal */
    858     Msg.setNextUInt32(pData->getMeta().getSize());                       /* cbMeta*/
    859     Msg.setNextPointer(pData->getFmtMutable(), pData->getFmtSize());    /* pvMetaFmt */
    860     Msg.setNextUInt32(pData->getFmtSize());                              /* cbMetaFmt */
    861     Msg.setNextUInt64(pURIData ? pURIData->getObjToProcess() : 0);       /* cObjects */
     882    Msg.setNextUInt64(pCtx->getTotal());                                 /* cbTotal */
     883    Msg.setNextUInt32(pCtx->Meta.cbData);                                /* cbMeta*/
     884    Msg.setNextPointer(unconst(pCtx->Meta.strFmt.c_str()), (uint32_t)pCtx->Meta.strFmt.length() + 1); /* pvMetaFmt */
     885    Msg.setNextUInt32((uint32_t)pCtx->Meta.strFmt.length() + 1);                                      /* cbMetaFmt */
     886    Msg.setNextUInt64(pCtx->mTransfer.cObjToProcess );                   /* cObjects */
    862887    Msg.setNextUInt32(0);                                                /** @todo enmCompression; not used yet. */
    863888    Msg.setNextUInt32(0);                                                /** @todo enmChecksumType; not used yet. */
     
    871896}
    872897
    873 int GuestDnDTarget::i_sendDirectory(GuestDnDSendCtx *pCtx, GuestDnDURIObjCtx *pObjCtx, GuestDnDMsg *pMsg)
    874 {
    875     AssertPtrReturn(pCtx,    VERR_INVALID_POINTER);
    876     AssertPtrReturn(pObjCtx, VERR_INVALID_POINTER);
    877     AssertPtrReturn(pMsg,    VERR_INVALID_POINTER);
    878 
    879     DnDURIObject *pObj = pObjCtx->getObj();
    880     AssertPtr(pObj);
    881 
    882     RTCString strPath = pObj->GetPath();
    883     if (strPath.isEmpty())
    884         return VERR_INVALID_PARAMETER;
    885     if (strPath.length() >= RTPATH_MAX) /* Note: Maximum is RTPATH_MAX on guest side. */
    886         return VERR_BUFFER_OVERFLOW;
    887 
    888     LogRel2(("DnD: Transferring host directory '%s' to guest\n", strPath.c_str()));
     898int GuestDnDTarget::i_sendDirectory(GuestDnDSendCtx *pCtx, PDNDTRANSFEROBJECT pObj, GuestDnDMsg *pMsg)
     899{
     900    AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
     901    AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
     902
     903    const char *pcszDstPath = DnDTransferObjectGetDestPath(pObj);
     904    AssertPtrReturn(pcszDstPath, VERR_INVALID_POINTER);
     905    const size_t cchPath = RTStrNLen(pcszDstPath, RTPATH_MAX); /* Note: Maximum is RTPATH_MAX on guest side. */
     906    AssertReturn(cchPath, VERR_INVALID_PARAMETER);
     907
     908    LogRel2(("DnD: Transferring host directory '%s' to guest\n", DnDTransferObjectGetSourcePath(pObj)));
    889909
    890910    pMsg->setType(HOST_DND_HG_SND_DIR);
    891911    if (mDataBase.m_uProtocolVersion >= 3)
    892912        pMsg->setNextUInt32(0); /** @todo ContextID not used yet. */
    893     pMsg->setNextString(strPath.c_str());                  /* path */
    894     pMsg->setNextUInt32((uint32_t)(strPath.length() + 1)); /* path length (maximum is RTPATH_MAX on guest side). */
    895     pMsg->setNextUInt32(pObj->GetMode());                  /* mode */
     913    pMsg->setNextString(pcszDstPath);                    /* path */
     914    pMsg->setNextUInt32((uint32_t)(cchPath + 1));        /* path length, including terminator. */
     915    pMsg->setNextUInt32(DnDTransferObjectGetMode(pObj)); /* mode */
    896916
    897917    return VINF_SUCCESS;
    898918}
    899919
    900 int GuestDnDTarget::i_sendFile(GuestDnDSendCtx *pCtx, GuestDnDURIObjCtx *pObjCtx, GuestDnDMsg *pMsg)
    901 {
    902     AssertPtrReturn(pCtx,    VERR_INVALID_POINTER);
    903     AssertPtrReturn(pObjCtx, VERR_INVALID_POINTER);
    904     AssertPtrReturn(pMsg,    VERR_INVALID_POINTER);
    905 
    906     DnDURIObject *pObj = pObjCtx->getObj();
    907     AssertPtr(pObj);
    908 
    909     RTCString strPathSrc = pObj->GetPath();
    910     if (strPathSrc.isEmpty())
    911         return VERR_INVALID_PARAMETER;
     920/**
     921 * Sends a transfer file to the guest.
     922 *
     923 * @returns VBox status code.
     924 * @param   pCtx
     925 * @param   pObj
     926 * @param   pMsg
     927 */
     928int GuestDnDTarget::i_sendFile(GuestDnDSendCtx *pCtx,
     929                               PDNDTRANSFEROBJECT pObj, GuestDnDMsg *pMsg)
     930{
     931    AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
     932    AssertPtrReturn(pObj, VERR_INVALID_POINTER);
     933    AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
     934
     935    const char *pcszSrcPath = DnDTransferObjectGetSourcePath(pObj);
     936    AssertPtrReturn(pcszSrcPath, VERR_INVALID_POINTER);
     937    const char *pcszDstPath = DnDTransferObjectGetDestPath(pObj);
     938    AssertPtrReturn(pcszDstPath, VERR_INVALID_POINTER);
    912939
    913940    int rc = VINF_SUCCESS;
    914941
    915     LogFlowFunc(("Sending file with %RU32 bytes buffer, using protocol v%RU32 ...\n",
    916                   mData.mcbBlockSize, mDataBase.m_uProtocolVersion));
    917     LogFlowFunc(("strPathSrc=%s, fIsOpen=%RTbool, cbSize=%RU64\n", strPathSrc.c_str(), pObj->IsOpen(), pObj->GetSize()));
    918 
    919     if (!pObj->IsOpen())
    920     {
    921         LogRel2(("DnD: Opening host file '%s' for transferring to guest\n", strPathSrc.c_str()));
    922         rc = pObj->Init(DnDURIObject::Type_File, strPathSrc);
    923         if (RT_SUCCESS(rc))
    924             rc = pObj->Open(RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
    925 
     942    if (!DnDTransferObjectIsOpen(pObj))
     943    {
     944        LogRel2(("DnD: Opening host file '%s' for transferring to guest\n", pcszSrcPath));
     945
     946        rc = DnDTransferObjectOpen(pObj, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE, 0 /* fMode */,
     947                                   DNDTRANSFEROBJECT_FLAGS_NONE);
    926948        if (RT_FAILURE(rc))
    927             LogRel(("DnD: Opening host file '%s' failed, rc=%Rrc\n", strPathSrc.c_str(), rc));
    928     }
     949            LogRel(("DnD: Opening host file '%s' failed, rc=%Rrc\n", pcszSrcPath, rc));
     950    }
     951
     952    if (RT_FAILURE(rc))
     953        return rc;
    929954
    930955    bool fSendData = false;
     
    933958        if (mDataBase.m_uProtocolVersion >= 2)
    934959        {
    935             uint32_t fState = pObjCtx->getState();
    936             if (!(fState & DND_OBJCTX_STATE_HAS_HDR))
     960            if (!(pCtx->mTransfer.mfObjState & DND_OBJ_STATE_HAS_HDR))
    937961            {
     962                const size_t  cchDstPath = RTStrNLen(pcszDstPath, RTPATH_MAX);
     963                const size_t  cbSize     = DnDTransferObjectGetSize(pObj);
     964                const RTFMODE fMode      = DnDTransferObjectGetMode(pObj);
     965
    938966                /*
    939967                 * Since protocol v2 the file header and the actual file contents are
     
    943971                pMsg->setType(HOST_DND_HG_SND_FILE_HDR);
    944972                pMsg->setNextUInt32(0); /** @todo ContextID not used yet. */
    945                 pMsg->setNextString(pObj->GetPath().c_str());                  /* pvName */
    946                 pMsg->setNextUInt32((uint32_t)(pObj->GetPath().length() + 1)); /* cbName */
    947                 pMsg->setNextUInt32(0);                                        /* uFlags */
    948                 pMsg->setNextUInt32(pObj->GetMode());                          /* fMode */
    949                 pMsg->setNextUInt64(pObj->GetSize());                          /* uSize */
    950 
    951                 LogFlowFunc(("Sending file header ...\n"));
    952                 LogRel2(("DnD: Transferring host file '%s' to guest (%RU64 bytes, mode 0x%x)\n",
    953                          pObj->GetPath().c_str(), pObj->GetSize(), pObj->GetMode()));
     973                pMsg->setNextString(pcszDstPath);                    /* pvName */
     974                pMsg->setNextUInt32((uint32_t)(cchDstPath + 1));     /* cbName */
     975                pMsg->setNextUInt32(0);                              /* uFlags */
     976                pMsg->setNextUInt32(fMode);                          /* fMode */
     977                pMsg->setNextUInt64(cbSize);                         /* uSize */
     978
     979                LogRel2(("DnD: Transferring host file '%s' to guest (%zu bytes, mode %#x)\n",
     980                         pcszSrcPath, cbSize, fMode));
    954981
    955982                /** @todo Set progress object title to current file being transferred? */
    956983
    957                 pObjCtx->setState(fState | DND_OBJCTX_STATE_HAS_HDR);
     984                /* Update object state to reflect that we have sent the file header. */
     985                pCtx->mTransfer.mfObjState |= DND_OBJ_STATE_HAS_HDR;
    958986            }
    959987            else
     
    9731001        && fSendData)
    9741002    {
    975         rc = i_sendFileData(pCtx, pObjCtx, pMsg);
     1003        rc = i_sendFileData(pCtx, pObj, pMsg);
    9761004    }
    9771005
    9781006    if (RT_FAILURE(rc))
    979         LogRel(("DnD: Sending host file to guest failed, rc=%Rrc\n", rc));
     1007        LogRel(("DnD: Sending host file '%s' to guest failed, rc=%Rrc\n", pcszSrcPath, rc));
    9801008
    9811009    LogFlowFuncLeaveRC(rc);
     
    9831011}
    9841012
    985 int GuestDnDTarget::i_sendFileData(GuestDnDSendCtx *pCtx, GuestDnDURIObjCtx *pObjCtx, GuestDnDMsg *pMsg)
    986 {
    987     AssertPtrReturn(pCtx,    VERR_INVALID_POINTER);
    988     AssertPtrReturn(pObjCtx, VERR_INVALID_POINTER);
    989     AssertPtrReturn(pMsg,    VERR_INVALID_POINTER);
    990 
    991     DnDURIObject *pObj = pObjCtx->getObj();
    992     AssertPtr(pObj);
    993 
    994     AssertPtr(pCtx->mpResp);
     1013int GuestDnDTarget::i_sendFileData(GuestDnDSendCtx *pCtx,
     1014                                   PDNDTRANSFEROBJECT pObj, GuestDnDMsg *pMsg)
     1015{
     1016    AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
     1017    AssertPtrReturn(pObj, VERR_INVALID_POINTER);
     1018    AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
     1019
     1020    AssertPtrReturn(pCtx->mpResp, VERR_WRONG_ORDER);
    9951021
    9961022    /** @todo Don't allow concurrent reads per context! */
    997 
    998     /*
    999      * Start sending stuff.
    1000      */
    10011023
    10021024    /* Set the message type. */
    10031025    pMsg->setType(HOST_DND_HG_SND_FILE_DATA);
     1026
     1027    const char *pcszSrcPath = DnDTransferObjectGetSourcePath(pObj);
     1028    const char *pcszDstPath = DnDTransferObjectGetDestPath(pObj);
    10041029
    10051030    /* Protocol version 1 sends the file path *every* time with a new file chunk.
     
    10071032    if (mDataBase.m_uProtocolVersion <= 1)
    10081033    {
    1009         pMsg->setNextString(pObj->GetPath().c_str());                  /* pvName */
    1010         pMsg->setNextUInt32((uint32_t)(pObj->GetPath().length() + 1)); /* cbName */
     1034        const size_t cchDstPath = RTStrNLen(pcszDstPath, RTPATH_MAX);
     1035
     1036        pMsg->setNextString(pcszDstPath);              /* pvName */
     1037        pMsg->setNextUInt32((uint32_t)cchDstPath + 1); /* cbName */
    10111038    }
    10121039    else if (mDataBase.m_uProtocolVersion >= 2)
    10131040    {
    1014         pMsg->setNextUInt32(0);                                            /** @todo ContextID not used yet. */
    1015     }
    1016 
    1017     uint32_t cbRead = 0;
    1018 
    1019     int rc = pObj->Read(pCtx->mURI.getBufferMutable(), pCtx->mURI.getBufferSize(), &cbRead);
     1041        pMsg->setNextUInt32(0);                        /** @todo ContextID not used yet. */
     1042    }
     1043
     1044    void *pvBuf  = pCtx->mTransfer.pvScratchBuf;
     1045    AssertPtr(pvBuf);
     1046    size_t cbBuf = pCtx->mTransfer.cbScratchBuf;
     1047    Assert(cbBuf);
     1048
     1049    uint32_t cbRead;
     1050
     1051    int rc = DnDTransferObjectRead(pObj, pvBuf, cbBuf, &cbRead);
    10201052    if (RT_SUCCESS(rc))
    10211053    {
    1022         pCtx->mData.addProcessed(cbRead);
    1023         LogFlowFunc(("cbBufSize=%zu, cbRead=%RU32\n", pCtx->mURI.getBufferSize(), cbRead));
     1054        pCtx->addProcessed(cbRead);
     1055
     1056        LogFlowFunc(("cbBufe=%zu, cbRead=%RU32\n", cbBuf, cbRead));
    10241057
    10251058        if (mDataBase.m_uProtocolVersion <= 1)
    10261059        {
    1027             pMsg->setNextPointer(pCtx->mURI.getBufferMutable(), cbRead);    /* pvData */
     1060            pMsg->setNextPointer(pvBuf, cbRead);                            /* pvData */
    10281061            pMsg->setNextUInt32(cbRead);                                    /* cbData */
    1029             pMsg->setNextUInt32(pObj->GetMode());                           /* fMode */
     1062            pMsg->setNextUInt32(DnDTransferObjectGetMode(pObj));            /* fMode */
    10301063        }
    10311064        else /* Protocol v2 and up. */
    10321065        {
    1033             pMsg->setNextPointer(pCtx->mURI.getBufferMutable(), cbRead);    /* pvData */
     1066            pMsg->setNextPointer(pvBuf, cbRead);                            /* pvData */
    10341067            pMsg->setNextUInt32(cbRead);                                    /* cbData */
    10351068
     
    10421075        }
    10431076
    1044         if (pObj->IsComplete()) /* Done reading? */
    1045         {
    1046             LogRel2(("DnD: Transferring file '%s' to guest complete\n", pObj->GetPath().c_str()));
    1047             LogFlowFunc(("File '%s' complete\n", pObj->GetPath().c_str()));
    1048 
    1049             /* DnDURIObject::Read() returns VINF_EOF when finished reading the entire fire,
    1050              * but we don't want this here -- so just override this with VINF_SUCCESS. */
     1077        if (DnDTransferObjectIsComplete(pObj)) /* Done reading? */
     1078        {
     1079            LogRel2(("DnD: Transferring host file '%s' to guest complete\n", pcszSrcPath));
     1080
     1081            /* DnDTransferObjectRead() returns VINF_EOF when finished reading the entire file,
     1082             * but we don't want this here -- so just set VINF_SUCCESS. */
    10511083            rc = VINF_SUCCESS;
    10521084        }
    10531085    }
    1054 
    1055     if (RT_FAILURE(rc))
    1056         LogRel(("DnD: Reading from host file '%s' failed, rc=%Rrc\n", pObj->GetPath().c_str(), rc));
     1086    else
     1087        LogRel(("DnD: Reading from host file '%s' failed, rc=%Rrc\n", pcszSrcPath, rc));
    10571088
    10581089    LogFlowFuncLeaveRC(rc);
     
    10961127                GuestDnDMsg *pMsg = new GuestDnDMsg();
    10971128
    1098                 rc = pThis->i_sendURIDataLoop(pCtx, pMsg);
     1129                rc = pThis->i_sendTransferDataLoop(pCtx, pMsg);
    10991130                if (rc == VINF_EOF) /* Transfer complete? */
    11001131                {
     
    12721303}
    12731304
    1274 int GuestDnDTarget::i_sendURIData(GuestDnDSendCtx *pCtx, RTMSINTERVAL msTimeout)
     1305/**
     1306 * Main function for sending the actual transfer data (i.e. files + directories) to the guest.
     1307 *
     1308 * @returns VBox status code.
     1309 * @param   pCtx                Send context to use.
     1310 * @param   msTimeout           Timeout (in ms) to use for getting the data sent.
     1311 */
     1312int GuestDnDTarget::i_sendTransferData(GuestDnDSendCtx *pCtx, RTMSINTERVAL msTimeout)
    12751313{
    12761314    AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
     
    12901328    } while (0)
    12911329
    1292     int rc = pCtx->mURI.init(mData.mcbBlockSize);
     1330    int rc = pCtx->mTransfer.init(mData.mcbBlockSize);
    12931331    if (RT_FAILURE(rc))
    12941332        return rc;
     
    13151353    {
    13161354        /*
    1317          * Extract URI list from current meta data.
     1355         * Extract transfer list from current meta data.
    13181356         */
    1319         GuestDnDData    *pData = &pCtx->mData;
    1320         GuestDnDURIData *pURI  = &pCtx->mURI;
    1321 
    1322         rc = pURI->fromLocalMetaData(pData->getMeta());
     1357        rc = DnDTransferListAppendPathsFromBuffer(&pCtx->mTransfer.mList, DNDTRANSFERLISTFMT_NATIVE,
     1358                                                  (const char *)pCtx->Meta.pvData, pCtx->Meta.cbData, "\n", DNDTRANSFERLIST_FLAGS_NONE);
    13231359        if (RT_FAILURE(rc))
    13241360            break;
    13251361
    1326         LogFlowFunc(("URI root objects: %zu, total bytes (raw data to transfer): %zu\n",
    1327                      pURI->getURIList().GetRootCount(), pURI->getURIList().GetTotalBytes()));
    1328 
    13291362        /*
    1330          * Set the new meta data with the URI list in it.
     1363         * Set the extra data size
    13311364         */
    1332         rc = pData->getMeta().fromURIList(pURI->getURIList());
    1333         if (RT_FAILURE(rc))
    1334             break;
    1335 
    1336         /*
    1337          * Set the estimated data sizes we are going to send.
    1338          * The total size also contains the meta data size.
    1339          */
    1340         const uint32_t cbMeta = pData->getMeta().getSize();
    1341         pData->setEstimatedSize(pURI->getURIList().GetTotalBytes() + cbMeta /* cbTotal */,
    1342                                                                      cbMeta /* cbMeta  */);
    1343 
    1344         /*
    1345          * Set the meta format.
    1346          */
    1347         void    *pvFmt = (void *)pCtx->mFmtReq.c_str();
    1348         uint32_t cbFmt = (uint32_t)pCtx->mFmtReq.length() + 1;           /* Include terminating zero. */
    1349 
    1350         pData->setFmt(pvFmt, cbFmt);
     1365        pCtx->cbExtra = DnDTransferListObjTotalBytes(&pCtx->mTransfer.mList);
    13511366
    13521367        /*
    13531368         * The first message always is the data header. The meta data itself then follows
    1354          * and *only* contains the root elements of an URI list.
     1369         * and *only* contains the root elements of a transfer list.
    13551370         *
    13561371         * After the meta data we generate the messages required to send the
     
    13651380         */
    13661381        if (mDataBase.m_uProtocolVersion >= 3)
    1367             rc = i_sendDataHeader(pCtx, pData, &pCtx->mURI);
     1382            rc = i_sendMetaDataHeader(pCtx);
    13681383
    13691384        /*
     
    13711386         */
    13721387        if (RT_SUCCESS(rc))
    1373             rc = i_sendDataBody(pCtx, pData);
     1388            rc = i_sendMetaDataBody(pCtx);
    13741389
    13751390        if (RT_SUCCESS(rc))
     
    14111426            AssertRC(rc2);
    14121427
     1428            LogRel2(("DnD: Sending transfer data to guest cancelled by user\n"));
     1429
    14131430            rc2 = pCtx->mpResp->setProgress(100, DND_PROGRESS_CANCELLED, VINF_SUCCESS);
    14141431            AssertRC(rc2);
     
    14161433        else if (rc != VERR_GSTDND_GUEST_ERROR) /* Guest-side error are already handled in the callback. */
    14171434        {
     1435            LogRel(("DnD: Sending transfer data to guest failed with rc=%Rrc\n", rc));
    14181436            int rc2 = pCtx->mpResp->setProgress(100, DND_PROGRESS_ERROR, rc,
    14191437                                                GuestDnDTarget::i_hostErrorToString(rc));
     
    14281446}
    14291447
    1430 int GuestDnDTarget::i_sendURIDataLoop(GuestDnDSendCtx *pCtx, GuestDnDMsg *pMsg)
     1448int GuestDnDTarget::i_sendTransferDataLoop(GuestDnDSendCtx *pCtx, GuestDnDMsg *pMsg)
    14311449{
    14321450    AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
    14331451    AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
    14341452
    1435     int rc = updateProgress(&pCtx->mData, pCtx->mpResp);
     1453    int rc = updateProgress(pCtx, pCtx->mpResp);
    14361454    AssertRC(rc);
    14371455
    1438     if (   pCtx->mData.isComplete()
    1439         && pCtx->mURI.isComplete())
     1456    if (   pCtx->isComplete()
     1457        && pCtx->mTransfer.isComplete())
    14401458    {
    14411459        return VINF_EOF;
    14421460    }
    14431461
    1444     GuestDnDURIObjCtx &objCtx = pCtx->mURI.getObjCurrent();
    1445     if (!objCtx.isValid())
     1462    PDNDTRANSFEROBJECT pObj = DnDTransferListObjGetFirst(&pCtx->mTransfer.mList);
     1463    if (!pObj)
    14461464        return VERR_WRONG_ORDER;
    14471465
    1448     DnDURIObject *pCurObj = objCtx.getObj();
    1449     AssertPtr(pCurObj);
    1450 
    1451     const DnDURIObject::Type enmType = pCurObj->GetType();
    1452 
    1453     LogRel3(("DnD: Processing: srcPath=%s, dstPath=%s, enmType=%RU32, cbSize=%RU32\n",
    1454              pCurObj->GetPath().c_str(), pCurObj->GetPath().c_str(),
    1455              enmType, pCurObj->GetSize()));
    1456 
    1457     if (enmType == DnDURIObject::Type_Directory)
    1458     {
    1459         rc = i_sendDirectory(pCtx, &objCtx, pMsg);
    1460     }
    1461     else if (DnDURIObject::Type_File)
    1462     {
    1463         rc = i_sendFile(pCtx, &objCtx, pMsg);
    1464     }
    1465     else
    1466     {
    1467         AssertMsgFailed(("enmType=%RU32 is not supported for srcPath=%s, dstPath=%s\n",
    1468                          enmType, pCurObj->GetPath().c_str(), pCurObj->GetPath().c_str()));
    1469         rc = VERR_NOT_SUPPORTED;
    1470     }
    1471 
    1472     bool fRemove = false; /* Remove current entry? */
    1473     if (   pCurObj->IsComplete()
     1466    switch (DnDTransferObjectGetType(pObj))
     1467    {
     1468        case DNDTRANSFEROBJTYPE_DIRECTORY:
     1469            rc = i_sendDirectory(pCtx, pObj, pMsg);
     1470            break;
     1471
     1472        case DNDTRANSFEROBJTYPE_FILE:
     1473            rc = i_sendFile(pCtx, pObj, pMsg);
     1474            break;
     1475
     1476        default:
     1477            AssertFailedStmt(rc = VERR_NOT_SUPPORTED);
     1478            break;
     1479    }
     1480
     1481    if (   DnDTransferObjectIsComplete(pObj)
    14741482        || RT_FAILURE(rc))
    1475     {
    1476         fRemove = true;
    1477     }
    1478 
    1479     if (fRemove)
    1480     {
    1481         LogFlowFunc(("Removing \"%s\" from list, rc=%Rrc\n", pCurObj->GetPath().c_str(), rc));
    1482         pCtx->mURI.removeObjCurrent();
    1483     }
     1483        DnDTransferListObjRemoveFirst(&pCtx->mTransfer.mList);
    14841484
    14851485    LogFlowFuncLeaveRC(rc);
     
    14871487}
    14881488
     1489/**
     1490 * Main function for sending raw data (e.g. text, RTF, ...) to the guest.
     1491 *
     1492 * @returns VBox status code.
     1493 * @param   pCtx                Send context to use.
     1494 * @param   msTimeout           Timeout (in ms) to use for getting the data sent.
     1495 */
    14891496int GuestDnDTarget::i_sendRawData(GuestDnDSendCtx *pCtx, RTMSINTERVAL msTimeout)
    14901497{
    14911498    AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
    14921499    NOREF(msTimeout);
    1493 
    1494     GuestDnDData *pData = &pCtx->mData;
    14951500
    14961501    /** @todo At the moment we only allow sending up to 64K raw data.
    14971502     *        For protocol v1+v2: Fix this by using HOST_DND_HG_SND_MORE_DATA.
    14981503     *        For protocol v3   : Send another HOST_DND_HG_SND_DATA message. */
    1499     if (!pData->getMeta().getSize())
     1504    if (!pCtx->Meta.cbData)
    15001505        return VINF_SUCCESS;
    15011506
    1502     int rc = VINF_SUCCESS;
    1503 
    1504     /*
    1505      * Send the data header first.
    1506      */
    1507     if (mDataBase.m_uProtocolVersion >= 3)
    1508         rc = i_sendDataHeader(pCtx, pData, NULL /* URI list */);
    1509 
    1510     /*
    1511      * Send the (meta) data body.
    1512      */
     1507    int rc = i_sendMetaDataHeader(pCtx);
    15131508    if (RT_SUCCESS(rc))
    1514         rc = i_sendDataBody(pCtx, pData);
     1509        rc = i_sendMetaDataBody(pCtx);
    15151510
    15161511    int rc2;
    15171512    if (RT_FAILURE(rc))
    1518         rc2 = pCtx->mpResp->setProgress(100, DND_PROGRESS_ERROR, rc,
     1513    {
     1514        LogRel(("DnD: Sending raw data to guest failed with rc=%Rrc\n", rc));
     1515        rc2 = pCtx->mpResp->setProgress(100 /* Percent */, DND_PROGRESS_ERROR, rc,
    15191516                                        GuestDnDTarget::i_hostErrorToString(rc));
     1517    }
    15201518    else
    1521         rc2 = pCtx->mpResp->setProgress(100, DND_PROGRESS_COMPLETE, rc);
     1519        rc2 = pCtx->mpResp->setProgress(100 /* Percent */, DND_PROGRESS_COMPLETE, rc);
    15221520    AssertRC(rc2);
    15231521
     
    15261524}
    15271525
     1526/**
     1527 * Cancels sending DnD data.
     1528 *
     1529 * @returns VBox status code.
     1530 * @param   aVeto               Whether cancelling was vetoed or not.
     1531 *                              Not implemented yet.
     1532 */
    15281533HRESULT GuestDnDTarget::cancel(BOOL *aVeto)
    15291534{
     
    15321537#else /* VBOX_WITH_DRAG_AND_DROP */
    15331538
     1539    LogRel2(("DnD: Sending cancelling request to the guest ...\n"));
     1540
    15341541    int rc = GuestDnDBase::sendCancel();
    15351542
    15361543    if (aVeto)
    1537         *aVeto = FALSE; /** @todo */
     1544        *aVeto = FALSE; /** @todo Impplement vetoing. */
    15381545
    15391546    HRESULT hr = RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
Note: See TracChangeset for help on using the changeset viewer.

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