VirtualBox

source: vbox/trunk/src/VBox/GuestHost/SharedClipboard/ClipboardURIList.cpp@ 78307

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

Shared Clipboard/URI: First commit; work in progress.

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