VirtualBox

source: vbox/trunk/src/VBox/GuestHost/DragAndDrop/DnDURIList.cpp@ 76678

Last change on this file since 76678 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.1 KB
Line 
1/* $Id: DnDURIList.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * DnD - URI list class.
4 */
5
6/*
7 * Copyright (C) 2014-2019 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/path.h>
30#include <iprt/string.h>
31#include <iprt/symlink.h>
32#include <iprt/uri.h>
33
34#include <VBox/log.h>
35
36
37DnDURIList::DnDURIList(void)
38 : m_cTotal(0)
39 , m_cbTotal(0)
40{
41}
42
43DnDURIList::~DnDURIList(void)
44{
45 Clear();
46}
47
48int DnDURIList::addEntry(const char *pcszSource, const char *pcszTarget, DNDURILISTFLAGS fFlags)
49{
50 AssertPtrReturn(pcszSource, VERR_INVALID_POINTER);
51 AssertPtrReturn(pcszTarget, VERR_INVALID_POINTER);
52
53 LogFlowFunc(("pcszSource=%s, pcszTarget=%s, fFlags=0x%x\n", pcszSource, pcszTarget, fFlags));
54
55 RTFSOBJINFO objInfo;
56 int rc = RTPathQueryInfo(pcszSource, &objInfo, RTFSOBJATTRADD_NOTHING);
57 if (RT_SUCCESS(rc))
58 {
59 if (RTFS_IS_FILE(objInfo.Attr.fMode))
60 {
61 LogFlowFunc(("File '%s' -> '%s' (%RU64 bytes, file mode 0x%x)\n",
62 pcszSource, pcszTarget, (uint64_t)objInfo.cbObject, objInfo.Attr.fMode));
63
64 DnDURIObject *pObjFile = new DnDURIObject(DnDURIObject::Type_File, pcszSource, pcszTarget);
65 if (pObjFile)
66 {
67 if (fFlags & DNDURILIST_FLAGS_KEEP_OPEN) /* Shall we keep the file open while being added to this list? */
68 {
69 /** @todo Add a standard fOpen mode for this list. */
70 rc = pObjFile->Open(DnDURIObject::View_Source, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
71 }
72 else /* Just query the information without opening the file. */
73 rc = pObjFile->QueryInfo(DnDURIObject::View_Source);
74
75 if (RT_SUCCESS(rc))
76 {
77 m_lstTree.append(pObjFile);
78
79 m_cTotal++;
80 m_cbTotal += pObjFile->GetSize();
81 }
82 else
83 delete pObjFile;
84 }
85 else
86 rc = VERR_NO_MEMORY;
87 }
88 else if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
89 {
90 LogFlowFunc(("Directory '%s' -> '%s' (file mode 0x%x)\n", pcszSource, pcszTarget, objInfo.Attr.fMode));
91
92 DnDURIObject *pObjDir = new DnDURIObject(DnDURIObject::Type_Directory, pcszSource, pcszTarget);
93 if (pObjDir)
94 {
95 m_lstTree.append(pObjDir);
96
97 /** @todo Add DNDURILIST_FLAGS_KEEP_OPEN handling? */
98 m_cTotal++;
99 }
100 else
101 rc = VERR_NO_MEMORY;
102 }
103 /* Note: Symlinks already should have been resolved at this point. */
104 else
105 rc = VERR_NOT_SUPPORTED;
106 }
107
108 LogFlowFuncLeaveRC(rc);
109 return rc;
110}
111
112int DnDURIList::appendPathRecursive(const char *pcszSrcPath,
113 const char *pcszDstPath, const char *pcszDstBase, size_t cchDstBase,
114 DNDURILISTFLAGS fFlags)
115{
116 AssertPtrReturn(pcszSrcPath, VERR_INVALID_POINTER);
117 AssertPtrReturn(pcszDstBase, VERR_INVALID_POINTER);
118 AssertPtrReturn(pcszDstPath, VERR_INVALID_POINTER);
119
120 LogFlowFunc(("pcszSrcPath=%s, pcszDstPath=%s, pcszDstBase=%s, cchDstBase=%zu, fFlags=0x%x\n",
121 pcszSrcPath, pcszDstPath, pcszDstBase, cchDstBase, fFlags));
122
123 RTFSOBJINFO objInfo;
124 int rc = RTPathQueryInfo(pcszSrcPath, &objInfo, RTFSOBJATTRADD_NOTHING);
125 if (RT_SUCCESS(rc))
126 {
127 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
128 {
129 rc = addEntry(pcszSrcPath, &pcszDstPath[cchDstBase], fFlags);
130 if (RT_SUCCESS(rc))
131 {
132 RTDIR hDir;
133 rc = RTDirOpen(&hDir, pcszSrcPath);
134 if (RT_SUCCESS(rc))
135 {
136 size_t cbDirEntry = 0;
137 PRTDIRENTRYEX pDirEntry = NULL;
138 do
139 {
140 /* Retrieve the next directory entry. */
141 rc = RTDirReadExA(hDir, &pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
142 if (RT_FAILURE(rc))
143 {
144 if (rc == VERR_NO_MORE_FILES)
145 rc = VINF_SUCCESS;
146 break;
147 }
148
149 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
150 {
151 case RTFS_TYPE_DIRECTORY:
152 {
153 /* Skip "." and ".." entries. */
154 if (RTDirEntryExIsStdDotLink(pDirEntry))
155 break;
156
157 char *pszSrc = RTPathJoinA(pcszSrcPath, pDirEntry->szName);
158 if (pszSrc)
159 {
160 char *pszDst = RTPathJoinA(pcszDstPath, pDirEntry->szName);
161 if (pszDst)
162 {
163 rc = appendPathRecursive(pszSrc, pszDst, pcszDstBase, cchDstBase, fFlags);
164 RTStrFree(pszDst);
165 }
166 else
167 rc = VERR_NO_MEMORY;
168
169 RTStrFree(pszSrc);
170 }
171 else
172 rc = VERR_NO_MEMORY;
173 break;
174 }
175
176 case RTFS_TYPE_FILE:
177 {
178 char *pszSrc = RTPathJoinA(pcszSrcPath, pDirEntry->szName);
179 if (pszSrc)
180 {
181 char *pszDst = RTPathJoinA(pcszDstPath, pDirEntry->szName);
182 if (pszDst)
183 {
184 rc = addEntry(pszSrc, &pszDst[cchDstBase], fFlags);
185 RTStrFree(pszDst);
186 }
187 else
188 rc = VERR_NO_MEMORY;
189 RTStrFree(pszSrc);
190 }
191 else
192 rc = VERR_NO_MEMORY;
193 break;
194 }
195 case RTFS_TYPE_SYMLINK:
196 {
197 if (fFlags & DNDURILIST_FLAGS_RESOLVE_SYMLINKS)
198 {
199 char *pszSrc = RTPathRealDup(pcszDstBase);
200 if (pszSrc)
201 {
202 rc = RTPathQueryInfo(pszSrc, &objInfo, RTFSOBJATTRADD_NOTHING);
203 if (RT_SUCCESS(rc))
204 {
205 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
206 {
207 LogFlowFunc(("Directory entry is symlink to directory\n"));
208 rc = appendPathRecursive(pszSrc, pcszDstPath, pcszDstBase, cchDstBase, fFlags);
209 }
210 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
211 {
212 LogFlowFunc(("Directory entry is symlink to file\n"));
213 rc = addEntry(pszSrc, &pcszDstPath[cchDstBase], fFlags);
214 }
215 else
216 rc = VERR_NOT_SUPPORTED;
217 }
218
219 RTStrFree(pszSrc);
220 }
221 else
222 rc = VERR_NO_MEMORY;
223 }
224 break;
225 }
226
227 default:
228 break;
229 }
230
231 } while (RT_SUCCESS(rc));
232
233 RTDirReadExAFree(&pDirEntry, &cbDirEntry);
234 RTDirClose(hDir);
235 }
236 }
237 }
238 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
239 {
240 rc = addEntry(pcszSrcPath, &pcszDstPath[cchDstBase], fFlags);
241 }
242 else if (RTFS_IS_SYMLINK(objInfo.Attr.fMode))
243 {
244 if (fFlags & DNDURILIST_FLAGS_RESOLVE_SYMLINKS)
245 {
246 char *pszSrc = RTPathRealDup(pcszSrcPath);
247 if (pszSrc)
248 {
249 rc = RTPathQueryInfo(pszSrc, &objInfo, RTFSOBJATTRADD_NOTHING);
250 if (RT_SUCCESS(rc))
251 {
252 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
253 {
254 LogFlowFunc(("Symlink to directory\n"));
255 rc = appendPathRecursive(pszSrc, pcszDstPath, pcszDstBase, cchDstBase, fFlags);
256 }
257 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
258 {
259 LogFlowFunc(("Symlink to file\n"));
260 rc = addEntry(pszSrc, &pcszDstPath[cchDstBase], fFlags);
261 }
262 else
263 rc = VERR_NOT_SUPPORTED;
264 }
265
266 RTStrFree(pszSrc);
267 }
268 else
269 rc = VERR_NO_MEMORY;
270 }
271 }
272 else
273 rc = VERR_NOT_SUPPORTED;
274 }
275
276 LogFlowFuncLeaveRC(rc);
277 return rc;
278}
279
280int DnDURIList::AppendNativePath(const char *pszPath, DNDURILISTFLAGS fFlags)
281{
282 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
283
284 int rc;
285 char *pszPathNative = RTStrDup(pszPath);
286 if (pszPathNative)
287 {
288 RTPathChangeToUnixSlashes(pszPathNative, true /* fForce */);
289
290 char *pszPathURI = RTUriCreate("file" /* pszScheme */, NULL /* pszAuthority */,
291 pszPathNative, NULL /* pszQuery */, NULL /* pszFragment */);
292 if (pszPathURI)
293 {
294 rc = AppendURIPath(pszPathURI, fFlags);
295 RTStrFree(pszPathURI);
296 }
297 else
298 rc = VERR_INVALID_PARAMETER;
299
300 RTStrFree(pszPathNative);
301 }
302 else
303 rc = VERR_NO_MEMORY;
304
305 return rc;
306}
307
308int DnDURIList::AppendNativePathsFromList(const char *pszNativePaths, size_t cbNativePaths,
309 DNDURILISTFLAGS fFlags)
310{
311 AssertPtrReturn(pszNativePaths, VERR_INVALID_POINTER);
312 AssertReturn(cbNativePaths, VERR_INVALID_PARAMETER);
313
314 RTCList<RTCString> lstPaths
315 = RTCString(pszNativePaths, cbNativePaths - 1).split("\r\n");
316 return AppendNativePathsFromList(lstPaths, fFlags);
317}
318
319int DnDURIList::AppendNativePathsFromList(const RTCList<RTCString> &lstNativePaths,
320 DNDURILISTFLAGS fFlags)
321{
322 int rc = VINF_SUCCESS;
323
324 for (size_t i = 0; i < lstNativePaths.size(); i++)
325 {
326 const RTCString &strPath = lstNativePaths.at(i);
327 rc = AppendNativePath(strPath.c_str(), fFlags);
328 if (RT_FAILURE(rc))
329 break;
330 }
331
332 LogFlowFuncLeaveRC(rc);
333 return rc;
334}
335
336int DnDURIList::AppendURIPath(const char *pszURI, DNDURILISTFLAGS fFlags)
337{
338 AssertPtrReturn(pszURI, VERR_INVALID_POINTER);
339 AssertReturn(!(fFlags & ~DNDURILIST_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
340 /** @todo Check for string termination? */
341
342 RTURIPARSED Parsed;
343 int rc = RTUriParse(pszURI, &Parsed);
344 if (RT_FAILURE(rc))
345 return rc;
346
347 char *pszSrcPath = NULL;
348
349 /* file://host.example.com/path/to/file.txt */
350 const char *pszParsedAuthority = RTUriParsedAuthority(pszURI, &Parsed);
351 if ( pszParsedAuthority
352 && pszParsedAuthority[0] != '\0') /* Authority present? */
353 {
354 const char *pszParsedPath = RTUriParsedPath(pszURI, &Parsed);
355 if (pszParsedPath)
356 {
357 /* Always use UNIXy paths internally. */
358 if (RTStrAPrintf(&pszSrcPath, "//%s%s", pszParsedAuthority, pszParsedPath) == -1)
359 rc = VERR_NO_MEMORY;
360 }
361 else
362 rc = VERR_INVALID_PARAMETER;
363 }
364 else
365 {
366 pszSrcPath = RTUriFilePath(pszURI);
367 if (!pszSrcPath)
368 rc = VERR_INVALID_PARAMETER;
369 }
370
371 LogFlowFunc(("pszURI=%s, fFlags=0x%x -> pszParsedAuthority=%s, pszSrcPath=%s, rc=%Rrc\n",
372 pszURI, fFlags, pszParsedAuthority ? pszParsedAuthority : "<None>", pszSrcPath, rc));
373
374 if (RT_SUCCESS(rc))
375 {
376 /* Add the path to our internal file list (recursive in
377 * the case of a directory). */
378 size_t cbPathLen = RTPathStripTrailingSlash(pszSrcPath);
379 if (cbPathLen)
380 {
381 char *pszFileName = RTPathFilename(pszSrcPath);
382 if (pszFileName)
383 {
384 Assert(pszFileName >= pszSrcPath);
385 size_t cchDstBase = (fFlags & DNDURILIST_FLAGS_ABSOLUTE_PATHS)
386 ? 0 /* Use start of path as root. */
387 : pszFileName - pszSrcPath;
388 char *pszDstPath = &pszSrcPath[cchDstBase];
389 m_lstRoot.append(pszDstPath);
390
391 LogFlowFunc(("pszSrcPath=%s, pszFileName=%s, pszRoot=%s\n",
392 pszSrcPath, pszFileName, pszDstPath));
393
394 rc = appendPathRecursive(pszSrcPath, pszSrcPath, pszSrcPath, cchDstBase, fFlags);
395 }
396 else
397 rc = VERR_PATH_NOT_FOUND;
398 }
399 else
400 rc = VERR_INVALID_PARAMETER;
401 }
402
403 RTStrFree(pszSrcPath);
404
405 LogFlowFuncLeaveRC(rc);
406 return rc;
407}
408
409int DnDURIList::AppendURIPathsFromList(const char *pszURIPaths, size_t cbURIPaths,
410 DNDURILISTFLAGS fFlags)
411{
412 AssertPtrReturn(pszURIPaths, VERR_INVALID_POINTER);
413 AssertReturn(cbURIPaths, VERR_INVALID_PARAMETER);
414
415 RTCList<RTCString> lstPaths
416 = RTCString(pszURIPaths, cbURIPaths - 1).split("\r\n");
417 return AppendURIPathsFromList(lstPaths, fFlags);
418}
419
420int DnDURIList::AppendURIPathsFromList(const RTCList<RTCString> &lstURI,
421 DNDURILISTFLAGS fFlags)
422{
423 int rc = VINF_SUCCESS;
424
425 for (size_t i = 0; i < lstURI.size(); i++)
426 {
427 RTCString strURI = lstURI.at(i);
428 rc = AppendURIPath(strURI.c_str(), fFlags);
429
430 if (RT_FAILURE(rc))
431 break;
432 }
433
434 LogFlowFuncLeaveRC(rc);
435 return rc;
436}
437
438void DnDURIList::Clear(void)
439{
440 m_lstRoot.clear();
441
442 for (size_t i = 0; i < m_lstTree.size(); i++)
443 {
444 DnDURIObject *pCurObj = m_lstTree.at(i);
445 AssertPtr(pCurObj);
446 RTMemFree(pCurObj);
447 }
448 m_lstTree.clear();
449
450 m_cTotal = 0;
451 m_cbTotal = 0;
452}
453
454void DnDURIList::RemoveFirst(void)
455{
456 if (m_lstTree.isEmpty())
457 return;
458
459 DnDURIObject *pCurObj = m_lstTree.first();
460 AssertPtr(pCurObj);
461
462 uint64_t cbSize = pCurObj->GetSize();
463 Assert(m_cbTotal >= cbSize);
464 m_cbTotal -= cbSize; /* Adjust total size. */
465
466 pCurObj->Close();
467 RTMemFree(pCurObj);
468
469 m_lstTree.removeFirst();
470}
471
472int DnDURIList::SetFromURIData(const void *pvData, size_t cbData, DNDURILISTFLAGS fFlags)
473{
474 Assert(fFlags == 0); RT_NOREF1(fFlags);
475 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
476 AssertReturn(cbData, VERR_INVALID_PARAMETER);
477
478 if (!RTStrIsValidEncoding(static_cast<const char *>(pvData)))
479 return VERR_INVALID_PARAMETER;
480
481 RTCList<RTCString> lstURI =
482 RTCString(static_cast<const char *>(pvData), cbData - 1).split("\r\n");
483 if (lstURI.isEmpty())
484 return VINF_SUCCESS;
485
486 int rc = VINF_SUCCESS;
487
488 for (size_t i = 0; i < lstURI.size(); ++i)
489 {
490 /* Query the path component of a file URI. If this hasn't a
491 * file scheme, NULL is returned. */
492 const char *pszURI = lstURI.at(i).c_str();
493 char *pszFilePath = RTUriFilePath(pszURI);
494#ifdef DEBUG_andy
495 LogFlowFunc(("pszURI=%s, pszFilePath=%s\n", pszURI, pszFilePath));
496#endif
497 if (pszFilePath)
498 {
499 rc = DnDPathSanitize(pszFilePath, strlen(pszFilePath));
500 if (RT_SUCCESS(rc))
501 {
502 m_lstRoot.append(pszFilePath);
503 m_cTotal++;
504 }
505
506 RTStrFree(pszFilePath);
507 }
508 else
509 rc = VERR_INVALID_PARAMETER;
510
511 if (RT_FAILURE(rc))
512 break;
513 }
514
515 return rc;
516}
517
518RTCString DnDURIList::GetRootEntries(const RTCString &strPathBase /* = "" */,
519 const RTCString &strSeparator /* = "\r\n" */) const
520{
521 RTCString strRet;
522 for (size_t i = 0; i < m_lstRoot.size(); i++)
523 {
524 const char *pszCurRoot = m_lstRoot.at(i).c_str();
525#ifdef DEBUG_andy
526 LogFlowFunc(("pszCurRoot=%s\n", pszCurRoot));
527#endif
528 if (strPathBase.isNotEmpty())
529 {
530 char *pszPath = RTPathJoinA(strPathBase.c_str(), pszCurRoot);
531 if (pszPath)
532 {
533 char *pszPathURI = RTUriFileCreate(pszPath);
534 if (pszPathURI)
535 {
536 strRet += RTCString(pszPathURI) + strSeparator;
537 LogFlowFunc(("URI (Base): %s\n", strRet.c_str()));
538 RTStrFree(pszPathURI);
539 }
540
541 RTStrFree(pszPath);
542
543 if (!pszPathURI)
544 break;
545 }
546 else
547 break;
548 }
549 else
550 {
551 char *pszPathURI = RTUriFileCreate(pszCurRoot);
552 if (pszPathURI)
553 {
554 strRet += RTCString(pszPathURI) + strSeparator;
555 LogFlowFunc(("URI: %s\n", strRet.c_str()));
556 RTStrFree(pszPathURI);
557 }
558 else
559 break;
560 }
561 }
562
563 return strRet;
564}
565
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