VirtualBox

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

Last change on this file since 85371 was 85371, checked in by vboxsync, 5 years ago

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.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.4 KB
Line 
1/* $Id: DnDTransferList.cpp 85371 2020-07-17 10:02:58Z vboxsync $ */
2/** @file
3 * DnD - transfer list implemenation.
4 */
5
6/*
7 * Copyright (C) 2014-2020 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/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_GUEST_DND
23#include <VBox/GuestHost/DragAndDrop.h>
24
25#include <iprt/dir.h>
26#include <iprt/err.h>
27#include <iprt/file.h>
28#include <iprt/fs.h>
29#include <iprt/mem.h>
30#include <iprt/path.h>
31#include <iprt/string.h>
32#include <iprt/symlink.h>
33#include <iprt/uri.h>
34
35#include <VBox/log.h>
36
37
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
157 if (RT_SUCCESS(rc))
158 {
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)
168 {
169 const bool fIsFile = RTFS_IS_FILE(fMode);
170
171 rc = DnDTransferObjectInit(pObj, fIsFile ? DNDTRANSFEROBJTYPE_FILE : DNDTRANSFEROBJTYPE_DIRECTORY,
172 pList->pszPathRootAbs, &pcszSrcAbs[idxPathToAdd]);
173 if (RT_SUCCESS(rc))
174 {
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);
194 }
195
196 if (RT_FAILURE(rc))
197 RTMemFree(pObj);
198 }
199 else
200 rc = VERR_NO_MEMORY;
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:
292 {
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);
591 }
592 else
593 rc = VERR_NO_MEMORY;
594 }
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 }
701 else
702 {
703 rc = RTStrAAppend(&pszString, szPath);
704 AssertRCBreak(rc);
705 }
706
707 rc = RTStrAAppend(&pszString, pcszSeparator);
708 AssertRCBreak(rc);
709 }
710
711 if (RT_SUCCESS(rc))
712 {
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);
893 if (RT_SUCCESS(rc))
894 {
895 LogRel2(("DnD: Got URI data item '%s'\n", pszFilePath));
896
897 PDNDTRANSFERLISTROOT pRoot = (PDNDTRANSFERLISTROOT)RTMemAlloc(sizeof(DNDTRANSFERLISTROOT));
898 if (pRoot)
899 {
900 pRoot->pszPathRoot = pszFilePath;
901
902 RTListAppend(&pList->lstRoot, &pRoot->Node);
903 pList->cRoots++;
904
905 }
906 else
907 rc = VERR_NO_MEMORY;
908 }
909 else
910 LogRel(("DnD: Path conversion of URI data item '%s' failed with %Rrc\n", pszFilePath, rc));
911 }
912 else
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);
917 }
918
919 LogFlowFuncLeaveRC(rc);
920 return rc;
921}
922
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);
938 AssertPtrReturn(pszURIPaths, VERR_INVALID_POINTER);
939 AssertReturn(cbURIPaths, VERR_INVALID_PARAMETER);
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);
957 }
958
959 LogFlowFuncLeaveRC(rc);
960 return rc;
961}
962#endif
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