diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
commit | f8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch) | |
tree | 26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/GuestHost/DragAndDrop | |
parent | Initial commit. (diff) | |
download | virtualbox-upstream.tar.xz virtualbox-upstream.zip |
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/GuestHost/DragAndDrop')
-rw-r--r-- | src/VBox/GuestHost/DragAndDrop/DnDDroppedFiles.cpp | 264 | ||||
-rw-r--r-- | src/VBox/GuestHost/DragAndDrop/DnDMIME.cpp | 43 | ||||
-rw-r--r-- | src/VBox/GuestHost/DragAndDrop/DnDPath.cpp | 72 | ||||
-rw-r--r-- | src/VBox/GuestHost/DragAndDrop/DnDURIList.cpp | 565 | ||||
-rw-r--r-- | src/VBox/GuestHost/DragAndDrop/DnDURIObject.cpp | 565 | ||||
-rw-r--r-- | src/VBox/GuestHost/DragAndDrop/Makefile.kmk | 59 |
6 files changed, 1568 insertions, 0 deletions
diff --git a/src/VBox/GuestHost/DragAndDrop/DnDDroppedFiles.cpp b/src/VBox/GuestHost/DragAndDrop/DnDDroppedFiles.cpp new file mode 100644 index 00000000..1b3651cb --- /dev/null +++ b/src/VBox/GuestHost/DragAndDrop/DnDDroppedFiles.cpp @@ -0,0 +1,264 @@ +/* $Id: DnDDroppedFiles.cpp $ */ +/** @file + * DnD - Directory handling. + */ + +/* + * Copyright (C) 2014-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_GUEST_DND +#include <VBox/GuestHost/DragAndDrop.h> + +#include <iprt/assert.h> +#include <iprt/dir.h> +#include <iprt/err.h> +#include <iprt/file.h> +#include <iprt/path.h> +#include <iprt/string.h> + + +#include <VBox/log.h> + +DnDDroppedFiles::DnDDroppedFiles(void) + : m_fOpen(0) + , m_hDir(NULL) { } + +DnDDroppedFiles::DnDDroppedFiles(const char *pszPath, DNDURIDROPPEDFILEFLAGS fFlags /* = DNDURIDROPPEDFILE_FLAGS_NONE */) + : m_fOpen(0) + , m_hDir(NULL) +{ + OpenEx(pszPath, fFlags); +} + +DnDDroppedFiles::~DnDDroppedFiles(void) +{ + /* Only make sure to not leak any handles and stuff, don't delete any + * directories / files here. */ + closeInternal(); +} + +int DnDDroppedFiles::AddFile(const char *pszFile) +{ + AssertPtrReturn(pszFile, VERR_INVALID_POINTER); + + if (!this->m_lstFiles.contains(pszFile)) + this->m_lstFiles.append(pszFile); + return VINF_SUCCESS; +} + +int DnDDroppedFiles::AddDir(const char *pszDir) +{ + AssertPtrReturn(pszDir, VERR_INVALID_POINTER); + + if (!this->m_lstDirs.contains(pszDir)) + this->m_lstDirs.append(pszDir); + return VINF_SUCCESS; +} + +int DnDDroppedFiles::closeInternal(void) +{ + int rc; + if (this->m_hDir != NULL) + { + rc = RTDirClose(this->m_hDir); + if (RT_SUCCESS(rc)) + this->m_hDir = NULL; + } + else + rc = VINF_SUCCESS; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +int DnDDroppedFiles::Close(void) +{ + return closeInternal(); +} + +const char *DnDDroppedFiles::GetDirAbs(void) const +{ + return this->m_strPathAbs.c_str(); +} + +bool DnDDroppedFiles::IsOpen(void) const +{ + return (this->m_hDir != NULL); +} + +int DnDDroppedFiles::OpenEx(const char *pszPath, DNDURIDROPPEDFILEFLAGS fFlags /* = DNDURIDROPPEDFILE_FLAGS_NONE */) +{ + AssertPtrReturn(pszPath, VERR_INVALID_POINTER); + AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER); /* Flags not supported yet. */ + + int rc; + + do + { + char pszDropDir[RTPATH_MAX]; + RTStrPrintf(pszDropDir, sizeof(pszDropDir), "%s", pszPath); + + /** @todo On Windows we also could use the registry to override + * this path, on Posix a dotfile and/or a guest property + * can be used. */ + + /* Append our base drop directory. */ + rc = RTPathAppend(pszDropDir, sizeof(pszDropDir), "VirtualBox Dropped Files"); /** @todo Make this tag configurable? */ + if (RT_FAILURE(rc)) + break; + + /* Create it when necessary. */ + if (!RTDirExists(pszDropDir)) + { + rc = RTDirCreateFullPath(pszDropDir, RTFS_UNIX_IRWXU); + if (RT_FAILURE(rc)) + break; + } + + /* The actually drop directory consist of the current time stamp and a + * unique number when necessary. */ + char pszTime[64]; + RTTIMESPEC time; + if (!RTTimeSpecToString(RTTimeNow(&time), pszTime, sizeof(pszTime))) + { + rc = VERR_BUFFER_OVERFLOW; + break; + } + + rc = DnDPathSanitizeFilename(pszTime, sizeof(pszTime)); + if (RT_FAILURE(rc)) + break; + + rc = RTPathAppend(pszDropDir, sizeof(pszDropDir), pszTime); + if (RT_FAILURE(rc)) + break; + + /* Create it (only accessible by the current user) */ + rc = RTDirCreateUniqueNumbered(pszDropDir, sizeof(pszDropDir), RTFS_UNIX_IRWXU, 3, '-'); + if (RT_SUCCESS(rc)) + { + RTDIR hDir; + rc = RTDirOpen(&hDir, pszDropDir); + if (RT_SUCCESS(rc)) + { + this->m_hDir = hDir; + this->m_strPathAbs = pszDropDir; + this->m_fOpen = fFlags; + } + } + + } while (0); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +int DnDDroppedFiles::OpenTemp(DNDURIDROPPEDFILEFLAGS fFlags /* = DNDURIDROPPEDFILE_FLAGS_NONE */) +{ + AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER); /* Flags not supported yet. */ + + /* + * Get the user's temp directory. Don't use the user's root directory (or + * something inside it) because we don't know for how long/if the data will + * be kept after the guest OS used it. + */ + char szTemp[RTPATH_MAX]; + int rc = RTPathTemp(szTemp, sizeof(szTemp)); + if (RT_SUCCESS(rc)) + rc = OpenEx(szTemp, fFlags); + + return rc; +} + +int DnDDroppedFiles::Reset(bool fRemoveDropDir) +{ + int rc = closeInternal(); + if (RT_SUCCESS(rc)) + { + if (fRemoveDropDir) + { + rc = Rollback(); + } + else + { + this->m_lstDirs.clear(); + this->m_lstFiles.clear(); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +int DnDDroppedFiles::Reopen(void) +{ + if (this->m_strPathAbs.isEmpty()) + return VERR_NOT_FOUND; + + return OpenEx(this->m_strPathAbs.c_str(), this->m_fOpen); +} + +int DnDDroppedFiles::Rollback(void) +{ + if (this->m_strPathAbs.isEmpty()) + return VINF_SUCCESS; + + int rc = VINF_SUCCESS; + + /* Rollback by removing any stuff created. + * Note: Only remove empty directories, never ever delete + * anything recursive here! Steam (tm) knows best ... :-) */ + int rc2; + for (size_t i = 0; i < this->m_lstFiles.size(); i++) + { + rc2 = RTFileDelete(this->m_lstFiles.at(i).c_str()); + if (RT_SUCCESS(rc2)) + this->m_lstFiles.removeAt(i); + else if (RT_SUCCESS(rc)) + rc = rc2; + /* Keep going. */ + } + + for (size_t i = 0; i < this->m_lstDirs.size(); i++) + { + rc2 = RTDirRemove(this->m_lstDirs.at(i).c_str()); + if (RT_SUCCESS(rc2)) + this->m_lstDirs.removeAt(i); + else if (RT_SUCCESS(rc)) + rc = rc2; + /* Keep going. */ + } + + if (RT_SUCCESS(rc)) + { + Assert(this->m_lstFiles.isEmpty()); + Assert(this->m_lstDirs.isEmpty()); + + rc2 = closeInternal(); + if (RT_SUCCESS(rc2)) + { + /* Try to remove the empty root dropped files directory as well. + * Might return VERR_DIR_NOT_EMPTY or similar. */ + rc2 = RTDirRemove(this->m_strPathAbs.c_str()); + } + if (RT_SUCCESS(rc)) + rc = rc2; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + diff --git a/src/VBox/GuestHost/DragAndDrop/DnDMIME.cpp b/src/VBox/GuestHost/DragAndDrop/DnDMIME.cpp new file mode 100644 index 00000000..0162f534 --- /dev/null +++ b/src/VBox/GuestHost/DragAndDrop/DnDMIME.cpp @@ -0,0 +1,43 @@ +/* $Id: DnDMIME.cpp $ */ +/** @file + * DnD - Path list class. + */ + +/* + * Copyright (C) 2014-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_GUEST_DND +#include <VBox/GuestHost/DragAndDrop.h> + +#include <iprt/string.h> + + +bool DnDMIMEHasFileURLs(const char *pcszFormat, size_t cchFormatMax) +{ + /** @todo "text/uri" also an official variant? */ + return ( RTStrNICmp(pcszFormat, "text/uri-list", cchFormatMax) == 0 + || RTStrNICmp(pcszFormat, "x-special/gnome-icon-list", cchFormatMax) == 0); +} + +bool DnDMIMENeedsDropDir(const char *pcszFormat, size_t cchFormatMax) +{ + bool fNeedsDropDir = false; + if (!RTStrNICmp(pcszFormat, "text/uri-list", cchFormatMax)) /** @todo Add "x-special/gnome-icon-list"? */ + fNeedsDropDir = true; + + return fNeedsDropDir; +} + diff --git a/src/VBox/GuestHost/DragAndDrop/DnDPath.cpp b/src/VBox/GuestHost/DragAndDrop/DnDPath.cpp new file mode 100644 index 00000000..3755e039 --- /dev/null +++ b/src/VBox/GuestHost/DragAndDrop/DnDPath.cpp @@ -0,0 +1,72 @@ +/* $Id: DnDPath.cpp $ */ +/** @file + * DnD - Path handling. + */ + +/* + * Copyright (C) 2014-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_GUEST_DND +#include <VBox/GuestHost/DragAndDrop.h> + +#include <iprt/err.h> +#include <iprt/path.h> +#include <iprt/string.h> + + +/** + * Sanitizes the file name component so that unsupported characters + * will be replaced by an underscore ("_"). + * + * @return IPRT status code. + * @param pszPath Path to sanitize. + * @param cbPath Size (in bytes) of path to sanitize. + */ +int DnDPathSanitizeFilename(char *pszPath, size_t cbPath) +{ + int rc = VINF_SUCCESS; +#ifdef RT_OS_WINDOWS + RT_NOREF1(cbPath); + /* Replace out characters not allowed on Windows platforms, put in by RTTimeSpecToString(). */ + /** @todo Use something like RTPathSanitize() if available later some time. */ + static const RTUNICP s_uszValidRangePairs[] = + { + ' ', ' ', + '(', ')', + '-', '.', + '0', '9', + 'A', 'Z', + 'a', 'z', + '_', '_', + 0xa0, 0xd7af, + '\0' + }; + ssize_t cReplaced = RTStrPurgeComplementSet(pszPath, s_uszValidRangePairs, '_' /* chReplacement */); + if (cReplaced < 0) + rc = VERR_INVALID_UTF8_ENCODING; +#else + RT_NOREF2(pszPath, cbPath); +#endif + return rc; +} + +int DnDPathSanitize(char *pszPath, size_t cbPath) +{ + /** @todo */ + RT_NOREF2(pszPath, cbPath); + return VINF_SUCCESS; +} + diff --git a/src/VBox/GuestHost/DragAndDrop/DnDURIList.cpp b/src/VBox/GuestHost/DragAndDrop/DnDURIList.cpp new file mode 100644 index 00000000..0013d760 --- /dev/null +++ b/src/VBox/GuestHost/DragAndDrop/DnDURIList.cpp @@ -0,0 +1,565 @@ +/* $Id: DnDURIList.cpp $ */ +/** @file + * DnD - URI list class. + */ + +/* + * Copyright (C) 2014-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_GUEST_DND +#include <VBox/GuestHost/DragAndDrop.h> + +#include <iprt/dir.h> +#include <iprt/err.h> +#include <iprt/file.h> +#include <iprt/fs.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/symlink.h> +#include <iprt/uri.h> + +#include <VBox/log.h> + + +DnDURIList::DnDURIList(void) + : m_cTotal(0) + , m_cbTotal(0) +{ +} + +DnDURIList::~DnDURIList(void) +{ + Clear(); +} + +int DnDURIList::addEntry(const char *pcszSource, const char *pcszTarget, DNDURILISTFLAGS fFlags) +{ + AssertPtrReturn(pcszSource, VERR_INVALID_POINTER); + AssertPtrReturn(pcszTarget, VERR_INVALID_POINTER); + + LogFlowFunc(("pcszSource=%s, pcszTarget=%s, fFlags=0x%x\n", pcszSource, pcszTarget, fFlags)); + + RTFSOBJINFO objInfo; + int rc = RTPathQueryInfo(pcszSource, &objInfo, RTFSOBJATTRADD_NOTHING); + if (RT_SUCCESS(rc)) + { + if (RTFS_IS_FILE(objInfo.Attr.fMode)) + { + LogFlowFunc(("File '%s' -> '%s' (%RU64 bytes, file mode 0x%x)\n", + pcszSource, pcszTarget, (uint64_t)objInfo.cbObject, objInfo.Attr.fMode)); + + DnDURIObject *pObjFile = new DnDURIObject(DnDURIObject::Type_File, pcszSource, pcszTarget); + if (pObjFile) + { + if (fFlags & DNDURILIST_FLAGS_KEEP_OPEN) /* Shall we keep the file open while being added to this list? */ + { + /** @todo Add a standard fOpen mode for this list. */ + rc = pObjFile->Open(DnDURIObject::View_Source, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE); + } + else /* Just query the information without opening the file. */ + rc = pObjFile->QueryInfo(DnDURIObject::View_Source); + + if (RT_SUCCESS(rc)) + { + m_lstTree.append(pObjFile); + + m_cTotal++; + m_cbTotal += pObjFile->GetSize(); + } + else + delete pObjFile; + } + else + rc = VERR_NO_MEMORY; + } + else if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode)) + { + LogFlowFunc(("Directory '%s' -> '%s' (file mode 0x%x)\n", pcszSource, pcszTarget, objInfo.Attr.fMode)); + + DnDURIObject *pObjDir = new DnDURIObject(DnDURIObject::Type_Directory, pcszSource, pcszTarget); + if (pObjDir) + { + m_lstTree.append(pObjDir); + + /** @todo Add DNDURILIST_FLAGS_KEEP_OPEN handling? */ + m_cTotal++; + } + else + rc = VERR_NO_MEMORY; + } + /* Note: Symlinks already should have been resolved at this point. */ + else + rc = VERR_NOT_SUPPORTED; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +int DnDURIList::appendPathRecursive(const char *pcszSrcPath, + const char *pcszDstPath, const char *pcszDstBase, size_t cchDstBase, + DNDURILISTFLAGS fFlags) +{ + AssertPtrReturn(pcszSrcPath, VERR_INVALID_POINTER); + AssertPtrReturn(pcszDstBase, VERR_INVALID_POINTER); + AssertPtrReturn(pcszDstPath, VERR_INVALID_POINTER); + + LogFlowFunc(("pcszSrcPath=%s, pcszDstPath=%s, pcszDstBase=%s, cchDstBase=%zu, fFlags=0x%x\n", + pcszSrcPath, pcszDstPath, pcszDstBase, cchDstBase, fFlags)); + + RTFSOBJINFO objInfo; + int rc = RTPathQueryInfo(pcszSrcPath, &objInfo, RTFSOBJATTRADD_NOTHING); + if (RT_SUCCESS(rc)) + { + if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode)) + { + rc = addEntry(pcszSrcPath, &pcszDstPath[cchDstBase], fFlags); + if (RT_SUCCESS(rc)) + { + RTDIR hDir; + rc = RTDirOpen(&hDir, pcszSrcPath); + if (RT_SUCCESS(rc)) + { + size_t cbDirEntry = 0; + PRTDIRENTRYEX pDirEntry = NULL; + do + { + /* Retrieve the next directory entry. */ + rc = RTDirReadExA(hDir, &pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK); + if (RT_FAILURE(rc)) + { + if (rc == VERR_NO_MORE_FILES) + rc = VINF_SUCCESS; + break; + } + + switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK) + { + case RTFS_TYPE_DIRECTORY: + { + /* Skip "." and ".." entries. */ + if (RTDirEntryExIsStdDotLink(pDirEntry)) + break; + + char *pszSrc = RTPathJoinA(pcszSrcPath, pDirEntry->szName); + if (pszSrc) + { + char *pszDst = RTPathJoinA(pcszDstPath, pDirEntry->szName); + if (pszDst) + { + rc = appendPathRecursive(pszSrc, pszDst, pcszDstBase, cchDstBase, fFlags); + RTStrFree(pszDst); + } + else + rc = VERR_NO_MEMORY; + + RTStrFree(pszSrc); + } + else + rc = VERR_NO_MEMORY; + break; + } + + case RTFS_TYPE_FILE: + { + char *pszSrc = RTPathJoinA(pcszSrcPath, pDirEntry->szName); + if (pszSrc) + { + char *pszDst = RTPathJoinA(pcszDstPath, pDirEntry->szName); + if (pszDst) + { + rc = addEntry(pszSrc, &pszDst[cchDstBase], fFlags); + RTStrFree(pszDst); + } + else + rc = VERR_NO_MEMORY; + RTStrFree(pszSrc); + } + else + rc = VERR_NO_MEMORY; + break; + } + case RTFS_TYPE_SYMLINK: + { + if (fFlags & DNDURILIST_FLAGS_RESOLVE_SYMLINKS) + { + char *pszSrc = RTPathRealDup(pcszDstBase); + if (pszSrc) + { + rc = RTPathQueryInfo(pszSrc, &objInfo, RTFSOBJATTRADD_NOTHING); + if (RT_SUCCESS(rc)) + { + if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode)) + { + LogFlowFunc(("Directory entry is symlink to directory\n")); + rc = appendPathRecursive(pszSrc, pcszDstPath, pcszDstBase, cchDstBase, fFlags); + } + else if (RTFS_IS_FILE(objInfo.Attr.fMode)) + { + LogFlowFunc(("Directory entry is symlink to file\n")); + rc = addEntry(pszSrc, &pcszDstPath[cchDstBase], fFlags); + } + else + rc = VERR_NOT_SUPPORTED; + } + + RTStrFree(pszSrc); + } + else + rc = VERR_NO_MEMORY; + } + break; + } + + default: + break; + } + + } while (RT_SUCCESS(rc)); + + RTDirReadExAFree(&pDirEntry, &cbDirEntry); + RTDirClose(hDir); + } + } + } + else if (RTFS_IS_FILE(objInfo.Attr.fMode)) + { + rc = addEntry(pcszSrcPath, &pcszDstPath[cchDstBase], fFlags); + } + else if (RTFS_IS_SYMLINK(objInfo.Attr.fMode)) + { + if (fFlags & DNDURILIST_FLAGS_RESOLVE_SYMLINKS) + { + char *pszSrc = RTPathRealDup(pcszSrcPath); + if (pszSrc) + { + rc = RTPathQueryInfo(pszSrc, &objInfo, RTFSOBJATTRADD_NOTHING); + if (RT_SUCCESS(rc)) + { + if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode)) + { + LogFlowFunc(("Symlink to directory\n")); + rc = appendPathRecursive(pszSrc, pcszDstPath, pcszDstBase, cchDstBase, fFlags); + } + else if (RTFS_IS_FILE(objInfo.Attr.fMode)) + { + LogFlowFunc(("Symlink to file\n")); + rc = addEntry(pszSrc, &pcszDstPath[cchDstBase], fFlags); + } + else + rc = VERR_NOT_SUPPORTED; + } + + RTStrFree(pszSrc); + } + else + rc = VERR_NO_MEMORY; + } + } + else + rc = VERR_NOT_SUPPORTED; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +int DnDURIList::AppendNativePath(const char *pszPath, DNDURILISTFLAGS fFlags) +{ + AssertPtrReturn(pszPath, VERR_INVALID_POINTER); + + int rc; + char *pszPathNative = RTStrDup(pszPath); + if (pszPathNative) + { + RTPathChangeToUnixSlashes(pszPathNative, true /* fForce */); + + char *pszPathURI = RTUriCreate("file" /* pszScheme */, NULL /* pszAuthority */, + pszPathNative, NULL /* pszQuery */, NULL /* pszFragment */); + if (pszPathURI) + { + rc = AppendURIPath(pszPathURI, fFlags); + RTStrFree(pszPathURI); + } + else + rc = VERR_INVALID_PARAMETER; + + RTStrFree(pszPathNative); + } + else + rc = VERR_NO_MEMORY; + + return rc; +} + +int DnDURIList::AppendNativePathsFromList(const char *pszNativePaths, size_t cbNativePaths, + DNDURILISTFLAGS fFlags) +{ + AssertPtrReturn(pszNativePaths, VERR_INVALID_POINTER); + AssertReturn(cbNativePaths, VERR_INVALID_PARAMETER); + + RTCList<RTCString> lstPaths + = RTCString(pszNativePaths, cbNativePaths - 1).split("\r\n"); + return AppendNativePathsFromList(lstPaths, fFlags); +} + +int DnDURIList::AppendNativePathsFromList(const RTCList<RTCString> &lstNativePaths, + DNDURILISTFLAGS fFlags) +{ + int rc = VINF_SUCCESS; + + for (size_t i = 0; i < lstNativePaths.size(); i++) + { + const RTCString &strPath = lstNativePaths.at(i); + rc = AppendNativePath(strPath.c_str(), fFlags); + if (RT_FAILURE(rc)) + break; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +int DnDURIList::AppendURIPath(const char *pszURI, DNDURILISTFLAGS fFlags) +{ + AssertPtrReturn(pszURI, VERR_INVALID_POINTER); + AssertReturn(!(fFlags & ~DNDURILIST_FLAGS_VALID_MASK), VERR_INVALID_FLAGS); + /** @todo Check for string termination? */ + + RTURIPARSED Parsed; + int rc = RTUriParse(pszURI, &Parsed); + if (RT_FAILURE(rc)) + return rc; + + char *pszSrcPath = NULL; + + /* file://host.example.com/path/to/file.txt */ + const char *pszParsedAuthority = RTUriParsedAuthority(pszURI, &Parsed); + if ( pszParsedAuthority + && pszParsedAuthority[0] != '\0') /* Authority present? */ + { + const char *pszParsedPath = RTUriParsedPath(pszURI, &Parsed); + if (pszParsedPath) + { + /* Always use UNIXy paths internally. */ + if (RTStrAPrintf(&pszSrcPath, "//%s%s", pszParsedAuthority, pszParsedPath) == -1) + rc = VERR_NO_MEMORY; + } + else + rc = VERR_INVALID_PARAMETER; + } + else + { + pszSrcPath = RTUriFilePath(pszURI); + if (!pszSrcPath) + rc = VERR_INVALID_PARAMETER; + } + + LogFlowFunc(("pszURI=%s, fFlags=0x%x -> pszParsedAuthority=%s, pszSrcPath=%s, rc=%Rrc\n", + pszURI, fFlags, pszParsedAuthority ? pszParsedAuthority : "<None>", pszSrcPath, rc)); + + if (RT_SUCCESS(rc)) + { + /* Add the path to our internal file list (recursive in + * the case of a directory). */ + size_t cbPathLen = RTPathStripTrailingSlash(pszSrcPath); + if (cbPathLen) + { + char *pszFileName = RTPathFilename(pszSrcPath); + if (pszFileName) + { + Assert(pszFileName >= pszSrcPath); + size_t cchDstBase = (fFlags & DNDURILIST_FLAGS_ABSOLUTE_PATHS) + ? 0 /* Use start of path as root. */ + : pszFileName - pszSrcPath; + char *pszDstPath = &pszSrcPath[cchDstBase]; + m_lstRoot.append(pszDstPath); + + LogFlowFunc(("pszSrcPath=%s, pszFileName=%s, pszRoot=%s\n", + pszSrcPath, pszFileName, pszDstPath)); + + rc = appendPathRecursive(pszSrcPath, pszSrcPath, pszSrcPath, cchDstBase, fFlags); + } + else + rc = VERR_PATH_NOT_FOUND; + } + else + rc = VERR_INVALID_PARAMETER; + } + + RTStrFree(pszSrcPath); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +int DnDURIList::AppendURIPathsFromList(const char *pszURIPaths, size_t cbURIPaths, + DNDURILISTFLAGS fFlags) +{ + AssertPtrReturn(pszURIPaths, VERR_INVALID_POINTER); + AssertReturn(cbURIPaths, VERR_INVALID_PARAMETER); + + RTCList<RTCString> lstPaths + = RTCString(pszURIPaths, cbURIPaths - 1).split("\r\n"); + return AppendURIPathsFromList(lstPaths, fFlags); +} + +int DnDURIList::AppendURIPathsFromList(const RTCList<RTCString> &lstURI, + DNDURILISTFLAGS fFlags) +{ + int rc = VINF_SUCCESS; + + for (size_t i = 0; i < lstURI.size(); i++) + { + RTCString strURI = lstURI.at(i); + rc = AppendURIPath(strURI.c_str(), fFlags); + + if (RT_FAILURE(rc)) + break; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +void DnDURIList::Clear(void) +{ + m_lstRoot.clear(); + + for (size_t i = 0; i < m_lstTree.size(); i++) + { + DnDURIObject *pCurObj = m_lstTree.at(i); + AssertPtr(pCurObj); + RTMemFree(pCurObj); + } + m_lstTree.clear(); + + m_cTotal = 0; + m_cbTotal = 0; +} + +void DnDURIList::RemoveFirst(void) +{ + if (m_lstTree.isEmpty()) + return; + + DnDURIObject *pCurObj = m_lstTree.first(); + AssertPtr(pCurObj); + + uint64_t cbSize = pCurObj->GetSize(); + Assert(m_cbTotal >= cbSize); + m_cbTotal -= cbSize; /* Adjust total size. */ + + pCurObj->Close(); + RTMemFree(pCurObj); + + m_lstTree.removeFirst(); +} + +int DnDURIList::SetFromURIData(const void *pvData, size_t cbData, DNDURILISTFLAGS fFlags) +{ + Assert(fFlags == 0); RT_NOREF1(fFlags); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + + if (!RTStrIsValidEncoding(static_cast<const char *>(pvData))) + return VERR_INVALID_PARAMETER; + + RTCList<RTCString> lstURI = + RTCString(static_cast<const char *>(pvData), cbData - 1).split("\r\n"); + if (lstURI.isEmpty()) + return VINF_SUCCESS; + + int rc = VINF_SUCCESS; + + for (size_t i = 0; i < lstURI.size(); ++i) + { + /* Query the path component of a file URI. If this hasn't a + * file scheme, NULL is returned. */ + const char *pszURI = lstURI.at(i).c_str(); + char *pszFilePath = RTUriFilePath(pszURI); +#ifdef DEBUG_andy + LogFlowFunc(("pszURI=%s, pszFilePath=%s\n", pszURI, pszFilePath)); +#endif + if (pszFilePath) + { + rc = DnDPathSanitize(pszFilePath, strlen(pszFilePath)); + if (RT_SUCCESS(rc)) + { + m_lstRoot.append(pszFilePath); + m_cTotal++; + } + + RTStrFree(pszFilePath); + } + else + rc = VERR_INVALID_PARAMETER; + + if (RT_FAILURE(rc)) + break; + } + + return rc; +} + +RTCString DnDURIList::GetRootEntries(const RTCString &strPathBase /* = "" */, + const RTCString &strSeparator /* = "\r\n" */) const +{ + RTCString strRet; + for (size_t i = 0; i < m_lstRoot.size(); i++) + { + const char *pszCurRoot = m_lstRoot.at(i).c_str(); +#ifdef DEBUG_andy + LogFlowFunc(("pszCurRoot=%s\n", pszCurRoot)); +#endif + if (strPathBase.isNotEmpty()) + { + char *pszPath = RTPathJoinA(strPathBase.c_str(), pszCurRoot); + if (pszPath) + { + char *pszPathURI = RTUriFileCreate(pszPath); + if (pszPathURI) + { + strRet += RTCString(pszPathURI) + strSeparator; + LogFlowFunc(("URI (Base): %s\n", strRet.c_str())); + RTStrFree(pszPathURI); + } + + RTStrFree(pszPath); + + if (!pszPathURI) + break; + } + else + break; + } + else + { + char *pszPathURI = RTUriFileCreate(pszCurRoot); + if (pszPathURI) + { + strRet += RTCString(pszPathURI) + strSeparator; + LogFlowFunc(("URI: %s\n", strRet.c_str())); + RTStrFree(pszPathURI); + } + else + break; + } + } + + return strRet; +} + diff --git a/src/VBox/GuestHost/DragAndDrop/DnDURIObject.cpp b/src/VBox/GuestHost/DragAndDrop/DnDURIObject.cpp new file mode 100644 index 00000000..6c764fb2 --- /dev/null +++ b/src/VBox/GuestHost/DragAndDrop/DnDURIObject.cpp @@ -0,0 +1,565 @@ +/* $Id: DnDURIObject.cpp $ */ +/** @file + * DnD - URI object class. For handling creation/reading/writing to files and directories on host or guest side. + */ + +/* + * Copyright (C) 2014-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_GUEST_DND +#include <VBox/GuestHost/DragAndDrop.h> + +#include <iprt/dir.h> +#include <iprt/err.h> +#include <iprt/file.h> +#include <iprt/fs.h> +#include <iprt/path.h> +#include <iprt/uri.h> + +#include <VBox/log.h> + + +DnDURIObject::DnDURIObject(void) + : m_enmType(Type_Unknown) + , m_enmView(View_Unknown) + , m_fIsOpen(false) +{ + RT_ZERO(u); +} + +DnDURIObject::DnDURIObject(Type enmType, + const RTCString &strSrcPathAbs /* = 0 */, + const RTCString &strDstPathAbs /* = 0 */) + : m_enmType(enmType) + , m_enmView(View_Unknown) + , m_strSrcPathAbs(strSrcPathAbs) + , m_strTgtPathAbs(strDstPathAbs) + , m_fIsOpen(false) +{ + RT_ZERO(u); +} + +DnDURIObject::~DnDURIObject(void) +{ + closeInternal(); +} + +/** + * Closes the object's internal handles (to files / ...). + * + */ +void DnDURIObject::closeInternal(void) +{ + LogFlowThisFuncEnter(); + + if (!m_fIsOpen) + return; + + switch (m_enmType) + { + case Type_File: + { + RTFileClose(u.File.hFile); + u.File.hFile = NIL_RTFILE; + RT_ZERO(u.File.objInfo); + break; + } + + case Type_Directory: + { + RTDirClose(u.Dir.hDir); + u.Dir.hDir = NIL_RTDIR; + RT_ZERO(u.Dir.objInfo); + break; + } + + default: + break; + } + + m_fIsOpen = false; +} + +/** + * Closes the object. + * This also closes the internal handles associated with the object (to files / ...). + */ +void DnDURIObject::Close(void) +{ + closeInternal(); +} + +/** + * Returns the directory / file mode of the object. + * + * @return File / directory mode. + */ +RTFMODE DnDURIObject::GetMode(void) const +{ + switch (m_enmType) + { + case Type_File: + return u.File.objInfo.Attr.fMode; + + case Type_Directory: + return u.Dir.objInfo.Attr.fMode; + + default: + break; + } + + AssertFailed(); + return 0; +} + +/** + * Returns the bytes already processed (read / written). + * + * Note: Only applies if the object is of type DnDURIObject::Type_File. + * + * @return Bytes already processed (read / written). + */ +uint64_t DnDURIObject::GetProcessed(void) const +{ + if (m_enmType == Type_File) + return u.File.cbProcessed; + + return 0; +} + +/** + * Returns the file's logical size (in bytes). + * + * Note: Only applies if the object is of type DnDURIObject::Type_File. + * + * @return The file's logical size (in bytes). + */ +uint64_t DnDURIObject::GetSize(void) const +{ + if (m_enmType == Type_File) + return u.File.cbToProcess; + + return 0; +} + +/** + * Returns whether the processing of the object is complete or not. + * For file objects this means that all bytes have been processed. + * + * @return True if complete, False if not. + */ +bool DnDURIObject::IsComplete(void) const +{ + bool fComplete; + + switch (m_enmType) + { + case Type_File: + Assert(u.File.cbProcessed <= u.File.cbToProcess); + fComplete = u.File.cbProcessed == u.File.cbToProcess; + break; + + case Type_Directory: + fComplete = true; + break; + + default: + fComplete = true; + break; + } + + return fComplete; +} + +/** + * Returns whether the object is in an open state or not. + */ +bool DnDURIObject::IsOpen(void) const +{ + return m_fIsOpen; +} + +/** + * (Re-)Opens the object with a specific view, open and file mode. + * + * @return IPRT status code. + * @param enmView View to use for opening the object. + * @param fOpen File open flags to use. + * @param fMode File mode to use. + */ +int DnDURIObject::Open(View enmView, uint64_t fOpen /* = 0 */, RTFMODE fMode /* = 0 */) +{ + return OpenEx( enmView == View_Source + ? m_strSrcPathAbs : m_strTgtPathAbs + , enmView, fOpen, fMode, DNDURIOBJECT_FLAGS_NONE); +} + +/** + * Open the object with a specific file type, and, depending on the type, specifying additional parameters. + * + * @return IPRT status code. + * @param strPathAbs Absolute path of the object (file / directory / ...). + * @param enmView View of the object. + * @param fOpen Open mode to use; only valid for file objects. + * @param fMode File mode to use; only valid for file objects. + * @param fFlags Additional DnD URI object flags. + */ +int DnDURIObject::OpenEx(const RTCString &strPathAbs, View enmView, + uint64_t fOpen /* = 0 */, RTFMODE fMode /* = 0 */, DNDURIOBJECTFLAGS fFlags /* = DNDURIOBJECT_FLAGS_NONE */) +{ + AssertReturn(!(fFlags & ~DNDURIOBJECT_FLAGS_VALID_MASK), VERR_INVALID_FLAGS); + RT_NOREF1(fFlags); + + if (m_fIsOpen) + return VINF_SUCCESS; + + int rc = VINF_SUCCESS; + + switch (enmView) + { + case View_Source: + m_strSrcPathAbs = strPathAbs; + break; + + case View_Target: + m_strTgtPathAbs = strPathAbs; + break; + + default: + rc = VERR_NOT_IMPLEMENTED; + break; + } + + if ( RT_SUCCESS(rc) + && fOpen) /* Opening mode specified? */ + { + LogFlowThisFunc(("strPath=%s, enmView=%RU32, fOpen=0x%x, fMode=0x%x, fFlags=0x%x\n", + strPathAbs.c_str(), enmView, fOpen, fMode, fFlags)); + switch (m_enmType) + { + case Type_File: + { + /* + * Open files on the source with RTFILE_O_DENY_WRITE to prevent races + * where the OS writes to the file while the destination side transfers + * it over. + */ + LogFlowThisFunc(("Opening ...\n")); + rc = RTFileOpen(&u.File.hFile, strPathAbs.c_str(), fOpen); + if (RT_SUCCESS(rc)) + { + if ( (fOpen & RTFILE_O_WRITE) /* Only set the file mode on write. */ + && fMode /* Some file mode to set specified? */) + { + rc = RTFileSetMode(u.File.hFile, fMode); + } + else if (fOpen & RTFILE_O_READ) + { + rc = queryInfoInternal(enmView); + } + } + + if (RT_SUCCESS(rc)) + { + LogFlowThisFunc(("File cbObject=%RU64, fMode=0x%x\n", + u.File.objInfo.cbObject, u.File.objInfo.Attr.fMode)); + u.File.cbToProcess = u.File.objInfo.cbObject; + u.File.cbProcessed = 0; + } + + break; + } + + case Type_Directory: + { + rc = RTDirOpen(&u.Dir.hDir, strPathAbs.c_str()); + if (RT_SUCCESS(rc)) + rc = queryInfoInternal(enmView); + break; + } + + default: + rc = VERR_NOT_IMPLEMENTED; + break; + } + } + + if (RT_SUCCESS(rc)) + { + m_enmView = enmView; + m_fIsOpen = true; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Queries information about the object using a specific view, internal version. + * + * @return IPRT status code. + * @param enmView View to use for querying information. + */ +int DnDURIObject::queryInfoInternal(View enmView) +{ + RT_NOREF(enmView); + + int rc; + + switch (m_enmType) + { + case Type_File: + rc = RTFileQueryInfo(u.File.hFile, &u.File.objInfo, RTFSOBJATTRADD_NOTHING); + break; + + case Type_Directory: + rc = RTDirQueryInfo(u.Dir.hDir, &u.Dir.objInfo, RTFSOBJATTRADD_NOTHING); + break; + + default: + rc = VERR_NOT_IMPLEMENTED; + break; + } + + return rc; +} + +/** + * Queries information about the object using a specific view. + * + * @return IPRT status code. + * @param enmView View to use for querying information. + */ +int DnDURIObject::QueryInfo(View enmView) +{ + return queryInfoInternal(enmView); +} + +/** + * Rebases an absolute URI path from an old path base to a new path base. + * This function is needed in order to transform path from the source side to the target side. + * + * @return IPRT status code. + * @param strPathAbs Absolute URI path to rebase. + * @param strBaseOld Old base path to rebase from. + * @param strBaseNew New base path to rebase to. + * + ** @todo Put this into an own class like DnDURIPath : public RTCString? + */ +/* static */ +int DnDURIObject::RebaseURIPath(RTCString &strPathAbs, + const RTCString &strBaseOld /* = "" */, + const RTCString &strBaseNew /* = "" */) +{ + char *pszPath = RTUriFilePath(strPathAbs.c_str()); + if (!pszPath) /* No URI? */ + pszPath = RTStrDup(strPathAbs.c_str()); + + int rc; + + if (pszPath) + { + const char *pszPathStart = pszPath; + const char *pszBaseOld = strBaseOld.c_str(); + if ( pszBaseOld + && RTPathStartsWith(pszPath, pszBaseOld)) + { + pszPathStart += strlen(pszBaseOld); + } + + rc = VINF_SUCCESS; + + if (RT_SUCCESS(rc)) + { + char *pszPathNew = RTPathJoinA(strBaseNew.c_str(), pszPathStart); + if (pszPathNew) + { + char *pszPathURI = RTUriCreate("file" /* pszScheme */, "/" /* pszAuthority */, + pszPathNew /* pszPath */, + NULL /* pszQuery */, NULL /* pszFragment */); + if (pszPathURI) + { + LogFlowFunc(("Rebasing \"%s\" to \"%s\"\n", strPathAbs.c_str(), pszPathURI)); + + strPathAbs = RTCString(pszPathURI) + "\r\n"; + RTStrFree(pszPathURI); + } + else + rc = VERR_INVALID_PARAMETER; + + RTStrFree(pszPathNew); + } + else + rc = VERR_NO_MEMORY; + } + + RTStrFree(pszPath); + } + else + rc = VERR_NO_MEMORY; + + return rc; +} + +/** + * Reads data from the object. Only applies to files objects. + * + * @return IPRT status code. + * @param pvBuf Buffer where to store the read data. + * @param cbBuf Size (in bytes) of the buffer. + * @param pcbRead Pointer where to store how many bytes were read. Optional. + */ +int DnDURIObject::Read(void *pvBuf, size_t cbBuf, uint32_t *pcbRead) +{ + AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); + AssertReturn(cbBuf, VERR_INVALID_PARAMETER); + /* pcbRead is optional. */ + + AssertMsgReturn(m_fIsOpen, ("Object not in open state\n"), VERR_INVALID_STATE); + AssertMsgReturn(m_enmView == View_Source, ("Cannot write to an object which is not in target view\n"), + VERR_INVALID_STATE); + + size_t cbRead = 0; + + int rc; + switch (m_enmType) + { + case Type_File: + { + rc = RTFileRead(u.File.hFile, pvBuf, cbBuf, &cbRead); + if (RT_SUCCESS(rc)) + { + u.File.cbProcessed += cbRead; + Assert(u.File.cbProcessed <= u.File.cbToProcess); + + /* End of file reached or error occurred? */ + if ( u.File.cbToProcess + && u.File.cbProcessed == u.File.cbToProcess) + { + rc = VINF_EOF; + } + } + break; + } + + case Type_Directory: + { + rc = VINF_SUCCESS; + break; + } + + default: + rc = VERR_NOT_IMPLEMENTED; + break; + } + + if (RT_SUCCESS(rc)) + { + if (pcbRead) + *pcbRead = (uint32_t)cbRead; + } + + LogFlowFunc(("Returning strSourcePath=%s, cbRead=%zu, rc=%Rrc\n", m_strSrcPathAbs.c_str(), cbRead, rc)); + return rc; +} + +/** + * Resets the object's state and closes all related handles. + */ +void DnDURIObject::Reset(void) +{ + LogFlowThisFuncEnter(); + + Close(); + + m_enmType = Type_Unknown; + m_enmView = View_Unknown; + m_strSrcPathAbs = ""; + m_strTgtPathAbs = ""; + + RT_ZERO(u); +} + +/** + * Sets the bytes to process by the object. + * + * Note: Only applies if the object is of type DnDURIObject::Type_File. + * + * @return IPRT return code. + * @param cbSize Size (in bytes) to process. + */ +int DnDURIObject::SetSize(uint64_t cbSize) +{ + AssertReturn(m_enmType == Type_File, VERR_INVALID_PARAMETER); + + /** @todo Implement sparse file support here. */ + + u.File.cbToProcess = cbSize; + return VINF_SUCCESS; +} + +/** + * Writes data to an object. Only applies to file objects. + * + * @return IPRT status code. + * @param pvBuf Buffer of data to write. + * @param cbBuf Size (in bytes) of data to write. + * @param pcbWritten Pointer where to store how many bytes were written. Optional. + */ +int DnDURIObject::Write(const void *pvBuf, size_t cbBuf, uint32_t *pcbWritten) +{ + AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); + AssertReturn(cbBuf, VERR_INVALID_PARAMETER); + /* pcbWritten is optional. */ + + AssertMsgReturn(m_fIsOpen, ("Object not in open state\n"), VERR_INVALID_STATE); + AssertMsgReturn(m_enmView == View_Target, ("Cannot write to an object which is not in target view\n"), + VERR_INVALID_STATE); + + size_t cbWritten = 0; + + int rc; + switch (m_enmType) + { + case Type_File: + { + rc = RTFileWrite(u.File.hFile, pvBuf, cbBuf, &cbWritten); + if (RT_SUCCESS(rc)) + u.File.cbProcessed += cbWritten; + break; + } + + case Type_Directory: + { + rc = VINF_SUCCESS; + break; + } + + default: + rc = VERR_NOT_IMPLEMENTED; + break; + } + + if (RT_SUCCESS(rc)) + { + if (pcbWritten) + *pcbWritten = (uint32_t)cbWritten; + } + + LogFlowThisFunc(("Returning strSourcePathAbs=%s, cbWritten=%zu, rc=%Rrc\n", m_strSrcPathAbs.c_str(), cbWritten, rc)); + return rc; +} + diff --git a/src/VBox/GuestHost/DragAndDrop/Makefile.kmk b/src/VBox/GuestHost/DragAndDrop/Makefile.kmk new file mode 100644 index 00000000..457c3426 --- /dev/null +++ b/src/VBox/GuestHost/DragAndDrop/Makefile.kmk @@ -0,0 +1,59 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the shared DnD code for both, host and guest. +# + +# +# Copyright (C) 2014-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + +VBOX_DND_GUESTHOST_FILES := \ + DnDDroppedFiles.cpp \ + DnDMIME.cpp \ + DnDPath.cpp \ + DnDURIList.cpp \ + DnDURIObject.cpp + +# +# DnDGuestR3Lib - For tools on the guest side, +# e.g. VBoxClient/VBoxTray. +# +ifdef VBOX_WITH_ADDITIONS + LIBRARIES += VBoxDnDGuestR3Lib + VBoxDnDGuestR3Lib_TEMPLATE = VBOXGUESTR3LIB + VBoxDnDGuestR3Lib_DEFS = + VBoxDnDGuestR3Lib_SOURCES = $(VBOX_DND_GUESTHOST_FILES) + +# LIBRARIES.win.amd64 += VBoxDnDGuestR3Lib-x86 +# VBoxDnDGuestR3Lib-x86_EXTENDS := VBoxDnDGuestR3Lib +# VBoxDnDGuestR3Lib-x86_BLD_TRG_ARCH := x86 +endif + +# +# DnDHostR3Lib - For the host side, e.g. Main +# and frontends. +# +LIBRARIES += VBoxDnDHostR3Lib +VBoxDnDHostR3Lib_TEMPLATE = VBOXR3 +VBoxDnDHostR3Lib_DEFS = +VBoxDnDHostR3Lib_SOURCES = $(VBOX_DND_GUESTHOST_FILES) + +# Include the common host services code. +VBOX_PATH_HOSTSERVICES_COMMON = $(PATH_ROOT)/src/VBox/HostServices/common +VBoxDnDHostR3Lib_SOURCES += \ + $(VBOX_PATH_HOSTSERVICES_COMMON)/client.cpp \ + $(VBOX_PATH_HOSTSERVICES_COMMON)/message.cpp + +include $(FILE_KBUILD_SUB_FOOTER) + |