VirtualBox

source: vbox/trunk/src/VBox/GuestHost/DragAndDrop/DnDTransferList.cpp@ 101359

Last change on this file since 101359 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.8 KB
Line 
1/* $Id: DnDTransferList.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * DnD - transfer list implemenation.
4 */
5
6/*
7 * Copyright (C) 2014-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/**
29 * This implementation is taylored to keeping track of a single DnD transfer by maintaining two separate entities,
30 * namely a list of root entries and a list of (recursive file system) transfer ojects to actually transfer.
31 *
32 * The list of root entries is sent to the target (guest/host) beforehand so that the OS has a data for the
33 * actual drag'n drop operation to work with. This also contains required header data like total number of
34 * objects or total bytes being received.
35 *
36 * The list of transfer objects only is needed in order to sending data from the source to the target.
37 * Currently there is no particular ordering implemented for the transfer object list; it depends on IPRT's RTDirRead().
38 *
39 * The target must not know anything about the actual (absolute) path the root entries are coming from
40 * due to security reasons. Those root entries then can be re-based on the target to desired location there.
41 *
42 * All data handling internally is done in the so-called "transport" format, that is, non-URI (regular) paths
43 * with the "/" as path separator. From/to URI conversion is provided for convenience only.
44 */
45
46
47/*********************************************************************************************************************************
48* Header Files *
49*********************************************************************************************************************************/
50#define LOG_GROUP LOG_GROUP_GUEST_DND
51#include <VBox/GuestHost/DragAndDrop.h>
52
53#include <iprt/dir.h>
54#include <iprt/err.h>
55#include <iprt/file.h>
56#include <iprt/fs.h>
57#include <iprt/mem.h>
58#include <iprt/path.h>
59#include <iprt/string.h>
60#include <iprt/symlink.h>
61#include <iprt/uri.h>
62
63#include <VBox/log.h>
64
65
66/*********************************************************************************************************************************
67* Prototypes *
68*********************************************************************************************************************************/
69static int dndTransferListSetRootPath(PDNDTRANSFERLIST pList, const char *pcszRootPathAbs);
70
71static int dndTransferListRootEntryAdd(PDNDTRANSFERLIST pList, const char *pcszRoot);
72static void dndTransferListRootEntryFree(PDNDTRANSFERLIST pList, PDNDTRANSFERLISTROOT pRootObj);
73
74static int dndTransferListObjAdd(PDNDTRANSFERLIST pList, const char *pcszSrcAbs, RTFMODE fMode, DNDTRANSFERLISTFLAGS fFlags);
75static void dndTransferListObjFree(PDNDTRANSFERLIST pList, PDNDTRANSFEROBJECT pLstObj);
76
77
78/** The size of the directory entry buffer we're using. */
79#define DNDTRANSFERLIST_DIRENTRY_BUF_SIZE (sizeof(RTDIRENTRYEX) + RTPATH_MAX)
80
81
82/**
83 * Initializes a transfer list, internal version.
84 *
85 * @returns VBox status code.
86 * @param pList Transfer list to initialize.
87 * @param pcszRootPathAbs Absolute root path to use for this list. Optional and can be NULL.
88 */
89static int dndTransferListInitInternal(PDNDTRANSFERLIST pList, const char *pcszRootPathAbs)
90{
91 AssertPtrReturn(pList, VERR_INVALID_POINTER);
92 /* pcszRootPathAbs is optional. */
93
94 if (pList->pszPathRootAbs) /* Already initialized? */
95 return VERR_WRONG_ORDER;
96
97 pList->pszPathRootAbs = NULL;
98
99 RTListInit(&pList->lstRoot);
100 pList->cRoots = 0;
101
102 RTListInit(&pList->lstObj);
103 pList->cObj = 0;
104 pList->cbObjTotal = 0;
105
106 if (pcszRootPathAbs)
107 return dndTransferListSetRootPath(pList, pcszRootPathAbs);
108
109 return VINF_SUCCESS;
110}
111
112/**
113 * Initializes a transfer list, extended version.
114 *
115 * @returns VBox status code.
116 * @param pList Transfer list to initialize.
117 * @param pcszRootPathAbs Absolute root path to use for this list.
118 * @param enmFmt Format of \a pcszRootPathAbs.
119 */
120int DnDTransferListInitEx(PDNDTRANSFERLIST pList, const char *pcszRootPathAbs, DNDTRANSFERLISTFMT enmFmt)
121{
122 AssertPtrReturn(pList, VERR_INVALID_POINTER);
123 AssertPtrReturn(pcszRootPathAbs, VERR_INVALID_POINTER);
124 AssertReturn(*pcszRootPathAbs, VERR_INVALID_PARAMETER);
125
126 int rc;
127
128 if (enmFmt == DNDTRANSFERLISTFMT_URI)
129 {
130 char *pszPath;
131 rc = RTUriFilePathEx(pcszRootPathAbs, RTPATH_STR_F_STYLE_UNIX, &pszPath, 0 /*cbPath*/, NULL /*pcchPath*/);
132 if (RT_SUCCESS(rc))
133 {
134 rc = dndTransferListInitInternal(pList, pszPath);
135 RTStrFree(pszPath);
136 }
137 }
138 else
139 rc = dndTransferListInitInternal(pList, pcszRootPathAbs);
140
141 return rc;
142}
143
144/**
145 * Initializes a transfer list.
146 *
147 * @returns VBox status code.
148 * @param pList Transfer list to initialize.
149 */
150int DnDTransferListInit(PDNDTRANSFERLIST pList)
151{
152 return dndTransferListInitInternal(pList, NULL /* pcszRootPathAbs */);
153}
154
155/**
156 * Destroys a transfer list.
157 *
158 * @param pList Transfer list to destroy.
159 */
160void DnDTransferListDestroy(PDNDTRANSFERLIST pList)
161{
162 if (!pList)
163 return;
164
165 DnDTransferListReset(pList);
166
167 RTStrFree(pList->pszPathRootAbs);
168 pList->pszPathRootAbs = NULL;
169}
170
171/**
172 * Initializes a transfer list and sets the root path.
173 *
174 * Convenience function which calls dndTransferListInitInternal() if not initialized already.
175 *
176 * @returns VBox status code.
177 * @param pList List to determine root path for.
178 * @param pcszRootPathAbs Root path to use.
179 */
180static int dndTransferInitAndSetRoot(PDNDTRANSFERLIST pList, const char *pcszRootPathAbs)
181{
182 int rc;
183
184 if (!pList->pszPathRootAbs)
185 {
186 rc = dndTransferListInitInternal(pList, pcszRootPathAbs);
187 AssertRCReturn(rc, rc);
188
189 LogRel2(("DnD: Determined root path is '%s'\n", pList->pszPathRootAbs));
190 }
191 else
192 rc = VINF_SUCCESS;
193
194 return rc;
195}
196
197/**
198 * Resets a transfer list to its initial state.
199 *
200 * @param pList Transfer list to reset.
201 */
202void DnDTransferListReset(PDNDTRANSFERLIST pList)
203{
204 AssertPtrReturnVoid(pList);
205
206 if (!pList->pszPathRootAbs)
207 return;
208
209 RTStrFree(pList->pszPathRootAbs);
210 pList->pszPathRootAbs = NULL;
211
212 PDNDTRANSFERLISTROOT pRootCur, pRootNext;
213 RTListForEachSafe(&pList->lstRoot, pRootCur, pRootNext, DNDTRANSFERLISTROOT, Node)
214 dndTransferListRootEntryFree(pList, pRootCur);
215 Assert(RTListIsEmpty(&pList->lstRoot));
216
217 PDNDTRANSFEROBJECT pObjCur, pObjNext;
218 RTListForEachSafe(&pList->lstObj, pObjCur, pObjNext, DNDTRANSFEROBJECT, Node)
219 dndTransferListObjFree(pList, pObjCur);
220 Assert(RTListIsEmpty(&pList->lstObj));
221
222 Assert(pList->cRoots == 0);
223 Assert(pList->cObj == 0);
224
225 pList->cbObjTotal = 0;
226}
227
228/**
229 * Adds a single transfer object entry to a transfer List.
230 *
231 * @returns VBox status code.
232 * @param pList Transfer list to add entry to.
233 * @param pcszSrcAbs Absolute source path (local) to use.
234 * @param fMode File mode of entry to add.
235 * @param fFlags Transfer list flags to use for appending.
236 */
237static int dndTransferListObjAdd(PDNDTRANSFERLIST pList, const char *pcszSrcAbs, RTFMODE fMode, DNDTRANSFERLISTFLAGS fFlags)
238{
239 AssertPtrReturn(pList, VERR_INVALID_POINTER);
240 AssertPtrReturn(pcszSrcAbs, VERR_INVALID_POINTER);
241
242 LogFlowFunc(("pcszSrcAbs=%s, fMode=%#x, fFlags=0x%x\n", pcszSrcAbs, fMode, fFlags));
243
244 int rc = VINF_SUCCESS;
245
246 if ( !RTFS_IS_FILE(fMode)
247 && !RTFS_IS_DIRECTORY(fMode))
248 /** @todo Symlinks not allowed. */
249 {
250 rc = VERR_NOT_SUPPORTED;
251 }
252
253 if (RT_SUCCESS(rc))
254 {
255 /* Calculate the path to add as the destination path to our URI object. */
256 const size_t idxPathToAdd = strlen(pList->pszPathRootAbs);
257 AssertReturn(strlen(pcszSrcAbs) > idxPathToAdd, VERR_INVALID_PARAMETER); /* Should never happen (tm). */
258
259 PDNDTRANSFEROBJECT pObj = (PDNDTRANSFEROBJECT)RTMemAllocZ(sizeof(DNDTRANSFEROBJECT));
260 if (pObj)
261 {
262 const bool fIsFile = RTFS_IS_FILE(fMode);
263
264 rc = DnDTransferObjectInitEx(pObj, fIsFile ? DNDTRANSFEROBJTYPE_FILE : DNDTRANSFEROBJTYPE_DIRECTORY,
265 pList->pszPathRootAbs, &pcszSrcAbs[idxPathToAdd]);
266 if (RT_SUCCESS(rc))
267 {
268 if (fIsFile)
269 rc = DnDTransferObjectOpen(pObj,
270 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE, /** @todo Add a standard fOpen mode for this list. */
271 0 /* fMode */, DNDTRANSFEROBJECT_FLAGS_NONE);
272 if (RT_SUCCESS(rc))
273 {
274 RTListAppend(&pList->lstObj, &pObj->Node);
275
276 pList->cObj++;
277 if (fIsFile)
278 pList->cbObjTotal += DnDTransferObjectGetSize(pObj);
279
280 if ( fIsFile
281 && !(fFlags & DNDTRANSFERLIST_FLAGS_KEEP_OPEN)) /* Shall we keep the file open while being added to this list? */
282 rc = DnDTransferObjectClose(pObj);
283 }
284
285 if (RT_FAILURE(rc))
286 DnDTransferObjectDestroy(pObj);
287 }
288
289 if (RT_FAILURE(rc))
290 RTMemFree(pObj);
291 }
292 else
293 rc = VERR_NO_MEMORY;
294 }
295
296 if (RT_FAILURE(rc))
297 LogRel(("DnD: Adding entry '%s' of type %#x failed with rc=%Rrc\n", pcszSrcAbs, fMode & RTFS_TYPE_MASK, rc));
298
299 LogFlowFuncLeaveRC(rc);
300 return rc;
301}
302
303/**
304 * Frees an internal DnD transfer list object.
305 *
306 * @param pList Transfer list to free object for.
307 * @param pLstObj transfer list object to free. The pointer will be invalid after calling.
308 */
309static void dndTransferListObjFree(PDNDTRANSFERLIST pList, PDNDTRANSFEROBJECT pObj)
310{
311 if (!pObj)
312 return;
313
314 DnDTransferObjectDestroy(pObj);
315 RTListNodeRemove(&pObj->Node);
316 RTMemFree(pObj);
317
318 AssertReturnVoid(pList->cObj);
319 pList->cObj--;
320}
321
322/**
323 * Helper routine for handling adding sub directories.
324 *
325 * @return IPRT status code.
326 * @param pList transfer list to add found entries to.
327 * @param pszDir Pointer to the directory buffer.
328 * @param cchDir The length of pszDir in pszDir.
329 * @param pDirEntry Pointer to the directory entry.
330 * @param fFlags Flags of type DNDTRANSFERLISTFLAGS.
331 */
332static int dndTransferListAppendPathRecursiveSub(PDNDTRANSFERLIST pList,
333 char *pszDir, size_t cchDir, PRTDIRENTRYEX pDirEntry,
334 DNDTRANSFERLISTFLAGS fFlags)
335
336{
337 Assert(cchDir > 0); Assert(pszDir[cchDir] == '\0');
338
339 /* Make sure we've got some room in the path, to save us extra work further down. */
340 if (cchDir + 3 >= RTPATH_MAX)
341 return VERR_BUFFER_OVERFLOW;
342
343 /* Open directory. */
344 RTDIR hDir;
345 int rc = RTDirOpen(&hDir, pszDir);
346 if (RT_FAILURE(rc))
347 return rc;
348
349 /* Ensure we've got a trailing slash (there is space for it see above). */
350 if (!RTPATH_IS_SEP(pszDir[cchDir - 1]))
351 {
352 pszDir[cchDir++] = RTPATH_SLASH;
353 pszDir[cchDir] = '\0';
354 }
355
356 rc = dndTransferListObjAdd(pList, pszDir, pDirEntry->Info.Attr.fMode, fFlags);
357 AssertRCReturn(rc, rc);
358
359 LogFlowFunc(("pszDir=%s\n", pszDir));
360
361 /*
362 * Process the files and subdirs.
363 */
364 for (;;)
365 {
366 /* Get the next directory. */
367 size_t cbDirEntry = DNDTRANSFERLIST_DIRENTRY_BUF_SIZE;
368 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
369 if (RT_FAILURE(rc))
370 break;
371
372 /* Check length. */
373 if (pDirEntry->cbName + cchDir + 3 >= RTPATH_MAX)
374 {
375 rc = VERR_BUFFER_OVERFLOW;
376 break;
377 }
378
379 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
380 {
381 case RTFS_TYPE_SYMLINK:
382 {
383 if (!(fFlags & DNDTRANSFERLIST_FLAGS_RESOLVE_SYMLINKS))
384 break;
385 RT_FALL_THRU();
386 }
387 case RTFS_TYPE_DIRECTORY:
388 {
389 if (RTDirEntryExIsStdDotLink(pDirEntry))
390 continue;
391
392 memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);
393 int rc2 = dndTransferListAppendPathRecursiveSub(pList, pszDir, cchDir + pDirEntry->cbName, pDirEntry, fFlags);
394 if (RT_SUCCESS(rc))
395 rc = rc2;
396 break;
397 }
398
399 case RTFS_TYPE_FILE:
400 {
401 memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);
402 rc = dndTransferListObjAdd(pList, pszDir, pDirEntry->Info.Attr.fMode, fFlags);
403 break;
404 }
405
406 default:
407 {
408
409 break;
410 }
411 }
412 }
413
414 if (rc == VERR_NO_MORE_FILES) /* Done reading current directory? */
415 {
416 rc = VINF_SUCCESS;
417 }
418 else if (RT_FAILURE(rc))
419 LogRel(("DnD: Error while adding files recursively, rc=%Rrc\n", rc));
420
421 int rc2 = RTDirClose(hDir);
422 if (RT_FAILURE(rc2))
423 {
424 if (RT_SUCCESS(rc))
425 rc = rc2;
426 }
427
428 return rc;
429}
430
431/**
432 * Appends a native system path recursively by adding these entries as transfer objects.
433 *
434 * @returns VBox status code.
435 * @param pList Transfer list to add found entries to.
436 * @param pcszPathAbs Absolute path to add.
437 * @param fFlags Flags of type DNDTRANSFERLISTFLAGS.
438 */
439static int dndTransferListAppendDirectoryRecursive(PDNDTRANSFERLIST pList,
440 const char *pcszPathAbs, DNDTRANSFERLISTFLAGS fFlags)
441{
442 char szPathAbs[RTPATH_MAX];
443 int rc = RTStrCopy(szPathAbs, sizeof(szPathAbs), pcszPathAbs);
444 if (RT_FAILURE(rc))
445 return rc;
446
447 union
448 {
449 uint8_t abPadding[DNDTRANSFERLIST_DIRENTRY_BUF_SIZE];
450 RTDIRENTRYEX DirEntry;
451 } uBuf;
452
453 const size_t cchPathAbs = RTStrNLen(szPathAbs, RTPATH_MAX);
454 AssertReturn(cchPathAbs, VERR_BUFFER_OVERFLOW);
455
456 /* Use the directory entry to hand-in the directorie's information. */
457 rc = RTPathQueryInfo(pcszPathAbs, &uBuf.DirEntry.Info, RTFSOBJATTRADD_NOTHING);
458 AssertRCReturn(rc, rc);
459
460 return dndTransferListAppendPathRecursiveSub(pList, szPathAbs, cchPathAbs, &uBuf.DirEntry, fFlags);
461}
462
463/**
464 * Helper function for appending a local directory to a DnD transfer list.
465 *
466 * @returns VBox status code.
467 * @param pList Transfer list to return total number of root entries for.
468 * @param pszPathAbs Absolute path of directory to append.
469 * @param cbPathAbs Size (in bytes) of absolute path of directory to append.
470 * @param pObjInfo Pointer to directory object info to append.
471 * @param fFlags Transfer list flags to use for appending.
472 */
473static int dndTransferListAppendDirectory(PDNDTRANSFERLIST pList, char* pszPathAbs, size_t cbPathAbs,
474 PRTFSOBJINFO pObjInfo, DNDTRANSFERLISTFLAGS fFlags)
475{
476 const size_t cchPathRoot = RTStrNLen(pList->pszPathRootAbs, RTPATH_MAX);
477 AssertReturn(cchPathRoot, VERR_INVALID_PARAMETER);
478
479 const size_t cchPathAbs = RTPathEnsureTrailingSeparator(pszPathAbs, sizeof(cbPathAbs));
480 AssertReturn(cchPathAbs, VERR_BUFFER_OVERFLOW);
481 AssertReturn(cchPathAbs >= cchPathRoot, VERR_BUFFER_UNDERFLOW);
482
483 const bool fPathIsRoot = cchPathAbs == cchPathRoot;
484
485 int rc;
486
487 if (!fPathIsRoot)
488 {
489 rc = dndTransferListObjAdd(pList, pszPathAbs, pObjInfo->Attr.fMode, fFlags);
490 AssertRCReturn(rc, rc);
491 }
492
493 RTDIR hDir;
494 rc = RTDirOpen(&hDir, pszPathAbs);
495 AssertRCReturn(rc, rc);
496
497 for (;;)
498 {
499 /* Get the next entry. */
500 RTDIRENTRYEX dirEntry;
501 rc = RTDirReadEx(hDir, &dirEntry, NULL, RTFSOBJATTRADD_UNIX,
502 RTPATH_F_ON_LINK /** @todo No symlinks yet. */);
503 if (RT_SUCCESS(rc))
504 {
505 if (RTDirEntryExIsStdDotLink(&dirEntry))
506 continue;
507
508 /* Check length. */
509 if (dirEntry.cbName + cchPathAbs + 3 >= cbPathAbs)
510 {
511 rc = VERR_BUFFER_OVERFLOW;
512 break;
513 }
514
515 /* Append the directory entry to our absolute path. */
516 memcpy(&pszPathAbs[cchPathAbs], dirEntry.szName, dirEntry.cbName + 1 /* Include terminator */);
517
518 LogFlowFunc(("szName=%s, pszPathAbs=%s\n", dirEntry.szName, pszPathAbs));
519
520 switch (dirEntry.Info.Attr.fMode & RTFS_TYPE_MASK)
521 {
522 case RTFS_TYPE_DIRECTORY:
523 {
524 if (fFlags & DNDTRANSFERLIST_FLAGS_RECURSIVE)
525 rc = dndTransferListAppendDirectoryRecursive(pList, pszPathAbs, fFlags);
526 break;
527 }
528
529 case RTFS_TYPE_FILE:
530 {
531 rc = dndTransferListObjAdd(pList, pszPathAbs, dirEntry.Info.Attr.fMode, fFlags);
532 break;
533 }
534
535 default:
536 /* Silently skip everything else. */
537 break;
538 }
539
540 if ( RT_SUCCESS(rc)
541 /* Make sure to add a root entry if we're processing the root path at the moment. */
542 && fPathIsRoot)
543 {
544 rc = dndTransferListRootEntryAdd(pList, pszPathAbs);
545 }
546 }
547 else if (rc == VERR_NO_MORE_FILES)
548 {
549 rc = VINF_SUCCESS;
550 break;
551 }
552 else
553 break;
554 }
555
556 RTDirClose(hDir);
557 return rc;
558}
559
560/**
561 * Appends a native path to a DnD transfer list.
562 *
563 * @returns VBox status code.
564 * @param pList Transfer list to append native path to.
565 * @param pcszPath Path (native) to append.
566 * @param fFlags Transfer list flags to use for appending.
567 */
568static int dndTransferListAppendPathNative(PDNDTRANSFERLIST pList, const char *pcszPath, DNDTRANSFERLISTFLAGS fFlags)
569{
570 /* We don't want to have a relative directory here. */
571 if (!RTPathStartsWithRoot(pcszPath))
572 return VERR_INVALID_PARAMETER;
573
574 int rc = DnDPathValidate(pcszPath, false /* fMustExist */);
575 AssertRCReturn(rc, rc);
576
577 char szPathAbs[RTPATH_MAX];
578 size_t cbPathAbs = sizeof(szPathAbs);
579 rc = RTStrCopy(szPathAbs, cbPathAbs, pcszPath);
580 AssertRCReturn(rc, rc);
581
582 size_t cchPathAbs = RTStrNLen(szPathAbs, cbPathAbs);
583 AssertReturn(cchPathAbs, VERR_INVALID_PARAMETER);
584
585 /* Convert path to transport style. */
586 rc = DnDPathConvert(szPathAbs, cbPathAbs, DNDPATHCONVERT_FLAGS_TRANSPORT);
587 if (RT_SUCCESS(rc))
588 {
589 /* Make sure the path has the same root path as our list. */
590 if (RTPathStartsWith(szPathAbs, pList->pszPathRootAbs))
591 {
592 RTFSOBJINFO objInfo;
593 rc = RTPathQueryInfo(szPathAbs, &objInfo, RTFSOBJATTRADD_NOTHING);
594 if (RT_SUCCESS(rc))
595 {
596 const uint32_t fType = objInfo.Attr.fMode & RTFS_TYPE_MASK;
597
598 if ( RTFS_IS_DIRECTORY(fType)
599 || RTFS_IS_FILE(fType))
600 {
601 if (RTFS_IS_DIRECTORY(fType))
602 {
603 cchPathAbs = RTPathEnsureTrailingSeparator(szPathAbs, cbPathAbs);
604 AssertReturn(cchPathAbs, VERR_BUFFER_OVERFLOW);
605 }
606
607 const size_t cchPathRoot = RTStrNLen(pList->pszPathRootAbs, RTPATH_MAX);
608 AssertStmt(cchPathRoot, rc = VERR_INVALID_PARAMETER);
609
610 if ( RT_SUCCESS(rc)
611 && cchPathAbs > cchPathRoot)
612 rc = dndTransferListRootEntryAdd(pList, szPathAbs);
613 }
614 else
615 rc = VERR_NOT_SUPPORTED;
616
617 if (RT_SUCCESS(rc))
618 {
619 switch (fType)
620 {
621 case RTFS_TYPE_DIRECTORY:
622 {
623 rc = dndTransferListAppendDirectory(pList, szPathAbs, cbPathAbs, &objInfo, fFlags);
624 break;
625 }
626
627 case RTFS_TYPE_FILE:
628 {
629 rc = dndTransferListObjAdd(pList, szPathAbs, objInfo.Attr.fMode, fFlags);
630 break;
631 }
632
633 default:
634 AssertFailed();
635 break;
636 }
637 }
638 }
639 /* On UNIX-y OSes RTPathQueryInfo() returns VERR_FILE_NOT_FOUND in some cases
640 * so tweak this to make it uniform to Windows. */
641 else if (rc == VERR_FILE_NOT_FOUND)
642 rc = VERR_PATH_NOT_FOUND;
643 }
644 else
645 rc = VERR_INVALID_PARAMETER;
646 }
647
648 if (RT_FAILURE(rc))
649 LogRel(("DnD: Adding native path '%s' failed with rc=%Rrc\n", pcszPath, rc));
650
651 return rc;
652}
653
654/**
655 * Appends an URI path to a DnD transfer list.
656 *
657 * @returns VBox status code.
658 * @param pList Transfer list to append native path to.
659 * @param pcszPath URI path to append.
660 * @param fFlags Transfer list flags to use for appending.
661 */
662static int dndTransferListAppendPathURI(PDNDTRANSFERLIST pList, const char *pcszPath, DNDTRANSFERLISTFLAGS fFlags)
663{
664 RT_NOREF(fFlags);
665
666 /* Query the path component of a file URI. If this hasn't a
667 * file scheme, NULL is returned. */
668 char *pszPath;
669 int rc = RTUriFilePathEx(pcszPath, RTPATH_STR_F_STYLE_UNIX, &pszPath, 0 /*cbPath*/, NULL /*pcchPath*/);
670 if (RT_SUCCESS(rc))
671 {
672 rc = dndTransferListAppendPathNative(pList, pszPath, fFlags);
673 RTStrFree(pszPath);
674 }
675
676 if (RT_FAILURE(rc))
677 LogRel(("DnD: Adding URI path '%s' failed with rc=%Rrc\n", pcszPath, rc));
678
679 return rc;
680}
681
682/**
683 * Appends a single path to a transfer list.
684 *
685 * @returns VBox status code. VERR_NOT_SUPPORTED if the path is not supported.
686 * @param pList Transfer list to append to.
687 * @param enmFmt Format of \a pszPaths to append.
688 * @param pcszPath Path to append. Must be part of the list's set root path.
689 * @param fFlags Transfer list flags to use for appending.
690 */
691int DnDTransferListAppendPath(PDNDTRANSFERLIST pList,
692 DNDTRANSFERLISTFMT enmFmt, const char *pcszPath, DNDTRANSFERLISTFLAGS fFlags)
693{
694 AssertPtrReturn(pList, VERR_INVALID_POINTER);
695 AssertPtrReturn(pcszPath, VERR_INVALID_POINTER);
696 AssertReturn(!(fFlags & ~DNDTRANSFERLIST_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
697 AssertReturn(!(fFlags & DNDTRANSFERLIST_FLAGS_RESOLVE_SYMLINKS), VERR_NOT_SUPPORTED);
698
699 int rc;
700
701 switch (enmFmt)
702 {
703 case DNDTRANSFERLISTFMT_NATIVE:
704 rc = dndTransferListAppendPathNative(pList, pcszPath, fFlags);
705 break;
706
707 case DNDTRANSFERLISTFMT_URI:
708 rc = dndTransferListAppendPathURI(pList, pcszPath, fFlags);
709 break;
710
711 default:
712 AssertFailedStmt(rc = VERR_NOT_SUPPORTED);
713 break; /* Never reached */
714 }
715
716 return rc;
717}
718
719/**
720 * Appends native paths to a transfer list.
721 *
722 * @returns VBox status code.
723 * @param pList Transfer list to append to.
724 * @param enmFmt Format of \a pszPaths to append.
725 * @param pszPaths Buffer of paths to append.
726 * @param cbPaths Size (in bytes) of buffer of paths to append.
727 * @param pcszSeparator Separator string to use.
728 * @param fFlags Transfer list flags to use for appending.
729 */
730int DnDTransferListAppendPathsFromBuffer(PDNDTRANSFERLIST pList,
731 DNDTRANSFERLISTFMT enmFmt, const char *pszPaths, size_t cbPaths,
732 const char *pcszSeparator, DNDTRANSFERLISTFLAGS fFlags)
733{
734 AssertPtrReturn(pList, VERR_INVALID_POINTER);
735 AssertPtrReturn(pszPaths, VERR_INVALID_POINTER);
736 AssertReturn(cbPaths, VERR_INVALID_PARAMETER);
737
738 char **papszPaths = NULL;
739 size_t cPaths = 0;
740 int rc = RTStrSplit(pszPaths, cbPaths, pcszSeparator, &papszPaths, &cPaths);
741 if (RT_SUCCESS(rc))
742 rc = DnDTransferListAppendPathsFromArray(pList, enmFmt, papszPaths, cPaths, fFlags);
743
744 for (size_t i = 0; i < cPaths; ++i)
745 RTStrFree(papszPaths[i]);
746 RTMemFree(papszPaths);
747
748 return rc;
749}
750
751/**
752 * Appends paths to a transfer list.
753 *
754 * @returns VBox status code. Will return VERR_INVALID_PARAMETER if a common root path could not be found.
755 * @param pList Transfer list to append path to.
756 * @param enmFmt Format of \a papcszPaths to append.
757 * @param papcszPaths Array of paths to append.
758 * @param cPaths Number of paths in \a papcszPaths to append.
759 * @param fFlags Transfer list flags to use for appending.
760 */
761int DnDTransferListAppendPathsFromArray(PDNDTRANSFERLIST pList,
762 DNDTRANSFERLISTFMT enmFmt,
763 const char * const *papcszPaths, size_t cPaths, DNDTRANSFERLISTFLAGS fFlags)
764{
765 AssertPtrReturn(pList, VERR_INVALID_POINTER);
766 AssertPtrReturn(papcszPaths, VERR_INVALID_POINTER);
767 AssertReturn(!(fFlags & ~DNDTRANSFERLIST_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
768
769 int rc = VINF_SUCCESS;
770
771 if (!cPaths) /* Nothing to add? Bail out. */
772 return VINF_SUCCESS;
773
774 char **papszPathsTmp = NULL;
775
776 /* If URI data is being handed in, extract the paths first. */
777 if (enmFmt == DNDTRANSFERLISTFMT_URI)
778 {
779 papszPathsTmp = (char **)RTMemAlloc(sizeof(char *) * cPaths);
780 if (papszPathsTmp)
781 {
782 for (size_t i = 0; i < cPaths; i++)
783 papszPathsTmp[i] = RTUriFilePath(papcszPaths[i]);
784 }
785 else
786 rc = VERR_NO_MEMORY;
787 }
788
789 if (RT_FAILURE(rc))
790 return rc;
791
792 /* If we don't have a root path set, try to find the common path of all handed-in paths. */
793 if (!pList->pszPathRootAbs)
794 {
795 /* Can we work on the unmodified, handed-in data or do we need to use our temporary paths? */
796 const char * const *papszPathTmp = enmFmt == DNDTRANSFERLISTFMT_NATIVE
797 ? papcszPaths : papszPathsTmp;
798
799 size_t cchRootPath = 0; /* Length of root path in chars. */
800 if (cPaths > 1)
801 {
802 cchRootPath = RTPathFindCommon(cPaths, papszPathTmp);
803 }
804 else
805 cchRootPath = RTPathParentLength(papszPathTmp[0]);
806
807 if (cchRootPath)
808 {
809 /* Just use the first path in the array as the reference. */
810 char *pszRootPath = RTStrDupN(papszPathTmp[0], cchRootPath);
811 if (pszRootPath)
812 {
813 rc = dndTransferInitAndSetRoot(pList, pszRootPath);
814 RTStrFree(pszRootPath);
815 }
816 else
817 rc = VERR_NO_MEMORY;
818 }
819 else
820 rc = VERR_INVALID_PARAMETER;
821 }
822
823 if (RT_SUCCESS(rc))
824 {
825 /*
826 * Add all paths to the list.
827 */
828 for (size_t i = 0; i < cPaths; i++)
829 {
830 const char *pcszPath = enmFmt == DNDTRANSFERLISTFMT_NATIVE
831 ? papcszPaths[i] : papszPathsTmp[i];
832 rc = DnDTransferListAppendPath(pList, DNDTRANSFERLISTFMT_NATIVE, pcszPath, fFlags);
833 if (RT_FAILURE(rc))
834 {
835 LogRel(("DnD: Adding path '%s' (format %#x, root '%s') to transfer list failed with %Rrc\n",
836 pcszPath, enmFmt, pList->pszPathRootAbs ? pList->pszPathRootAbs : "<None>", rc));
837 break;
838 }
839 }
840 }
841
842 if (papszPathsTmp)
843 {
844 for (size_t i = 0; i < cPaths; i++)
845 RTStrFree(papszPathsTmp[i]);
846 RTMemFree(papszPathsTmp);
847 }
848
849 LogFlowFuncLeaveRC(rc);
850 return rc;
851}
852
853/**
854 * Appends the root entries for a transfer list.
855 *
856 * @returns VBox status code.
857 * @param pList Transfer list to append to.
858 * @param enmFmt Format of \a pszPaths to append.
859 * @param pszPaths Buffer of paths to append.
860 * @param cbPaths Size (in bytes) of buffer of paths to append.
861 * @param pcszSeparator Separator string to use.
862 * @param fFlags Transfer list flags to use for appending.
863 */
864int DnDTransferListAppendRootsFromBuffer(PDNDTRANSFERLIST pList,
865 DNDTRANSFERLISTFMT enmFmt, const char *pszPaths, size_t cbPaths,
866 const char *pcszSeparator, DNDTRANSFERLISTFLAGS fFlags)
867{
868 AssertPtrReturn(pList, VERR_INVALID_POINTER);
869 AssertPtrReturn(pszPaths, VERR_INVALID_POINTER);
870 AssertReturn(cbPaths, VERR_INVALID_PARAMETER);
871
872 char **papszPaths = NULL;
873 size_t cPaths = 0;
874 int rc = RTStrSplit(pszPaths, cbPaths, pcszSeparator, &papszPaths, &cPaths);
875 if (RT_SUCCESS(rc))
876 rc = DnDTransferListAppendRootsFromArray(pList, enmFmt, papszPaths, cPaths, fFlags);
877
878 for (size_t i = 0; i < cPaths; ++i)
879 RTStrFree(papszPaths[i]);
880 RTMemFree(papszPaths);
881
882 return rc;
883}
884
885/**
886 * Appends root entries to a transfer list.
887 *
888 * @returns VBox status code.
889 * @param pList Transfer list to append root entries to.
890 * @param enmFmt Format of \a papcszPaths to append.
891 * @param papcszPaths Array of paths to append.
892 * @param cPaths Number of paths in \a papcszPaths to append.
893 * @param fFlags Transfer list flags to use for appending.
894 */
895int DnDTransferListAppendRootsFromArray(PDNDTRANSFERLIST pList,
896 DNDTRANSFERLISTFMT enmFmt,
897 const char * const *papcszPaths, size_t cPaths, DNDTRANSFERLISTFLAGS fFlags)
898{
899 AssertPtrReturn(pList, VERR_INVALID_POINTER);
900 AssertPtrReturn(papcszPaths, VERR_INVALID_POINTER);
901 AssertReturn(!(fFlags & ~DNDTRANSFERLIST_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
902
903 AssertMsgReturn(pList->pszPathRootAbs, ("Root path not set yet\n"), VERR_WRONG_ORDER);
904
905 int rc = VINF_SUCCESS;
906
907 if (!cPaths) /* Nothing to add? Bail out. */
908 return VINF_SUCCESS;
909
910 char **papszPathsTmp = NULL;
911
912 /* If URI data is being handed in, extract the paths first. */
913 if (enmFmt == DNDTRANSFERLISTFMT_URI)
914 {
915 papszPathsTmp = (char **)RTMemAlloc(sizeof(char *) * cPaths);
916 if (papszPathsTmp)
917 {
918 for (size_t i = 0; i < cPaths; i++)
919 papszPathsTmp[i] = RTUriFilePath(papcszPaths[i]);
920 }
921 else
922 rc = VERR_NO_MEMORY;
923 }
924
925 if (RT_FAILURE(rc))
926 return rc;
927
928 char szPath[RTPATH_MAX];
929
930 /*
931 * Add all root entries to the root list.
932 */
933 for (size_t i = 0; i < cPaths; i++)
934 {
935 const char *pcszPath = enmFmt == DNDTRANSFERLISTFMT_NATIVE
936 ? papcszPaths[i] : papszPathsTmp[i];
937
938 rc = RTPathJoin(szPath, sizeof(szPath), pList->pszPathRootAbs, pcszPath);
939 AssertRCBreak(rc);
940
941 rc = DnDPathConvert(szPath, sizeof(szPath), DNDPATHCONVERT_FLAGS_TRANSPORT);
942 AssertRCBreak(rc);
943
944 rc = dndTransferListRootEntryAdd(pList, szPath);
945 if (RT_FAILURE(rc))
946 {
947 LogRel(("DnD: Adding root entry '%s' (format %#x, root '%s') to transfer list failed with %Rrc\n",
948 szPath, enmFmt, pList->pszPathRootAbs, rc));
949 break;
950 }
951 }
952
953 if (papszPathsTmp)
954 {
955 for (size_t i = 0; i < cPaths; i++)
956 RTStrFree(papszPathsTmp[i]);
957 RTMemFree(papszPathsTmp);
958 }
959
960 LogFlowFuncLeaveRC(rc);
961 return rc;
962}
963
964/**
965 * Returns the first transfer object in a list.
966 *
967 * @returns Pointer to transfer object if found, or NULL if not found.
968 * @param pList Transfer list to get first transfer object from.
969 */
970PDNDTRANSFEROBJECT DnDTransferListObjGetFirst(PDNDTRANSFERLIST pList)
971{
972 AssertPtrReturn(pList, NULL);
973
974 return RTListGetFirst(&pList->lstObj, DNDTRANSFEROBJECT, Node);
975}
976
977/**
978 * Removes an object from a transfer list, internal version.
979 *
980 * @param pList Transfer list to remove object from.
981 * @param pObj Object to remove. The object will be free'd and the pointer is invalid after calling.
982 */
983static void dndTransferListObjRemoveInternal(PDNDTRANSFERLIST pList, PDNDTRANSFEROBJECT pObj)
984{
985 AssertPtrReturnVoid(pList);
986 AssertPtrReturnVoid(pObj);
987
988 /** @todo Validate if \a pObj is part of \a pList. */
989
990 uint64_t cbSize = DnDTransferObjectGetSize(pObj);
991 Assert(pList->cbObjTotal >= cbSize);
992 pList->cbObjTotal -= cbSize; /* Adjust total size. */
993
994 dndTransferListObjFree(pList, pObj);
995}
996
997/**
998 * Removes an object from a transfer list.
999 *
1000 * @param pList Transfer list to remove object from.
1001 * @param pObj Object to remove. The object will be free'd and the pointer is invalid after calling.
1002 */
1003void DnDTransferListObjRemove(PDNDTRANSFERLIST pList, PDNDTRANSFEROBJECT pObj)
1004{
1005 return dndTransferListObjRemoveInternal(pList, pObj);
1006}
1007
1008/**
1009 * Removes the first DnD transfer object from a transfer list.
1010 *
1011 * @param pList Transfer list to remove first entry for.
1012 */
1013void DnDTransferListObjRemoveFirst(PDNDTRANSFERLIST pList)
1014{
1015 AssertPtrReturnVoid(pList);
1016
1017 if (!pList->cObj)
1018 return;
1019
1020 PDNDTRANSFEROBJECT pObj = RTListGetFirst(&pList->lstObj, DNDTRANSFEROBJECT, Node);
1021 AssertPtr(pObj);
1022
1023 dndTransferListObjRemoveInternal(pList, pObj);
1024}
1025
1026/**
1027 * Returns all root entries of a transfer list as a string.
1028 *
1029 * @returns VBox status code.
1030 * @param pList Transfer list to return root paths for.
1031 * @param pcszPathBase Root path to use as a base path. If NULL, the list's absolute root path will be used (if any).
1032 * @param pcszSeparator Separator to use for separating the root entries.
1033 * @param ppszBuffer Where to return the allocated string on success. Needs to be free'd with RTStrFree().
1034 * @param pcbBuffer Where to return the size (in bytes) of the allocated string on success, including terminator.
1035 */
1036int DnDTransferListGetRootsEx(PDNDTRANSFERLIST pList,
1037 DNDTRANSFERLISTFMT enmFmt, const char *pcszPathBase, const char *pcszSeparator,
1038 char **ppszBuffer, size_t *pcbBuffer)
1039{
1040 AssertPtrReturn(pList, VERR_INVALID_POINTER);
1041 /* pcszPathBase can be NULL. */
1042 AssertPtrReturn(pcszSeparator, VERR_INVALID_POINTER);
1043 AssertPtrReturn(ppszBuffer, VERR_INVALID_POINTER);
1044 AssertPtrReturn(pcbBuffer, VERR_INVALID_POINTER);
1045
1046 char *pszString = NULL;
1047 size_t cchString = 0;
1048
1049 const size_t cchSep = RTStrNLen(pcszSeparator, RTPATH_MAX);
1050
1051 /* Find out which root path to use. */
1052 const char *pcszPathRootTmp = pcszPathBase ? pcszPathBase : pList->pszPathRootAbs;
1053 /* pcszPathRootTmp can be NULL */
1054
1055 LogFlowFunc(("Using root path '%s'\n", pcszPathRootTmp ? pcszPathRootTmp : "<None>"));
1056
1057 int rc = DnDPathValidate(pcszPathRootTmp, false /* fMustExist */);
1058 if (RT_FAILURE(rc))
1059 return rc;
1060
1061 char szPath[RTPATH_MAX];
1062
1063 PDNDTRANSFERLISTROOT pRoot;
1064 RTListForEach(&pList->lstRoot, pRoot, DNDTRANSFERLISTROOT, Node)
1065 {
1066 if (pcszPathRootTmp)
1067 {
1068 rc = RTStrCopy(szPath, sizeof(szPath), pcszPathRootTmp);
1069 AssertRCBreak(rc);
1070 }
1071
1072 rc = RTPathAppend(szPath, sizeof(szPath), pRoot->pszPathRoot);
1073 AssertRCBreak(rc);
1074
1075 if (enmFmt == DNDTRANSFERLISTFMT_URI)
1076 {
1077 char *pszPathURI = RTUriFileCreate(szPath);
1078 AssertPtrBreakStmt(pszPathURI, rc = VERR_NO_MEMORY);
1079 rc = RTStrAAppend(&pszString, pszPathURI);
1080 cchString += RTStrNLen(pszPathURI, RTPATH_MAX);
1081 RTStrFree(pszPathURI);
1082 AssertRCBreak(rc);
1083 }
1084 else /* Native */
1085 {
1086#if RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS
1087 /* Convert paths to native path style. */
1088 rc = DnDPathConvert(szPath, sizeof(szPath), DNDPATHCONVERT_FLAGS_TO_DOS);
1089#endif
1090 if (RT_SUCCESS(rc))
1091 {
1092 rc = RTStrAAppend(&pszString, szPath);
1093 AssertRCBreak(rc);
1094
1095 cchString += RTStrNLen(szPath, RTPATH_MAX);
1096 }
1097 }
1098
1099 rc = RTStrAAppend(&pszString, pcszSeparator);
1100 AssertRCBreak(rc);
1101
1102 cchString += cchSep;
1103 }
1104
1105 if (RT_SUCCESS(rc))
1106 {
1107 *ppszBuffer = pszString;
1108 *pcbBuffer = pszString ? cchString + 1 /* Include termination */ : 0;
1109 }
1110 else
1111 RTStrFree(pszString);
1112 return rc;
1113}
1114
1115/**
1116 * Returns all root entries for a DnD transfer list.
1117 *
1118 * Note: Convenience function which uses the default DnD path separator.
1119 *
1120 * @returns VBox status code.
1121 * @param pList Transfer list to return root entries for.
1122 * @param enmFmt Which format to use for returning the entries.
1123 * @param ppszBuffer Where to return the allocated string on success. Needs to be free'd with RTStrFree().
1124 * @param pcbBuffer Where to return the size (in bytes) of the allocated string on success, including terminator.
1125 */
1126int DnDTransferListGetRoots(PDNDTRANSFERLIST pList,
1127 DNDTRANSFERLISTFMT enmFmt, char **ppszBuffer, size_t *pcbBuffer)
1128{
1129 return DnDTransferListGetRootsEx(pList, enmFmt, "" /* pcszPathRoot */, DND_PATH_SEPARATOR_STR,
1130 ppszBuffer, pcbBuffer);
1131}
1132
1133/**
1134 * Returns the total root entries count for a DnD transfer list.
1135 *
1136 * @returns Total number of root entries.
1137 * @param pList Transfer list to return total number of root entries for.
1138 */
1139uint64_t DnDTransferListGetRootCount(PDNDTRANSFERLIST pList)
1140{
1141 AssertPtrReturn(pList, 0);
1142 return pList->cRoots;
1143}
1144
1145/**
1146 * Returns the absolute root path for a DnD transfer list.
1147 *
1148 * @returns Pointer to the root path.
1149 * @param pList Transfer list to return root path for.
1150 */
1151const char *DnDTransferListGetRootPathAbs(PDNDTRANSFERLIST pList)
1152{
1153 AssertPtrReturn(pList, NULL);
1154 return pList->pszPathRootAbs;
1155}
1156
1157/**
1158 * Returns the total transfer object count for a DnD transfer list.
1159 *
1160 * @returns Total number of transfer objects.
1161 * @param pList Transfer list to return total number of transfer objects for.
1162 */
1163uint64_t DnDTransferListObjCount(PDNDTRANSFERLIST pList)
1164{
1165 AssertPtrReturn(pList, 0);
1166 return pList->cObj;
1167}
1168
1169/**
1170 * Returns the total bytes of all handled transfer objects for a DnD transfer list.
1171 *
1172 * @returns VBox status code.
1173 * @param pList Transfer list to return total bytes for.
1174 */
1175uint64_t DnDTransferListObjTotalBytes(PDNDTRANSFERLIST pList)
1176{
1177 AssertPtrReturn(pList, 0);
1178 return pList->cbObjTotal;
1179}
1180
1181/**
1182 * Sets the absolute root path of a transfer list.
1183 *
1184 * @returns VBox status code.
1185 * @param pList Transfer list to set root path for.
1186 * @param pcszRootPathAbs Absolute root path to set.
1187 */
1188static int dndTransferListSetRootPath(PDNDTRANSFERLIST pList, const char *pcszRootPathAbs)
1189{
1190 AssertPtrReturn(pList, VERR_INVALID_POINTER);
1191 AssertPtrReturn(pcszRootPathAbs, VERR_INVALID_POINTER);
1192 AssertReturn(pList->pszPathRootAbs == NULL, VERR_WRONG_ORDER); /* Already initialized? */
1193
1194 LogFlowFunc(("pcszRootPathAbs=%s\n", pcszRootPathAbs));
1195
1196 char szRootPath[RTPATH_MAX];
1197 int rc = RTStrCopy(szRootPath, sizeof(szRootPath), pcszRootPathAbs);
1198 if (RT_FAILURE(rc))
1199 return rc;
1200
1201 /* Note: The list's root path is kept in native style, so no conversion needed here. */
1202
1203 RTPathEnsureTrailingSeparatorEx(szRootPath, sizeof(szRootPath), RTPATH_STR_F_STYLE_HOST);
1204
1205 /* Make sure the root path is a directory (and no symlink or stuff). */
1206 RTFSOBJINFO objInfo;
1207 rc = RTPathQueryInfo(szRootPath, &objInfo, RTFSOBJATTRADD_NOTHING);
1208 if (RT_SUCCESS(rc))
1209 {
1210 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode & RTFS_TYPE_MASK))
1211 {
1212 pList->pszPathRootAbs = RTStrDup(szRootPath);
1213 if (pList->pszPathRootAbs)
1214 {
1215 LogFlowFunc(("Root path is '%s'\n", pList->pszPathRootAbs));
1216 }
1217 else
1218 rc = VERR_NO_MEMORY;
1219 }
1220 else
1221 rc = VERR_NOT_A_DIRECTORY;
1222 }
1223
1224 return rc;
1225}
1226
1227/**
1228 * Adds a root entry to a DnD transfer list.
1229 *
1230 * @returns VBox status code.
1231 * @param pList Transfer list to add root entry to.
1232 * @param pcszRoot Root entry to add.
1233 */
1234static int dndTransferListRootEntryAdd(PDNDTRANSFERLIST pList, const char *pcszRoot)
1235{
1236 AssertPtrReturn(pList->pszPathRootAbs, VERR_WRONG_ORDER); /* The list's root path must be set first. */
1237
1238 int rc;
1239
1240 /** @todo Handle / reject double entries. */
1241
1242 /* Get the index pointing to the relative path in relation to set the root path. */
1243 const size_t idxPathToAdd = strlen(pList->pszPathRootAbs);
1244 AssertReturn(strlen(pcszRoot) > idxPathToAdd, VERR_INVALID_PARAMETER); /* Should never happen (tm). */
1245
1246 PDNDTRANSFERLISTROOT pRoot = (PDNDTRANSFERLISTROOT)RTMemAllocZ(sizeof(DNDTRANSFERLISTROOT));
1247 if (pRoot)
1248 {
1249 const char *pcszRootIdx = &pcszRoot[idxPathToAdd];
1250
1251 LogFlowFunc(("pcszRoot=%s\n", pcszRootIdx));
1252
1253 pRoot->pszPathRoot = RTStrDup(pcszRootIdx);
1254 if (pRoot->pszPathRoot)
1255 {
1256 RTListAppend(&pList->lstRoot, &pRoot->Node);
1257 pList->cRoots++;
1258
1259 rc = VINF_SUCCESS;
1260 }
1261 else
1262 rc = VERR_NO_MEMORY;
1263
1264 if (RT_FAILURE(rc))
1265 {
1266 RTMemFree(pRoot);
1267 pRoot = NULL;
1268 }
1269 }
1270 else
1271 rc = VERR_NO_MEMORY;
1272
1273 return rc;
1274}
1275
1276/**
1277 * Removes (and destroys) a DnD transfer root entry.
1278 *
1279 * @param pList Transfer list to free root for.
1280 * @param pRootObj Transfer list root to free. The pointer will be invalid after calling.
1281 */
1282static void dndTransferListRootEntryFree(PDNDTRANSFERLIST pList, PDNDTRANSFERLISTROOT pRootObj)
1283{
1284 if (!pRootObj)
1285 return;
1286
1287 RTStrFree(pRootObj->pszPathRoot);
1288
1289 RTListNodeRemove(&pRootObj->Node);
1290 RTMemFree(pRootObj);
1291
1292 AssertReturnVoid(pList->cRoots);
1293 pList->cRoots--;
1294}
1295
Note: See TracBrowser for help on using the repository browser.

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