VirtualBox

Changeset 85371 in vbox for trunk/src/VBox/GuestHost


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/GuestHost/DragAndDrop
Files:
4 edited
2 moved

Legend:

Unmodified
Added
Removed
  • 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#
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