summaryrefslogtreecommitdiffstats
path: root/src/VBox/GuestHost/DragAndDrop/DnDDroppedFiles.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:49:04 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:49:04 +0000
commit16f504a9dca3fe3b70568f67b7d41241ae485288 (patch)
treec60f36ada0496ba928b7161059ba5ab1ab224f9d /src/VBox/GuestHost/DragAndDrop/DnDDroppedFiles.cpp
parentInitial commit. (diff)
downloadvirtualbox-upstream.tar.xz
virtualbox-upstream.zip
Adding upstream version 7.0.6-dfsg.upstream/7.0.6-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/GuestHost/DragAndDrop/DnDDroppedFiles.cpp')
-rw-r--r--src/VBox/GuestHost/DragAndDrop/DnDDroppedFiles.cpp450
1 files changed, 450 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..57fd03bb
--- /dev/null
+++ b/src/VBox/GuestHost/DragAndDrop/DnDDroppedFiles.cpp
@@ -0,0 +1,450 @@
+/* $Id: DnDDroppedFiles.cpp $ */
+/** @file
+ * DnD - Directory handling.
+ */
+
+/*
+ * Copyright (C) 2014-2022 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* 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/mem.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+
+#include <VBox/log.h>
+
+
+/*********************************************************************************************************************************
+* Prototypes *
+*********************************************************************************************************************************/
+static int dndDroppedFilesCloseInternal(PDNDDROPPEDFILES pDF);
+
+
+/**
+ * Initializes a DnD Dropped Files struct, internal version.
+ *
+ * @returns VBox status code.
+ * @param pDF DnD Dropped Files to initialize.
+ */
+static int dndDroppedFilesInitInternal(PDNDDROPPEDFILES pDF)
+{
+ pDF->m_fOpen = 0;
+ pDF->m_hDir = NIL_RTDIR;
+ pDF->pszPathAbs = NULL;
+
+ RTListInit(&pDF->m_lstDirs);
+ RTListInit(&pDF->m_lstFiles);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Initializes a DnD Dropped Files struct, extended version.
+ *
+ * @returns VBox status code.
+ * @param pDF DnD Dropped Files to initialize.
+ * @param fFlags Dropped Files flags to use for initialization.
+ */
+int DnDDroppedFilesInitEx(PDNDDROPPEDFILES pDF,
+ const char *pszPath, DNDURIDROPPEDFILEFLAGS fFlags /* = DNDURIDROPPEDFILE_FLAGS_NONE */)
+{
+ int rc = dndDroppedFilesInitInternal(pDF);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ return DnDDroppedFilesOpenEx(pDF, pszPath, fFlags);
+}
+
+/**
+ * Initializes a DnD Dropped Files struct.
+ *
+ * @returns VBox status code.
+ * @param pDF DnD Dropped Files to initialize.
+ */
+int DnDDroppedFilesInit(PDNDDROPPEDFILES pDF)
+{
+ return dndDroppedFilesInitInternal(pDF);
+}
+
+/**
+ * Destroys a DnD Dropped Files struct.
+ *
+ * Note: This does *not* (physically) delete any added content.
+ * Make sure to call DnDDroppedFilesReset() then.
+ *
+ * @param pDF DnD Dropped Files to destroy.
+ */
+void DnDDroppedFilesDestroy(PDNDDROPPEDFILES pDF)
+{
+ /* Only make sure to not leak any handles and stuff, don't delete any
+ * directories / files here. */
+ dndDroppedFilesCloseInternal(pDF);
+
+ RTStrFree(pDF->pszPathAbs);
+ pDF->pszPathAbs = NULL;
+}
+
+/**
+ * Adds a file reference to a Dropped Files directory.
+ *
+ * @returns VBox status code.
+ * @param pDF DnD Dropped Files to add file to.
+ * @param pszFile Path of file entry to add.
+ */
+int DnDDroppedFilesAddFile(PDNDDROPPEDFILES pDF, const char *pszFile)
+{
+ AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
+
+ PDNDDROPPEDFILESENTRY pEntry = (PDNDDROPPEDFILESENTRY)RTMemAlloc(sizeof(DNDDROPPEDFILESENTRY));
+ if (!pEntry)
+ return VERR_NO_MEMORY;
+
+ pEntry->pszPath = RTStrDup(pszFile);
+ if (pEntry->pszPath)
+ {
+ RTListAppend(&pDF->m_lstFiles, &pEntry->Node);
+ return VINF_SUCCESS;
+ }
+
+ RTMemFree(pEntry);
+ return VERR_NO_MEMORY;
+}
+
+/**
+ * Adds a directory reference to a Dropped Files directory.
+ *
+ * Note: This does *not* (recursively) add sub entries.
+ *
+ * @returns VBox status code.
+ * @param pDF DnD Dropped Files to add directory to.
+ * @param pszDir Path of directory entry to add.
+ */
+int DnDDroppedFilesAddDir(PDNDDROPPEDFILES pDF, const char *pszDir)
+{
+ AssertPtrReturn(pszDir, VERR_INVALID_POINTER);
+
+ PDNDDROPPEDFILESENTRY pEntry = (PDNDDROPPEDFILESENTRY)RTMemAlloc(sizeof(DNDDROPPEDFILESENTRY));
+ if (!pEntry)
+ return VERR_NO_MEMORY;
+
+ pEntry->pszPath = RTStrDup(pszDir);
+ if (pEntry->pszPath)
+ {
+ RTListAppend(&pDF->m_lstDirs, &pEntry->Node);
+ return VINF_SUCCESS;
+ }
+
+ RTMemFree(pEntry);
+ return VERR_NO_MEMORY;
+}
+
+/**
+ * Closes the dropped files directory handle, internal version.
+ *
+ * @returns VBox status code.
+ * @param pDF DnD Dropped Files to close.
+ */
+static int dndDroppedFilesCloseInternal(PDNDDROPPEDFILES pDF)
+{
+ int rc;
+ if (pDF->m_hDir != NULL)
+ {
+ rc = RTDirClose(pDF->m_hDir);
+ if (RT_SUCCESS(rc))
+ pDF->m_hDir = NULL;
+ }
+ else
+ rc = VINF_SUCCESS;
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Closes the dropped files directory handle.
+ *
+ * @returns VBox status code.
+ * @param pDF DnD Dropped Files to close.
+ */
+int DnDDroppedFilesClose(PDNDDROPPEDFILES pDF)
+{
+ return dndDroppedFilesCloseInternal(pDF);
+}
+
+/**
+ * Returns the absolute path of the dropped files directory.
+ *
+ * @returns Pointer to absolute path of the dropped files directory.
+ * @param pDF DnD Dropped Files return absolute path of the dropped files directory for.
+ */
+const char *DnDDroppedFilesGetDirAbs(PDNDDROPPEDFILES pDF)
+{
+ return pDF->pszPathAbs;
+}
+
+/**
+ * Returns whether the dropped files directory has been opened or not.
+ *
+ * @returns \c true if open, \c false if not.
+ * @param pDF DnD Dropped Files to return open status for.
+ */
+bool DnDDroppedFilesIsOpen(PDNDDROPPEDFILES pDF)
+{
+ return (pDF->m_hDir != NULL);
+}
+
+/**
+ * Opens (creates) the dropped files directory.
+ *
+ * @returns VBox status code.
+ * @param pDF DnD Dropped Files to open.
+ * @param pszPath Absolute path where to create the dropped files directory.
+ * @param fFlags Dropped files flags to use for this directory.
+ */
+int DnDDroppedFilesOpenEx(PDNDDROPPEDFILES pDF,
+ 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 szDropDir[RTPATH_MAX];
+ RTStrPrintf(szDropDir, sizeof(szDropDir), "%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(szDropDir, sizeof(szDropDir), "VirtualBox Dropped Files"); /** @todo Make this tag configurable? */
+ if (RT_FAILURE(rc))
+ break;
+
+ /* Create it when necessary. */
+ if (!RTDirExists(szDropDir))
+ {
+ rc = RTDirCreateFullPath(szDropDir, 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 szTime[64];
+ RTTIMESPEC time;
+ if (!RTTimeSpecToString(RTTimeNow(&time), szTime, sizeof(szTime)))
+ {
+ rc = VERR_BUFFER_OVERFLOW;
+ break;
+ }
+
+ rc = DnDPathSanitizeFileName(szTime, sizeof(szTime));
+ if (RT_FAILURE(rc))
+ break;
+
+ rc = RTPathAppend(szDropDir, sizeof(szDropDir), szTime);
+ if (RT_FAILURE(rc))
+ break;
+
+ /* Create it (only accessible by the current user) */
+ rc = RTDirCreateUniqueNumbered(szDropDir, sizeof(szDropDir), RTFS_UNIX_IRWXU, 3, '-');
+ if (RT_SUCCESS(rc))
+ {
+ RTDIR hDir;
+ rc = RTDirOpen(&hDir, szDropDir);
+ if (RT_SUCCESS(rc))
+ {
+ pDF->pszPathAbs = RTStrDup(szDropDir);
+ AssertPtrBreakStmt(pDF->pszPathAbs, rc = VERR_NO_MEMORY);
+ pDF->m_hDir = hDir;
+ pDF->m_fOpen = fFlags;
+ }
+ }
+
+ } while (0);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Opens (creates) the dropped files directory in the system's temp directory.
+ *
+ * @returns VBox status code.
+ * @param pDF DnD Dropped Files to open.
+ * @param fFlags Dropped files flags to use for this directory.
+ */
+int DnDDroppedFilesOpenTemp(PDNDDROPPEDFILES pDF, DNDURIDROPPEDFILEFLAGS fFlags)
+{
+ 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 = DnDDroppedFilesOpenEx(pDF, szTemp, fFlags);
+
+ return rc;
+}
+
+/**
+ * Free's an internal DnD Dropped Files entry.
+ *
+ * @param pEntry Pointer to entry to free. The pointer will be invalid after calling.
+ */
+static void dndDroppedFilesEntryFree(PDNDDROPPEDFILESENTRY pEntry)
+{
+ if (!pEntry)
+ return;
+ RTStrFree(pEntry->pszPath);
+ RTListNodeRemove(&pEntry->Node);
+ RTMemFree(pEntry);
+}
+
+/**
+ * Resets an internal DnD Dropped Files list.
+ *
+ * @param pListAnchor Pointer to list (anchor) to reset.
+ */
+static void dndDroppedFilesResetList(PRTLISTANCHOR pListAnchor)
+{
+ PDNDDROPPEDFILESENTRY pEntryCur, pEntryNext;
+ RTListForEachSafe(pListAnchor, pEntryCur, pEntryNext, DNDDROPPEDFILESENTRY, Node)
+ dndDroppedFilesEntryFree(pEntryCur);
+ Assert(RTListIsEmpty(pListAnchor));
+}
+
+/**
+ * Resets a droppped files directory.
+ *
+ * @returns VBox status code.
+ * @param pDF DnD Dropped Files to reset.
+ * @param fDelete Whether to physically delete the directory and its content
+ * or just clear the internal references.
+ */
+int DnDDroppedFilesReset(PDNDDROPPEDFILES pDF, bool fDelete)
+{
+ int rc = dndDroppedFilesCloseInternal(pDF);
+ if (RT_SUCCESS(rc))
+ {
+ if (fDelete)
+ {
+ rc = DnDDroppedFilesRollback(pDF);
+ }
+ else
+ {
+ dndDroppedFilesResetList(&pDF->m_lstDirs);
+ dndDroppedFilesResetList(&pDF->m_lstFiles);
+ }
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Re-opens a droppes files directory.
+ *
+ * @returns VBox status code, or VERR_NOT_FOUND if the dropped files directory has not been opened before.
+ * @param pDF DnD Dropped Files to re-open.
+ */
+int DnDDroppedFilesReopen(PDNDDROPPEDFILES pDF)
+{
+ if (!pDF->pszPathAbs)
+ return VERR_NOT_FOUND;
+
+ return DnDDroppedFilesOpenEx(pDF, pDF->pszPathAbs, pDF->m_fOpen);
+}
+
+/**
+ * Performs a rollback of a dropped files directory.
+ * This cleans the directory by physically deleting all files / directories which have been added before.
+ *
+ * @returns VBox status code.
+ * @param pDF DnD Dropped Files to roll back.
+ */
+int DnDDroppedFilesRollback(PDNDDROPPEDFILES pDF)
+{
+ if (!pDF->pszPathAbs)
+ 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;
+ PDNDDROPPEDFILESENTRY pEntryCur, pEntryNext;
+ RTListForEachSafe(&pDF->m_lstFiles, pEntryCur, pEntryNext, DNDDROPPEDFILESENTRY, Node)
+ {
+ rc2 = RTFileDelete(pEntryCur->pszPath);
+ if (RT_SUCCESS(rc2))
+ dndDroppedFilesEntryFree(pEntryCur);
+ else if (RT_SUCCESS(rc))
+ rc = rc2;
+ /* Keep going. */
+ }
+
+ RTListForEachSafe(&pDF->m_lstDirs, pEntryCur, pEntryNext, DNDDROPPEDFILESENTRY, Node)
+ {
+ rc2 = RTDirRemove(pEntryCur->pszPath);
+ if (RT_SUCCESS(rc2))
+ dndDroppedFilesEntryFree(pEntryCur);
+ else if (RT_SUCCESS(rc))
+ rc = rc2;
+ /* Keep going. */
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ rc2 = dndDroppedFilesCloseInternal(pDF);
+ 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(pDF->pszPathAbs);
+ }
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+