VirtualBox

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

Last change on this file since 85373 was 85373, checked in by vboxsync, 4 years ago

DnD: Fixed crashes on VM shutdown.

  • 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 85373 2020-07-17 12:45:56Z 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 if (!pList->pszPathRootAbs)
117 return;
118
119 PDNDTRANSFERLISTROOT pRootCur, pRootNext;
120 RTListForEachSafe(&pList->lstRoot, pRootCur, pRootNext, DNDTRANSFERLISTROOT, Node)
121 dndTransferListRootFree(pList, pRootCur);
122 Assert(RTListIsEmpty(&pList->lstRoot));
123
124 PDNDTRANSFEROBJECT pObjCur, pObjNext;
125 RTListForEachSafe(&pList->lstObj, pObjCur, pObjNext, DNDTRANSFEROBJECT, Node)
126 dndTransferListObjFree(pList, pObjCur);
127 Assert(RTListIsEmpty(&pList->lstObj));
128
129 Assert(pList->cRoots == 0);
130 Assert(pList->cObj == 0);
131
132 pList->cbObjTotal = 0;
133}
134
135/**
136 * Adds a single transfer object entry to a transfer List.
137 *
138 * @returns VBox status code.
139 * @param pList Transfer list to add entry to.
140 * @param pcszSrcAbs Absolute source path (local) to use.
141 * @param fMode File mode of entry to add.
142 * @param fFlags Transfer list flags to use for appending.
143 */
144static int dndTransferListObjAdd(PDNDTRANSFERLIST pList, const char *pcszSrcAbs, RTFMODE fMode, DNDTRANSFERLISTFLAGS fFlags)
145{
146 AssertPtrReturn(pList, VERR_INVALID_POINTER);
147 AssertPtrReturn(pcszSrcAbs, VERR_INVALID_POINTER);
148
149 LogFlowFunc(("pcszSrcAbs=%s, fMode=%#x, fFlags=0x%x\n", pcszSrcAbs, fMode, fFlags));
150
151 int rc = VINF_SUCCESS;
152
153 if ( !RTFS_IS_FILE(fMode)
154 && !RTFS_IS_DIRECTORY(fMode))
155 /** @todo Symlinks not allowed. */
156 {
157 rc = VERR_NOT_SUPPORTED;
158 }
159
160 if (RT_SUCCESS(rc))
161 {
162 /* Calculate the path to add as the destination path to our URI object. */
163 const size_t idxPathToAdd = strlen(pList->pszPathRootAbs);
164 AssertReturn(strlen(pcszSrcAbs) > idxPathToAdd, VERR_INVALID_PARAMETER); /* Should never happen (tm). */
165
166 PDNDTRANSFEROBJECT pObj = (PDNDTRANSFEROBJECT)RTMemAllocZ(sizeof(DNDTRANSFEROBJECT));
167 if (pObj)
168 {
169 pObj = (PDNDTRANSFEROBJECT)RTMemAllocZ(sizeof(DNDTRANSFEROBJECT));
170 if (pObj)
171 {
172 const bool fIsFile = RTFS_IS_FILE(fMode);
173
174 rc = DnDTransferObjectInit(pObj, fIsFile ? DNDTRANSFEROBJTYPE_FILE : DNDTRANSFEROBJTYPE_DIRECTORY,
175 pList->pszPathRootAbs, &pcszSrcAbs[idxPathToAdd]);
176 if (RT_SUCCESS(rc))
177 {
178 if (fIsFile)
179 rc = DnDTransferObjectOpen(pObj,
180 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE, /** @todo Add a standard fOpen mode for this list. */
181 0 /* fMode */, DNDTRANSFEROBJECT_FLAGS_NONE);
182 if (RT_SUCCESS(rc))
183 {
184 RTListAppend(&pList->lstObj, &pObj->Node);
185
186 pList->cObj++;
187 if (fIsFile)
188 pList->cbObjTotal += DnDTransferObjectGetSize(pObj);
189
190 if ( fIsFile
191 && !(fFlags & DNDTRANSFERLIST_FLAGS_KEEP_OPEN)) /* Shall we keep the file open while being added to this list? */
192 DnDTransferObjectClose(pObj);
193 }
194
195 if (RT_FAILURE(rc))
196 DnDTransferObjectDestroy(pObj);
197 }
198
199 if (RT_FAILURE(rc))
200 RTMemFree(pObj);
201 }
202 else
203 rc = VERR_NO_MEMORY;
204
205 if (RT_FAILURE(rc))
206 RTMemFree(pObj);
207 }
208 else
209 rc = VERR_NO_MEMORY;
210 }
211
212 if (RT_FAILURE(rc))
213 LogRel(("DnD: Adding entry '%s' of type %#x failed with rc=%Rrc\n", pcszSrcAbs, fMode & RTFS_TYPE_MASK, rc));
214
215 LogFlowFuncLeaveRC(rc);
216 return rc;
217}
218
219/**
220 * Frees an internal DnD transfer list object.
221 *
222 * @param pList Transfer list to free object for.
223 * @param pLstObj transfer list object to free. The pointer will be invalid after calling.
224 */
225static void dndTransferListObjFree(PDNDTRANSFERLIST pList, PDNDTRANSFEROBJECT pObj)
226{
227 if (!pObj)
228 return;
229
230 DnDTransferObjectDestroy(pObj);
231 RTListNodeRemove(&pObj->Node);
232 RTMemFree(pObj);
233
234 AssertReturnVoid(pList->cObj);
235 pList->cObj--;
236}
237
238/**
239 * Helper routine for handling adding sub directories.
240 *
241 * @return IPRT status code.
242 * @param pList transfer list to add found entries to.
243 * @param pszDir Pointer to the directory buffer.
244 * @param cchDir The length of pszDir in pszDir.
245 * @param pDirEntry Pointer to the directory entry.
246 * @param fFlags Flags of type DNDTRANSFERLISTFLAGS.
247 */
248static int dndTransferListAppendPathRecursiveSub(PDNDTRANSFERLIST pList,
249 char *pszDir, size_t cchDir, PRTDIRENTRYEX pDirEntry,
250 DNDTRANSFERLISTFLAGS fFlags)
251
252{
253 Assert(cchDir > 0); Assert(pszDir[cchDir] == '\0');
254
255 /* Make sure we've got some room in the path, to save us extra work further down. */
256 if (cchDir + 3 >= RTPATH_MAX)
257 return VERR_BUFFER_OVERFLOW;
258
259 /* Open directory. */
260 RTDIR hDir;
261 int rc = RTDirOpen(&hDir, pszDir);
262 if (RT_FAILURE(rc))
263 return rc;
264
265 /* Ensure we've got a trailing slash (there is space for it see above). */
266 if (!RTPATH_IS_SEP(pszDir[cchDir - 1]))
267 {
268 pszDir[cchDir++] = RTPATH_SLASH;
269 pszDir[cchDir] = '\0';
270 }
271
272 LogFlowFunc(("pszDir=%s\n", pszDir));
273
274 /*
275 * Process the files and subdirs.
276 */
277 for (;;)
278 {
279 /* Get the next directory. */
280 size_t cbDirEntry = DNDTRANSFERLIST_DIRENTRY_BUF_SIZE;
281 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
282 if (RT_FAILURE(rc))
283 break;
284
285 /* Check length. */
286 if (pDirEntry->cbName + cchDir + 3 >= RTPATH_MAX)
287 {
288 rc = VERR_BUFFER_OVERFLOW;
289 break;
290 }
291
292 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
293 {
294 case RTFS_TYPE_SYMLINK:
295 {
296 if (!(fFlags & DNDTRANSFERLIST_FLAGS_RESOLVE_SYMLINKS))
297 break;
298 RT_FALL_THRU();
299 }
300 case RTFS_TYPE_DIRECTORY:
301 {
302 if (RTDirEntryExIsStdDotLink(pDirEntry))
303 continue;
304
305 memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);
306 int rc2 = dndTransferListAppendPathRecursiveSub(pList, pszDir, cchDir + pDirEntry->cbName, pDirEntry, fFlags);
307 if (RT_SUCCESS(rc))
308 rc = rc2;
309 break;
310 }
311
312 case RTFS_TYPE_FILE:
313 {
314 memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);
315 rc = dndTransferListObjAdd(pList, pszDir, pDirEntry->Info.Attr.fMode, fFlags);
316 break;
317 }
318
319 default:
320 {
321
322 break;
323 }
324 }
325 }
326
327 if (rc == VERR_NO_MORE_FILES) /* Done reading current directory? */
328 {
329 rc = VINF_SUCCESS;
330 }
331 else if (RT_FAILURE(rc))
332 LogRel(("DnD: Error while adding files recursively, rc=%Rrc\n", rc));
333
334 int rc2 = RTDirClose(hDir);
335 if (RT_FAILURE(rc2))
336 {
337 if (RT_SUCCESS(rc))
338 rc = rc2;
339 }
340
341 return rc;
342}
343
344/**
345 * Appends a native system path recursively by adding these entries as transfer objects.
346 *
347 * @returns VBox status code.
348 * @param pList Transfer list to add found entries to.
349 * @param pcszPathAbs Absolute path to add.
350 * @param fFlags Flags of type DNDTRANSFERLISTFLAGS.
351 */
352static int dndTransferListAppendPathNativeRecursive(PDNDTRANSFERLIST pList,
353 const char *pcszPathAbs, DNDTRANSFERLISTFLAGS fFlags)
354{
355 char szPathAbs[RTPATH_MAX];
356 int rc = RTStrCopy(szPathAbs, sizeof(szPathAbs), pcszPathAbs);
357 if (RT_FAILURE(rc))
358 return rc;
359
360 union
361 {
362 uint8_t abPadding[DNDTRANSFERLIST_DIRENTRY_BUF_SIZE];
363 RTDIRENTRYEX DirEntry;
364 } uBuf;
365 const size_t cchPathAbs = strlen(szPathAbs);
366 if (!cchPathAbs)
367 return VINF_SUCCESS;
368 return dndTransferListAppendPathRecursiveSub(pList, szPathAbs, cchPathAbs, &uBuf.DirEntry, fFlags);
369}
370
371static int dndTransferListAppendPathNative(PDNDTRANSFERLIST pList, const char *pcszPath, DNDTRANSFERLISTFLAGS fFlags)
372{
373 /* We don't want to have a relative directory here. */
374 if (!RTPathStartsWithRoot(pcszPath))
375 return VERR_INVALID_PARAMETER;
376
377 int rc = DnDPathValidate(pcszPath, false /* fMustExist */);
378 if (RT_FAILURE(rc))
379 return rc;
380
381 char szPathAbs[RTPATH_MAX];
382 rc = RTStrCopy(szPathAbs, sizeof(szPathAbs), pcszPath);
383 if (RT_FAILURE(rc))
384 return rc;
385
386 size_t cchPathAbs = RTStrNLen(szPathAbs, sizeof(szPathAbs));
387 AssertReturn(cchPathAbs, VERR_INVALID_PARAMETER);
388
389 /* Convert path to transport style. */
390 rc = DnDPathConvert(szPathAbs, sizeof(szPathAbs), DNDPATHCONVERT_FLAGS_TRANSPORT);
391 if (RT_SUCCESS(rc))
392 {
393 /* Make sure the path has the same root path as our list. */
394 if (RTPathStartsWith(szPathAbs, pList->pszPathRootAbs))
395 {
396 RTDIR hDir;
397 rc = RTDirOpen(&hDir, szPathAbs);
398 if (RT_SUCCESS(rc))
399 {
400 for (;;)
401 {
402 /* Get the next directory. */
403 RTDIRENTRYEX dirEntry;
404 rc = RTDirReadEx(hDir, &dirEntry, NULL, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK /** @todo No symlinks yet. */);
405 if (RT_SUCCESS(rc))
406 {
407 if (RTDirEntryExIsStdDotLink(&dirEntry))
408 continue;
409
410 /* Check length. */
411 if (dirEntry.cbName + cchPathAbs + 3 >= sizeof(szPathAbs))
412 {
413 rc = VERR_BUFFER_OVERFLOW;
414 break;
415 }
416
417 /* Append the directory entry to our absolute path. */
418 memcpy(&szPathAbs[cchPathAbs], dirEntry.szName, dirEntry.cbName + 1);
419
420 LogFlowFunc(("szName=%s, szPathAbs=%s\n", dirEntry.szName, szPathAbs));
421
422 switch (dirEntry.Info.Attr.fMode & RTFS_TYPE_MASK)
423 {
424 case RTFS_TYPE_DIRECTORY:
425 {
426 rc = dndTransferListAppendPathNativeRecursive(pList, szPathAbs, fFlags);
427 break;
428 }
429
430 case RTFS_TYPE_FILE:
431 {
432 rc = dndTransferListObjAdd(pList, szPathAbs, dirEntry.Info.Attr.fMode, fFlags);
433 break;
434 }
435
436 default:
437 rc = VERR_NOT_SUPPORTED;
438 break;
439 }
440
441 if (RT_SUCCESS(rc))
442 rc = dndTransferListRootAdd(pList, dirEntry.szName);
443 }
444 else if (rc == VERR_NO_MORE_FILES)
445 {
446 rc = VINF_SUCCESS;
447 break;
448 }
449 else
450 break;
451
452 if (RT_FAILURE(rc))
453 break;
454 }
455 }
456 }
457 else
458 rc = VERR_INVALID_PARAMETER;
459 }
460
461 if (RT_FAILURE(rc))
462 LogRel(("DnD: Adding native path '%s' failed with rc=%Rrc\n", pcszPath, rc));
463
464 return rc;
465}
466
467static int dndTransferListAppendPathURI(PDNDTRANSFERLIST pList, const char *pcszPath, DNDTRANSFERLISTFLAGS fFlags)
468{
469 RT_NOREF(fFlags);
470
471 /* Query the path component of a file URI. If this hasn't a
472 * file scheme, NULL is returned. */
473 char *pszFilePath;
474 int rc = RTUriFilePathEx(pcszPath, RTPATH_STR_F_STYLE_UNIX, &pszFilePath, 0 /*cbPath*/, NULL /*pcchPath*/);
475 if (RT_SUCCESS(rc))
476 {
477 LogFlowFunc(("pcszPath=%s -> pszFilePath=%s\n", pcszPath, pszFilePath));
478 rc = dndTransferListRootAdd(pList, pszFilePath);
479 RTStrFree(pszFilePath);
480
481 }
482
483 if (RT_FAILURE(rc))
484 LogRel(("DnD: Adding URI path '%s' failed with rc=%Rrc\n", pcszPath, rc));
485
486 return rc;
487}
488
489/**
490 * Appends a single path to a transfer list.
491 *
492 * @returns VBox status code. VERR_NOT_SUPPORTED if the path is not supported.
493 * @param pList Transfer list to append to.
494 * @param enmFmt Format of \a pszPaths to append.
495 * @param pcszPath Path to append. Must be part of the list's set root path.
496 * @param fFlags Transfer list flags to use for appending.
497 */
498int DnDTransferListAppendPath(PDNDTRANSFERLIST pList,
499 DNDTRANSFERLISTFMT enmFmt, const char *pcszPath, DNDTRANSFERLISTFLAGS fFlags)
500{
501 AssertPtrReturn(pList, VERR_INVALID_POINTER);
502 AssertPtrReturn(pcszPath, VERR_INVALID_POINTER);
503 AssertReturn(!(fFlags & ~DNDTRANSFERLIST_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
504 AssertReturn(!(fFlags & DNDTRANSFERLIST_FLAGS_RESOLVE_SYMLINKS), VERR_NOT_SUPPORTED);
505
506 int rc;
507
508 switch (enmFmt)
509 {
510 case DNDTRANSFERLISTFMT_NATIVE:
511 rc = dndTransferListAppendPathNative(pList, pcszPath, fFlags);
512 break;
513
514 case DNDTRANSFERLISTFMT_URI:
515 rc = dndTransferListAppendPathURI(pList, pcszPath, fFlags);
516 break;
517
518 default:
519 AssertFailedStmt(rc = VERR_NOT_SUPPORTED);
520 break; /* Never reached */
521 }
522
523 return rc;
524}
525
526/**
527 * Appends native paths to a transfer list.
528 *
529 * @returns VBox status code.
530 * @param pList Transfer list to append to.
531 * @param enmFmt Format of \a pszPaths to append.
532 * @param pszPaths Buffer of paths to append.
533 * @param cbPaths Size (in bytes) of buffer of paths to append.
534 * @param pcszSeparator Separator string to use.
535 * @param fFlags Transfer list flags to use for appending.
536 */
537int DnDTransferListAppendPathsFromBuffer(PDNDTRANSFERLIST pList,
538 DNDTRANSFERLISTFMT enmFmt, const char *pszPaths, size_t cbPaths,
539 const char *pcszSeparator, DNDTRANSFERLISTFLAGS fFlags)
540{
541 AssertPtrReturn(pList, VERR_INVALID_POINTER);
542 AssertPtrReturn(pszPaths, VERR_INVALID_POINTER);
543 AssertReturn(cbPaths, VERR_INVALID_PARAMETER);
544
545 char **papszPaths = NULL;
546 size_t cPaths = 0;
547 int rc = RTStrSplit(pszPaths, cbPaths, pcszSeparator, &papszPaths, &cPaths);
548 if (RT_SUCCESS(rc))
549 rc = DnDTransferListAppendPathsFromArray(pList, enmFmt, papszPaths, cPaths, fFlags);
550
551 for (size_t i = 0; i < cPaths; ++i)
552 RTStrFree(papszPaths[i]);
553 RTMemFree(papszPaths);
554
555 return rc;
556}
557
558/**
559 * Appends paths to a transfer list.
560 *
561 * @returns VBox status code. Will return VERR_INVALID_PARAMETER if a common root path could not be found.
562 * @param pList Transfer list to append path to.
563 * @param enmFmt Format of \a papcszPaths to append.
564 * @param papcszPaths Array of paths to append.
565 * @param cPaths Number of paths in \a papcszPaths to append.
566 * @param fFlags Transfer list flags to use for appending.
567 */
568int DnDTransferListAppendPathsFromArray(PDNDTRANSFERLIST pList,
569 DNDTRANSFERLISTFMT enmFmt,
570 const char * const *papcszPaths, size_t cPaths, DNDTRANSFERLISTFLAGS fFlags)
571{
572 AssertPtrReturn(pList, VERR_INVALID_POINTER);
573 AssertPtrReturn(papcszPaths, VERR_INVALID_POINTER);
574 AssertReturn(!(fFlags & ~DNDTRANSFERLIST_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
575
576 int rc = VINF_SUCCESS;
577
578 if (!cPaths) /* Nothing to add? Bail out. */
579 return VINF_SUCCESS;
580
581 /* If we don't have a root path set, try to find the common path of all handed-in paths. */
582 if (!pList->pszPathRootAbs)
583 {
584 size_t cchRootPath = RTPathFindCommon(papcszPaths, cPaths);
585 if (cchRootPath)
586 {
587 /* Just use the first path in the array as the reference. */
588 char *pszRootPath = RTStrDupN(papcszPaths[0], cchRootPath);
589 if (pszRootPath)
590 {
591 LogRel2(("DnD: Determined root path is '%s'\n", pszRootPath));
592 rc = dndTransferListSetRootPath(pList, pszRootPath);
593 RTStrFree(pszRootPath);
594 }
595 else
596 rc = VERR_NO_MEMORY;
597 }
598 }
599
600 /*
601 * Go through the created list and make sure all entries have the same root path.
602 */
603 for (size_t i = 0; i < cPaths; i++)
604 {
605 rc = DnDTransferListAppendPath(pList, enmFmt, papcszPaths[i], fFlags);
606 if (RT_FAILURE(rc))
607 break;
608 }
609
610 LogFlowFuncLeaveRC(rc);
611 return rc;
612}
613
614/**
615 * Returns the first transfer object in a list.
616 *
617 * @returns Pointer to transfer object if found, or NULL if not found.
618 * @param pList Transfer list to get first transfer object from.
619 */
620PDNDTRANSFEROBJECT DnDTransferListObjGetFirst(PDNDTRANSFERLIST pList)
621{
622 AssertPtrReturn(pList, NULL);
623
624 return RTListGetFirst(&pList->lstObj, DNDTRANSFEROBJECT, Node);
625}
626
627/**
628 * Removes the first DnD transfer object from a transfer list.
629 *
630 * @param pList Transfer list to remove first entry for.
631 */
632void DnDTransferListObjRemoveFirst(PDNDTRANSFERLIST pList)
633{
634 AssertPtrReturnVoid(pList);
635
636 if (!pList->cObj)
637 return;
638
639 PDNDTRANSFEROBJECT pObj = RTListGetFirst(&pList->lstObj, DNDTRANSFEROBJECT, Node);
640 AssertPtr(pObj);
641
642 uint64_t cbSize = DnDTransferObjectGetSize(pObj);
643 Assert(pList->cbObjTotal >= cbSize);
644 pList->cbObjTotal -= cbSize; /* Adjust total size. */
645
646 dndTransferListObjFree(pList, pObj);
647}
648
649/**
650 * Returns all root entries of a transfer list as a string.
651 *
652 * @returns VBox status code.
653 * @param pList Transfer list to return root paths for.
654 * @param pcszPathBase Root path to use as a base path. If NULL, the list's absolute root path will be used (if any).
655 * @param pcszSeparator Separator to use for separating the root entries.
656 * @param ppszBuffer Where to return the allocated string on success. Needs to be free'd with RTStrFree().
657 * @param pcbBuffer Where to return the size (in bytes) of the allocated string on success, including terminator.
658 */
659int DnDTransferListGetRootsEx(PDNDTRANSFERLIST pList,
660 DNDTRANSFERLISTFMT enmFmt, const char *pcszPathBase, const char *pcszSeparator,
661 char **ppszBuffer, size_t *pcbBuffer)
662{
663 AssertPtrReturn(pList, VERR_INVALID_POINTER);
664 /* pcszPathBase can be NULL. */
665 AssertPtrReturn(pcszSeparator, VERR_INVALID_POINTER);
666 AssertPtrReturn(ppszBuffer, VERR_INVALID_POINTER);
667 AssertPtrReturn(pcbBuffer, VERR_INVALID_POINTER);
668
669 char *pszString = NULL;
670
671 /* Find out which root path to use. */
672 const char *pcszPathRootTmp = pcszPathBase ? pcszPathBase : pList->pszPathRootAbs;
673 /* pcszPathRootTmp can be NULL*/
674
675 LogFlowFunc(("Using root path '%s'\n", pcszPathRootTmp ? pcszPathRootTmp : "<None>"));
676
677 int rc = DnDPathValidate(pcszPathRootTmp, false /* fMustExist */);
678 if (RT_FAILURE(rc))
679 return rc;
680
681 char szPath[RTPATH_MAX];
682
683 PDNDTRANSFERLISTROOT pRoot;
684 RTListForEach(&pList->lstRoot, pRoot, DNDTRANSFERLISTROOT, Node)
685 {
686 if (pcszPathRootTmp)
687 {
688 rc = RTStrCopy(szPath, sizeof(szPath), pcszPathRootTmp);
689 AssertRCBreak(rc);
690 }
691
692 rc = RTPathAppend(szPath, sizeof(szPath), pRoot->pszPathRoot);
693 AssertRCBreak(rc);
694
695 if (enmFmt == DNDTRANSFERLISTFMT_URI)
696 {
697 char *pszPathURI = RTUriFileCreate(szPath);
698 AssertPtrBreakStmt(pszPathURI, rc = VERR_NO_MEMORY);
699
700 rc = RTStrAAppend(&pszString, pszPathURI);
701 RTStrFree(pszPathURI);
702 AssertRCBreak(rc);
703 }
704 else
705 {
706 rc = RTStrAAppend(&pszString, szPath);
707 AssertRCBreak(rc);
708 }
709
710 rc = RTStrAAppend(&pszString, pcszSeparator);
711 AssertRCBreak(rc);
712 }
713
714 if (RT_SUCCESS(rc))
715 {
716 *ppszBuffer = pszString;
717 *pcbBuffer = pszString ? strlen(pszString) + 1 /* Include termination */ : 0;
718 }
719 else
720 RTStrFree(pszString);
721 return rc;
722}
723
724int DnDTransferListGetRoots(PDNDTRANSFERLIST pList,
725 DNDTRANSFERLISTFMT enmFmt, char **ppszBuffer, size_t *pcbBuffer)
726{
727 return DnDTransferListGetRootsEx(pList, enmFmt, "" /* pcszPathRoot */, DND_PATH_SEPARATOR,
728 ppszBuffer, pcbBuffer);
729}
730
731/**
732 * Returns the total root entries count for a DnD transfer list.
733 *
734 * @returns Total number of root entries.
735 * @param pList Transfer list to return total number of root entries for.
736 */
737uint64_t DnDTransferListGetRootCount(PDNDTRANSFERLIST pList)
738{
739 AssertPtrReturn(pList, 0);
740 return pList->cRoots;
741}
742
743/**
744 * Returns the absolute root path for a DnD transfer list.
745 *
746 * @returns Pointer to the root path.
747 * @param pList Transfer list to return root path for.
748 */
749const char *DnDTransferListGetRootPathAbs(PDNDTRANSFERLIST pList)
750{
751 AssertPtrReturn(pList, NULL);
752 return pList->pszPathRootAbs;
753}
754
755/**
756 * Returns the total transfer object count for a DnD transfer list.
757 *
758 * @returns Total number of transfer objects.
759 * @param pList Transfer list to return total number of transfer objects for.
760 */
761uint64_t DnDTransferListObjCount(PDNDTRANSFERLIST pList)
762{
763 AssertPtrReturn(pList, 0);
764 return pList->cObj;
765}
766
767/**
768 * Returns the total bytes of all handled transfer objects for a DnD transfer list.
769 *
770 * @returns VBox status code.
771 * @param pList Transfer list to return total bytes for.
772 */
773uint64_t DnDTransferListObjTotalBytes(PDNDTRANSFERLIST pList)
774{
775 AssertPtrReturn(pList, 0);
776 return pList->cbObjTotal;
777}
778
779/**
780 * Sets the absolute root path of a transfer list.
781 *
782 * @returns VBox status code.
783 * @param pList Transfer list to set root path for.
784 * @param pcszRootPathAbs Absolute root path to set.
785 */
786static int dndTransferListSetRootPath(PDNDTRANSFERLIST pList, const char *pcszRootPathAbs)
787{
788 AssertPtrReturn(pList, VERR_INVALID_POINTER);
789 AssertPtrReturn(pcszRootPathAbs, VERR_INVALID_POINTER);
790 AssertReturn(pList->pszPathRootAbs == NULL, VERR_WRONG_ORDER); /* Already initialized? */
791
792 LogFlowFunc(("pcszRootPathAbs=%s\n", pcszRootPathAbs));
793
794 char szRootPath[RTPATH_MAX];
795 int rc = RTStrCopy(szRootPath, sizeof(szRootPath), pcszRootPathAbs);
796 if (RT_FAILURE(rc))
797 return rc;
798
799 /* Note: The list's root path is kept in native style, so no conversion needed here. */
800
801 RTPathEnsureTrailingSeparatorEx(szRootPath, sizeof(szRootPath), RTPATH_STR_F_STYLE_HOST);
802
803 pList->pszPathRootAbs = RTStrDup(szRootPath);
804 if (pList->pszPathRootAbs)
805 {
806 LogFlowFunc(("Root path is '%s'\n", pList->pszPathRootAbs));
807 }
808 else
809 rc = VERR_NO_MEMORY;
810
811 return rc;
812}
813
814static int dndTransferListRootAdd(PDNDTRANSFERLIST pList, const char *pcszRoot)
815{
816 int rc;
817
818 PDNDTRANSFERLISTROOT pRoot = (PDNDTRANSFERLISTROOT)RTMemAllocZ(sizeof(DNDTRANSFERLISTROOT));
819 if (pRoot)
820 {
821 pRoot->pszPathRoot = RTStrDup(pcszRoot);
822 if (pRoot->pszPathRoot)
823 {
824 RTListAppend(&pList->lstRoot, &pRoot->Node);
825 pList->cRoots++;
826
827 rc = VINF_SUCCESS;
828 }
829 else
830 rc = VERR_NO_MEMORY;
831
832 if (RT_FAILURE(rc))
833 {
834 RTMemFree(pRoot);
835 pRoot = NULL;
836 }
837 }
838 else
839 rc = VERR_NO_MEMORY;
840
841 return rc;
842}
843
844/**
845 * Frees an internal DnD transfer root.
846 *
847 * @param pList Transfer list to free root for.
848 * @param pRootObj Transfer list root to free. The pointer will be invalid after calling.
849 */
850static void dndTransferListRootFree(PDNDTRANSFERLIST pList, PDNDTRANSFERLISTROOT pRootObj)
851{
852 if (!pRootObj)
853 return;
854
855 RTStrFree(pRootObj->pszPathRoot);
856
857 RTListNodeRemove(&pRootObj->Node);
858 RTMemFree(pRootObj);
859
860 AssertReturnVoid(pList->cRoots);
861 pList->cRoots--;
862}
863
864
865#if 0
866/**
867 * Appends a single URI path to a transfer list.
868 *
869 * @returns VBox status code.
870 * @param pList Transfer list to append URI path to.
871 * @param pszURIPath URI path to append.
872 * @param fFlags Transfer list flags to use for appending.
873 */
874int DnDTransferListURIAppendPath(PDNDTRANSFERLIST pList, const char *pszURIPath, DNDTRANSFERLISTFLAGS fFlags)
875{
876 AssertPtrReturn(pList, VERR_INVALID_POINTER);
877 AssertPtrReturn(pszURIPath, VERR_INVALID_POINTER);
878 AssertReturn(!(fFlags & ~DNDTRANSFERLIST_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
879
880 in rc;
881
882 /* Query the path component of a file URI. If this hasn't a
883 * file scheme, NULL is returned. */
884 char *pszFilePath = RTUriFilePathEx(pszURIPath, RTPATH_STR_F_STYLE_UNIX, &pszFilePath, 0 /*cbPath*/, NULL /*pcchPath*/);
885 LogFlowFunc(("pszPath=%s, pszFilePath=%s\n", pszFilePath));
886 if (pszFilePath)
887 {
888 rc = DnDPathValidate(pszFilePath, false /* fMustExist */);
889 if (RT_SUCCESS(rc))
890 {
891 uint32_t fPathConvert = DNDPATHCONVERT_FLAGS_TRANSPORT;
892#ifdef RT_OS_WINDOWS
893 fPathConvert |= DNDPATHCONVERT_FLAGS_TO_DOS;
894#endif
895 rc = DnDPathConvert(pszFilePath, strlen(pszFilePath) + 1, fPathConvert);
896 if (RT_SUCCESS(rc))
897 {
898 LogRel2(("DnD: Got URI data item '%s'\n", pszFilePath));
899
900 PDNDTRANSFERLISTROOT pRoot = (PDNDTRANSFERLISTROOT)RTMemAlloc(sizeof(DNDTRANSFERLISTROOT));
901 if (pRoot)
902 {
903 pRoot->pszPathRoot = pszFilePath;
904
905 RTListAppend(&pList->lstRoot, &pRoot->Node);
906 pList->cRoots++;
907
908 }
909 else
910 rc = VERR_NO_MEMORY;
911 }
912 else
913 LogRel(("DnD: Path conversion of URI data item '%s' failed with %Rrc\n", pszFilePath, rc));
914 }
915 else
916 LogRel(("DnD: Path validation for URI data item '%s' failed with %Rrc\n", pszFilePath, rc));
917
918 if (RT_FAILURE(rc))
919 RTStrFree(pszFilePath);
920 }
921
922 LogFlowFuncLeaveRC(rc);
923 return rc;
924}
925
926/**
927 * Appends transfer list items from an URI string buffer.
928 *
929 * @returns VBox status code.
930 * @param pList Transfer list to append list data to.
931 * @param pszURIPaths String list to append.
932 * @param cbURIPaths Size (in bytes) of string list to append.
933 * @param pcszSeparator Separator string to use for separating strings of \a pszURIPathsAbs.
934 * @param fFlags Transfer list flags to use for appending.
935 */
936int DnDTransferListURIAppendFromBuffer(PDNDTRANSFERLIST pList,
937 const char *pszURIPaths, size_t cbURIPaths,
938 const char *pcszSeparator, DNDTRANSFERLISTFLAGS fFlags)
939{
940 AssertPtrReturn(pList, VERR_INVALID_POINTER);
941 AssertPtrReturn(pszURIPaths, VERR_INVALID_POINTER);
942 AssertReturn(cbURIPaths, VERR_INVALID_PARAMETER);
943 AssertReturn(!(fFlags & ~DNDTRANSFERLIST_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
944
945 char **papszPaths = NULL;
946 size_t cPaths = 0;
947 int rc = RTStrSplit(pszURIPaths, cbURIPaths, pcszSeparator, &papszPaths, &cPaths);
948 if (RT_SUCCESS(rc))
949 {
950 for (size_t i = 0; i < cPaths; i++)
951 {
952 rc = DnDTransferListURIAppendPath(pList, papszPaths[i], fFlags);
953 if (RT_FAILURE(rc))
954 break;
955 }
956
957 for (size_t i = 0; i < cPaths; ++i)
958 RTStrFree(papszPaths[i]);
959 RTMemFree(papszPaths);
960 }
961
962 LogFlowFuncLeaveRC(rc);
963 return rc;
964}
965#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