VirtualBox

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

Last change on this file since 93622 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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