VirtualBox

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

Last change on this file since 50610 was 50610, checked in by vboxsync, 11 years ago

DnD/DnDURIList: RTStrAPrintf2 -> RTPathJoinA.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.6 KB
Line 
1/* $Id: DnDURIList.cpp 50610 2014-02-26 14:35:56Z vboxsync $ */
2/** @file
3 * DnD: URI list class.
4 */
5
6/*
7 * Copyright (C) 2014 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 * Header Files *
20 ******************************************************************************/
21
22#include <iprt/dir.h>
23#include <iprt/file.h>
24#include <iprt/fs.h>
25#include <iprt/path.h>
26#include <iprt/uri.h>
27
28#ifdef LOG_GROUP
29 #undef LOG_GROUP
30#endif
31#define LOG_GROUP LOG_GROUP_GUEST_DND
32#include <VBox/log.h>
33
34#include <VBox/GuestHost/DragAndDrop.h>
35
36DnDURIObject::DnDURIObject(Type type,
37 const RTCString &strSrcPath,
38 const RTCString &strDstPath,
39 uint32_t fMode, uint64_t cbSize)
40 : m_Type(type)
41 , m_strSrcPath(strSrcPath)
42 , m_strDstPath(strDstPath)
43 , m_fMode(fMode)
44 , m_cbSize(cbSize)
45 , m_cbProcessed(0)
46{
47 RT_ZERO(u);
48}
49
50DnDURIObject::~DnDURIObject(void)
51{
52 closeInternal();
53}
54
55void DnDURIObject::closeInternal(void)
56{
57 if (m_Type == File)
58 {
59 if (u.m_hFile)
60 {
61 RTFileClose(u.m_hFile);
62 u.m_hFile = NULL;
63 }
64 }
65}
66
67bool DnDURIObject::IsComplete(void) const
68{
69 bool fComplete = false;
70
71 Assert(m_cbProcessed <= m_cbSize);
72 if (m_cbProcessed == m_cbSize)
73 fComplete = true;
74
75 switch (m_Type)
76 {
77 case File:
78 if (!fComplete)
79 fComplete = !u.m_hFile;
80 break;
81
82 case Directory:
83 fComplete = true;
84 break;
85
86 default:
87 break;
88 }
89
90 return fComplete;
91}
92
93/* static */
94/** @todo Put this into an own class like DnDURIPath : public RTCString? */
95int DnDURIObject::RebaseURIPath(RTCString &strPath,
96 const RTCString &strBaseOld,
97 const RTCString &strBaseNew)
98{
99 int rc;
100 const char *pszPath = RTUriPath(strPath.c_str());
101 if (pszPath)
102 {
103 const char *pszPathStart = pszPath;
104 const char *pszBaseOld = strBaseOld.c_str();
105 if ( pszBaseOld
106 && RTPathStartsWith(pszPath, pszBaseOld))
107 {
108 pszPathStart += strlen(pszBaseOld);
109 }
110
111 rc = VINF_SUCCESS;
112
113 if (RT_SUCCESS(rc))
114 {
115 char *pszPathNew = RTPathJoinA(strBaseNew.c_str(), pszPathStart);
116 if (pszPathNew)
117 {
118 char *pszPathURI = RTUriCreate("file" /* pszScheme */, "/" /* pszAuthority */,
119 pszPathNew /* pszPath */,
120 NULL /* pszQuery */, NULL /* pszFragment */);
121 if (pszPathURI)
122 {
123#ifdef DEBUG_andy
124 LogFlowFunc(("Rebasing \"%s\" to \"%s\"", strPath.c_str(), pszPathURI));
125#endif
126 strPath = RTCString(pszPathURI) + "\r\n";
127 RTStrFree(pszPathURI);
128
129 rc = VINF_SUCCESS;
130 }
131 else
132 rc = VERR_INVALID_PARAMETER;
133
134 RTStrFree(pszPathNew);
135 }
136 else
137 rc = VERR_NO_MEMORY;
138 }
139 }
140 else
141 rc = VERR_INVALID_PARAMETER;
142
143#ifdef DEBUG_andy
144 LogFlowFuncLeaveRC(rc);
145#endif
146 return rc;
147}
148
149int DnDURIObject::Read(void *pvBuf, uint32_t cbToRead, uint32_t *pcbRead)
150{
151 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
152 AssertReturn(cbToRead, VERR_INVALID_PARAMETER);
153 /* pcbRead is optional. */
154
155 int rc;
156 switch (m_Type)
157 {
158 case File:
159 {
160 if (!u.m_hFile)
161 {
162 /* Open files on the source with RTFILE_O_DENY_WRITE to prevent races
163 * where the OS writes to the file while the destination side transfers
164 * it over. */
165 rc = RTFileOpen(&u.m_hFile, m_strSrcPath.c_str(),
166 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
167 }
168 else
169 rc = VINF_SUCCESS;
170
171 bool fDone = false;
172 if (RT_SUCCESS(rc))
173 {
174 size_t cbRead;
175 rc = RTFileRead(u.m_hFile, pvBuf, cbToRead, &cbRead);
176 if (RT_SUCCESS(rc))
177 {
178 if (pcbRead)
179 *pcbRead = (uint32_t)cbRead;
180
181 m_cbProcessed += cbRead;
182 Assert(m_cbProcessed <= m_cbSize);
183
184 /* End of file reached or error occurred? */
185 if ( m_cbProcessed == m_cbSize
186 || RT_FAILURE(rc))
187 {
188 closeInternal();
189 }
190 }
191 }
192
193 break;
194 }
195
196 case Directory:
197 {
198 rc = VINF_SUCCESS;
199 break;
200 }
201
202 default:
203 rc = VERR_NOT_IMPLEMENTED;
204 break;
205 }
206
207 LogFlowFunc(("Returning strSourcePath=%s, rc=%Rrc\n",
208 m_strSrcPath.c_str(), rc));
209 return rc;
210}
211
212/*** */
213
214DnDURIList::DnDURIList(void)
215 : m_cbTotal(0)
216{
217}
218
219DnDURIList::~DnDURIList(void)
220{
221}
222
223int DnDURIList::appendPathRecursive(const char *pcszPath, size_t cbBaseLen,
224 uint32_t fFlags)
225{
226 AssertPtrReturn(pcszPath, VERR_INVALID_POINTER);
227
228 RTFSOBJINFO objInfo;
229 int rc = RTPathQueryInfo(pcszPath, &objInfo, RTFSOBJATTRADD_NOTHING);
230 if (RT_FAILURE(rc))
231 return rc;
232
233 /*
234 * These are the types we currently support. Symlinks are not directly
235 * supported. First the guest could be an OS which doesn't support it and
236 * second the symlink could point to a file which is out of the base tree.
237 * Both things are hard to support. For now we just copy the target file in
238 * this case.
239 */
240 if (!( RTFS_IS_DIRECTORY(objInfo.Attr.fMode)
241 || RTFS_IS_FILE(objInfo.Attr.fMode)
242 || RTFS_IS_SYMLINK(objInfo.Attr.fMode)))
243 return VINF_SUCCESS;
244
245 uint64_t cbSize = 0;
246 rc = RTFileQuerySize(pcszPath, &cbSize);
247 if (rc == VERR_IS_A_DIRECTORY)
248 rc = VINF_SUCCESS;
249
250 if (RT_FAILURE(rc))
251 return rc;
252
253 m_lstTree.append(DnDURIObject( RTFS_IS_DIRECTORY(objInfo.Attr.fMode)
254 ? DnDURIObject::Directory
255 : DnDURIObject::File,
256 pcszPath, &pcszPath[cbBaseLen],
257 objInfo.Attr.fMode, cbSize));
258 m_cbTotal += cbSize;
259#ifdef DEBUG_andy
260 LogFlowFunc(("strSrcPath=%s, strDstPath=%s, fMode=0x%x, cbSize=%RU64, cbTotal=%zu\n",
261 pcszPath, &pcszPath[cbBaseLen], objInfo.Attr.fMode, cbSize, m_cbTotal));
262#endif
263
264 PRTDIR hDir;
265 /* We have to try to open even symlinks, cause they could
266 * be symlinks to directories. */
267 rc = RTDirOpen(&hDir, pcszPath);
268 /* The following error happens when this was a symlink
269 * to an file or a regular file. */
270 if ( rc == VERR_PATH_NOT_FOUND
271 || rc == VERR_NOT_A_DIRECTORY)
272 return VINF_SUCCESS;
273 if (RT_FAILURE(rc))
274 return rc;
275
276 while (RT_SUCCESS(rc))
277 {
278 RTDIRENTRY DirEntry;
279 rc = RTDirRead(hDir, &DirEntry, NULL);
280 if (RT_FAILURE(rc))
281 {
282 if (rc == VERR_NO_MORE_FILES)
283 rc = VINF_SUCCESS;
284 break;
285 }
286 switch (DirEntry.enmType)
287 {
288 case RTDIRENTRYTYPE_DIRECTORY:
289 {
290 /* Skip "." and ".." entries. */
291 if ( RTStrCmp(DirEntry.szName, ".") == 0
292 || RTStrCmp(DirEntry.szName, "..") == 0)
293 break;
294
295 char *pszRecDir = RTPathJoinA(pcszPath, DirEntry.szName);
296 if (pszRecDir)
297 {
298 rc = appendPathRecursive(pszRecDir, cbBaseLen, fFlags);
299 RTStrFree(pszRecDir);
300 }
301 else
302 rc = VERR_NO_MEMORY;
303 break;
304 }
305 case RTDIRENTRYTYPE_SYMLINK:
306 case RTDIRENTRYTYPE_FILE:
307 {
308 char *pszNewFile = RTPathJoinA(pcszPath, DirEntry.szName);
309 if (pszNewFile)
310 {
311 /* We need the size and the mode of the file. */
312 RTFSOBJINFO objInfo1;
313 rc = RTPathQueryInfo(pszNewFile, &objInfo1, RTFSOBJATTRADD_NOTHING);
314 if (RT_FAILURE(rc))
315 return rc;
316 rc = RTFileQuerySize(pszNewFile, &cbSize);
317 if (RT_FAILURE(rc))
318 break;
319
320 m_lstTree.append(DnDURIObject(DnDURIObject::File,
321 pszNewFile, &pszNewFile[cbBaseLen],
322 objInfo1.Attr.fMode, cbSize));
323 m_cbTotal += cbSize;
324#ifdef DEBUG_andy
325 LogFlowFunc(("strSrcPath=%s, strDstPath=%s, fMode=0x%x, cbSize=%RU64, cbTotal=%zu\n",
326 pszNewFile, &pszNewFile[cbBaseLen], objInfo1.Attr.fMode, cbSize, m_cbTotal));
327#endif
328 RTStrFree(pszNewFile);
329 }
330 else
331 rc = VERR_NO_MEMORY;
332 break;
333 }
334
335 default:
336 break;
337 }
338 }
339
340 RTDirClose(hDir);
341 return rc;
342}
343
344int DnDURIList::AppendNativePath(const char *pszPath, uint32_t fFlags)
345{
346 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
347
348 int rc;
349 char *pszPathNative = RTStrDup(pszPath);
350 if (pszPathNative)
351 {
352 RTPathChangeToUnixSlashes(pszPathNative, true /* fForce */);
353
354 char *pszPathURI = RTUriCreate("file" /* pszScheme */, "/" /* pszAuthority */,
355 pszPathNative, NULL /* pszQuery */, NULL /* pszFragment */);
356 if (pszPathURI)
357 {
358 rc = AppendURIPath(pszPathURI, fFlags);
359 RTStrFree(pszPathURI);
360 }
361 else
362 rc = VERR_INVALID_PARAMETER;
363
364 RTStrFree(pszPathNative);
365 }
366 else
367 rc = VERR_NO_MEMORY;
368
369 return rc;
370}
371
372int DnDURIList::AppendNativePathsFromList(const char *pszNativePaths, size_t cbNativePaths,
373 uint32_t fFlags)
374{
375 AssertPtrReturn(pszNativePaths, VERR_INVALID_POINTER);
376 AssertReturn(cbNativePaths, VERR_INVALID_PARAMETER);
377
378 RTCList<RTCString> lstPaths
379 = RTCString(pszNativePaths, cbNativePaths - 1).split("\r\n");
380 return AppendNativePathsFromList(lstPaths, fFlags);
381}
382
383int DnDURIList::AppendNativePathsFromList(const RTCList<RTCString> &lstNativePaths,
384 uint32_t fFlags)
385{
386 int rc = VINF_SUCCESS;
387
388 for (size_t i = 0; i < lstNativePaths.size(); i++)
389 {
390 const RTCString &strPath = lstNativePaths.at(i);
391 rc = AppendNativePath(strPath.c_str(), fFlags);
392 if (RT_FAILURE(rc))
393 break;
394 }
395
396 LogFlowFuncLeaveRC(rc);
397 return rc;
398}
399
400int DnDURIList::AppendURIPath(const char *pszURI, uint32_t fFlags)
401{
402 AssertPtrReturn(pszURI, VERR_INVALID_POINTER);
403
404#ifdef DEBUG_andy
405 LogFlowFunc(("pszPath=%s, fFlags=0x%x\n", pszURI, fFlags));
406#endif
407 int rc = VINF_SUCCESS;
408
409 /* Query the path component of a file URI. If this hasn't a
410 * file scheme NULL is returned. */
411 char *pszFilePath = RTUriFilePath(pszURI, URI_FILE_FORMAT_AUTO);
412 if (pszFilePath)
413 {
414 /* Add the path to our internal file list (recursive in
415 * the case of a directory). */
416 char *pszFileName = RTPathFilename(pszFilePath);
417 if (pszFileName)
418 {
419 Assert(pszFileName >= pszFilePath);
420 char *pszRoot = &pszFilePath[pszFileName - pszFilePath];
421 m_lstRoot.append(pszRoot);
422#ifdef DEBUG_andy
423 LogFlowFunc(("pszFilePath=%s, pszFileName=%s, pszRoot=%s\n",
424 pszFilePath, pszFileName, pszRoot));
425#endif
426 rc = appendPathRecursive(pszFilePath,
427 pszFileName - pszFilePath,
428 fFlags);
429 }
430 else
431 rc = VERR_NOT_FOUND;
432
433 RTStrFree(pszFilePath);
434 }
435 else
436 rc = VERR_INVALID_PARAMETER;
437
438 LogFlowFuncLeaveRC(rc);
439 return rc;
440}
441
442int DnDURIList::AppendURIPathsFromList(const char *pszURIPaths, size_t cbURIPaths,
443 uint32_t fFlags)
444{
445 AssertPtrReturn(pszURIPaths, VERR_INVALID_POINTER);
446 AssertReturn(cbURIPaths, VERR_INVALID_PARAMETER);
447
448 RTCList<RTCString> lstPaths
449 = RTCString(pszURIPaths, cbURIPaths - 1).split("\r\n");
450 return AppendURIPathsFromList(lstPaths, fFlags);
451}
452
453int DnDURIList::AppendURIPathsFromList(const RTCList<RTCString> &lstURI,
454 uint32_t fFlags)
455{
456 int rc = VINF_SUCCESS;
457
458 for (size_t i = 0; i < lstURI.size(); i++)
459 {
460 RTCString strURI = lstURI.at(i);
461 rc = AppendURIPath(strURI.c_str(), fFlags);
462
463 if (RT_FAILURE(rc))
464 break;
465 }
466
467 LogFlowFuncLeaveRC(rc);
468 return rc;
469}
470
471void DnDURIList::Clear(void)
472{
473 m_lstRoot.clear();
474 m_lstTree.clear();
475
476 m_cbTotal = 0;
477}
478
479#if 0
480int DnDURIList::FromData(const void *pvData, size_t cbData,
481
482 uint32_t fFlags)
483{
484 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
485 AssertReturn(cbData, VERR_INVALID_PARAMETER);
486
487 RTCList<RTCString> lstURI =
488 RTCString(static_cast<const char*>(pvData), cbData - 1).split("\r\n");
489 if (lstURI.isEmpty())
490 return VINF_SUCCESS;
491
492 int rc = VINF_SUCCESS;
493
494 for (size_t i = 0; i < lstURI.size(); ++i)
495 {
496 const RTCString &strUri = lstURI.at(i);
497 /* Query the path component of a file URI. If this hasn't a
498 * file scheme, null is returned. */
499 char *pszFilePath = RTUriFilePath(strUri.c_str(), URI_FILE_FORMAT_AUTO);
500 if (pszFilePath)
501 {
502 rc = DnDPathSanitize(pszFilePath, strlen(pszFilePath));
503 if (RT_SUCCESS(rc))
504 {
505 /** @todo Use RTPathJoin? */
506 RTCString strFullPath;
507 if (strBasePath.isNotEmpty())
508 strFullPath = RTCString().printf("%s%c%s", strBasePath.c_str(),
509 RTPATH_SLASH, pszFilePath);
510 else
511 strFullPath = pszFilePath;
512
513 char *pszNewUri = RTUriFileCreate(strFullPath.c_str());
514 if (pszNewUri)
515 {
516 m_lstRoot.append(pszNewUri);
517 RTStrFree(pszNewUri);
518 }
519 }
520 }
521 else
522 rc = VERR_INVALID_PARAMETER;
523
524 if (RT_FAILURE(rc))
525 break;
526 }
527
528 return rc;
529}
530#endif
531
532void DnDURIList::RemoveFirst(void)
533{
534 DnDURIObject &curPath = m_lstTree.first();
535
536 uint64_t cbSize = curPath.GetSize();
537 Assert(m_cbTotal >= cbSize);
538 m_cbTotal -= cbSize; /* Adjust total size. */
539
540 m_lstTree.removeFirst();
541}
542
543int DnDURIList::RootFromURIData(const void *pvData, size_t cbData,
544 uint32_t fFlags)
545{
546 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
547 AssertReturn(cbData, VERR_INVALID_PARAMETER);
548
549 RTCList<RTCString> lstURI =
550 RTCString(static_cast<const char*>(pvData), cbData - 1).split("\r\n");
551 if (lstURI.isEmpty())
552 return VINF_SUCCESS;
553
554 int rc = VINF_SUCCESS;
555
556 for (size_t i = 0; i < lstURI.size(); ++i)
557 {
558 /* Query the path component of a file URI. If this hasn't a
559 * file scheme, NULL is returned. */
560 const char *pszURI = lstURI.at(i).c_str();
561 char *pszFilePath = RTUriFilePath(pszURI,
562 URI_FILE_FORMAT_AUTO);
563#ifdef DEBUG_andy
564 LogFlowFunc(("pszURI=%s, pszFilePath=%s\n", pszURI, pszFilePath));
565#endif
566 if (pszFilePath)
567 {
568 rc = DnDPathSanitize(pszFilePath, strlen(pszFilePath));
569 if (RT_SUCCESS(rc))
570 m_lstRoot.append(pszFilePath);
571
572 RTStrFree(pszFilePath);
573 }
574 else
575 rc = VERR_INVALID_PARAMETER;
576
577 if (RT_FAILURE(rc))
578 break;
579 }
580
581 return rc;
582}
583
584RTCString DnDURIList::RootToString(const RTCString &strBasePath /* = "" */,
585 const RTCString &strSeparator /* = "\r\n" */)
586{
587 RTCString strRet;
588 for (size_t i = 0; i < m_lstRoot.size(); i++)
589 {
590 const char *pszCurRoot = m_lstRoot.at(i).c_str();
591 if (strBasePath.isNotEmpty())
592 {
593 char *pszPath = RTPathJoinA(strBasePath.c_str(), pszCurRoot);
594 if (pszPath)
595 {
596 char *pszPathURI = RTUriFileCreate(pszPath);
597 if (pszPathURI)
598 {
599 strRet += RTCString(pszPathURI) + strSeparator;
600 RTStrFree(pszPathURI);
601 }
602 else
603 break;
604 RTStrFree(pszPath);
605 }
606 else
607 break;
608 }
609 else
610 {
611 char *pszPathURI = RTUriFileCreate(pszCurRoot);
612 if (pszPathURI)
613 {
614 strRet += RTCString(pszPathURI) + strSeparator;
615 RTStrFree(pszPathURI);
616 }
617 else
618 break;
619 }
620 }
621
622 return strRet;
623}
624
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette