Index: trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-internal.h
===================================================================
--- trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-internal.h	(revision 100291)
+++ trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-internal.h	(revision 100367)
@@ -282,6 +282,4 @@
 extern SHCLEXTSTATE g_ExtState;
 
-int shClSvcSetSource(PSHCLCLIENT pClient, SHCLSOURCE enmSource);
-
 void shClSvcMsgQueueReset(PSHCLCLIENT pClient);
 PSHCLCLIENTMSG shClSvcMsgAlloc(PSHCLCLIENT pClient, uint32_t uMsg, uint32_t cParms);
@@ -302,13 +300,4 @@
 int shClSvcClientWakeup(PSHCLCLIENT pClient);
 
-# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
-int shClSvcTransferModeSet(uint32_t fMode);
-int shClSvcTransferInit(PSHCLCLIENT pClient, SHCLTRANSFERDIR enmDir, SHCLSOURCE enmSource, PSHCLTRANSFER *ppTransfer);
-int shClSvcTransferStart(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer);
-int shClSvcTransferStop(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, bool fWaitForGuest);
-bool shClSvcTransferMsgIsAllowed(uint32_t uMode, uint32_t uMsg);
-void shClSvcClientTransfersReset(PSHCLCLIENT pClient);
-#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
-
 /** @name Service functions, accessible by the backends.
  * Locking is between the (host) service thread and the platform-dependent (window) thread.
@@ -316,4 +305,5 @@
  */
 int ShClSvcReadDataFromGuestAsync(PSHCLCLIENT pClient, SHCLFORMATS fFormats, PSHCLEVENT *ppEvent);
+int ShClSvcReadDataFromGuest(PSHCLCLIENT pClient, SHCLFORMAT uFmt, void **ppv, uint32_t *pcb);
 int ShClSvcGuestDataSignal(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat, void *pvData, uint32_t cbData);
 int ShClSvcHostReportFormats(PSHCLCLIENT pClient, SHCLFORMATS fFormats);
@@ -451,23 +441,26 @@
  */
 /**
- * Called after a transfer got created.
+ * Called before a transfer gets destroyed.
  *
  * @returns VBox status code.
  * @param   pBackend            Shared Clipboard backend to use.
  * @param   pClient             Shared Clipboard client context.
- * @param   pTransfer           Shared Clipboard transfer created.
- */
-int ShClBackendTransferCreate(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer);
-/**
- * Called before a transfer gets destroyed.
+ * @param   pTransfer           Shared Clipboard transfer to destroy.
+ */
+int ShClBackendTransferDestroy(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer);
+/**
+ * Called after a transfer status got processed.
  *
  * @returns VBox status code.
  * @param   pBackend            Shared Clipboard backend to use.
  * @param   pClient             Shared Clipboard client context.
- * @param   pTransfer           Shared Clipboard transfer to destroy.
- */
-int ShClBackendTransferDestroy(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer);
-/**
- * Called when getting (determining) the transfer roots on the host side.
+ * @param   pTransfer           Shared Clipboard transfer to process status for.
+ * @param   enmSource           Transfer source which issues the reply.
+ * @param   enmStatus           Transfer status.
+ * @param   rcStatus            Status code (IPRT-style). Depends on \a enmStatus set.
+ */
+int ShClBackendTransferHandleStatusReply(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, SHCLSOURCE enmSource, SHCLTRANSFERSTATUS enmStatus, int rcStatus);
+/**
+ * Called when the guest wants to read the transfer roots.
  *
  * @returns VBox status code.
@@ -476,5 +469,5 @@
  * @param   pTransfer           Shared Clipboard transfer to get roots for.
  */
-int ShClBackendTransferGetRoots(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer);
+int ShClBackendTransferHGRootListRead(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer);
 /** @} */
 #endif
@@ -497,17 +490,17 @@
 
 int shClSvcTransferIfaceRootsGet(PSHCLTXPROVIDERCTX pCtx, PSHCLLIST pRootList);
-int shClSvcTransferIfaceListOpen(PSHCLTXPROVIDERCTX pCtx, PSHCLLISTOPENPARMS pOpenParms, PSHCLLISTHANDLE phList);
-int shClSvcTransferIfaceListClose(PSHCLTXPROVIDERCTX pCtx, SHCLLISTHANDLE hList);
-int shClSvcTransferIfaceListHdrRead(PSHCLTXPROVIDERCTX pCtx, SHCLLISTHANDLE hList, PSHCLLISTHDR pListHdr);
-int shClSvcTransferIfaceListHdrWrite(PSHCLTXPROVIDERCTX pCtx, SHCLLISTHANDLE hList, PSHCLLISTHDR pListHdr);
-int shClSvcTransferIfaceListEntryRead(PSHCLTXPROVIDERCTX pCtx, SHCLLISTHANDLE hList, PSHCLLISTENTRY pListEntry);
-int shClSvcTransferIfaceListEntryWrite(PSHCLTXPROVIDERCTX pCtx, SHCLLISTHANDLE hList, PSHCLLISTENTRY pListEntry);
-
-int shClSvcTransferIfaceObjOpen(PSHCLTXPROVIDERCTX pCtx, PSHCLOBJOPENCREATEPARMS pCreateParms,
+int shClSvcTransferIfaceGHListOpen(PSHCLTXPROVIDERCTX pCtx, PSHCLLISTOPENPARMS pOpenParms, PSHCLLISTHANDLE phList);
+int shClSvcTransferIfaceGHListClose(PSHCLTXPROVIDERCTX pCtx, SHCLLISTHANDLE hList);
+int shClSvcTransferIfaceGHListHdrRead(PSHCLTXPROVIDERCTX pCtx, SHCLLISTHANDLE hList, PSHCLLISTHDR pListHdr);
+int shClSvcTransferIfaceHGListHdrWrite(PSHCLTXPROVIDERCTX pCtx, SHCLLISTHANDLE hList, PSHCLLISTHDR pListHdr);
+int shClSvcTransferIfaceGHListEntryRead(PSHCLTXPROVIDERCTX pCtx, SHCLLISTHANDLE hList, PSHCLLISTENTRY pListEntry);
+int shClSvcTransferIfaceHGListEntryWrite(PSHCLTXPROVIDERCTX pCtx, SHCLLISTHANDLE hList, PSHCLLISTENTRY pListEntry);
+
+int shClSvcTransferIfaceGHObjOpen(PSHCLTXPROVIDERCTX pCtx, PSHCLOBJOPENCREATEPARMS pCreateParms,
                                 PSHCLOBJHANDLE phObj);
-int shClSvcTransferIfaceObjClose(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj);
-int shClSvcTransferIfaceObjRead(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj,
+int shClSvcTransferIfaceGHObjClose(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj);
+int shClSvcTransferIfaceGHObjRead(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj,
                                 void *pvData, uint32_t cbData, uint32_t fFlags, uint32_t *pcbRead);
-int shClSvcTransferIfaceObjWrite(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj,
+int shClSvcTransferIfaceHGObjWrite(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj,
                                  void *pvData, uint32_t cbData, uint32_t fFlags, uint32_t *pcbWritten);
 /** @} */
Index: trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-transfers.cpp
===================================================================
--- trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-transfers.cpp	(revision 100291)
+++ trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-transfers.cpp	(revision 100367)
@@ -61,20 +61,15 @@
 *   Prototypes                                                                                                                   *
 *********************************************************************************************************************************/
-static int shClSvcTransferSetListOpen(uint32_t cParms, VBOXHGCMSVCPARM aParms[],
-                                      uint64_t idCtx, PSHCLLISTOPENPARMS pOpenParms);
-static int shClSvcTransferSetListClose(uint32_t cParms, VBOXHGCMSVCPARM aParms[],
-                                       uint64_t idCtx, SHCLLISTHANDLE hList);
-
-
-/*********************************************************************************************************************************
-*   Provider implementation                                                                                                      *
-*********************************************************************************************************************************/
-
-/**
- * Resets all transfers of a Shared Clipboard client.
- *
- * @param   pClient             Client to reset transfers for.
- */
-void shClSvcClientTransfersReset(PSHCLCLIENT pClient)
+static int shClSvcTransferSendStatusAsyncInternal(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, SHCLTRANSFERSTATUS uStatus, int rcTransfer, PSHCLEVENT *ppEvent);
+static int shClSvcTransferSetListOpen(uint32_t cParms, VBOXHGCMSVCPARM aParms[], uint64_t idCtx, PSHCLLISTOPENPARMS pOpenParms);
+static int shClSvcTransferSetListClose(uint32_t cParms, VBOXHGCMSVCPARM aParms[], uint64_t idCtx, SHCLLISTHANDLE hList);
+
+
+/**
+ * Destroys all transfers of a Shared Clipboard client.
+ *
+ * @param   pClient             Client to destroy transfers for.
+ */
+void shClSvcTransferDestroyAll(PSHCLCLIENT pClient)
 {
     if (!pClient)
@@ -83,11 +78,189 @@
     LogFlowFuncEnter();
 
-    /* Make sure to let the backend know that all transfers are getting destroyed. */
+    /* Unregister and destroy all transfers.
+     * Also make sure to let the backend know that all transfers are getting destroyed. */
     uint32_t      uIdx = 0;
     PSHCLTRANSFER pTransfer;
     while ((pTransfer = ShClTransferCtxGetTransferByIndex(&pClient->Transfers.Ctx, uIdx++)))
-        ShClBackendTransferDestroy(pClient->pBackend, pClient, pTransfer);
-
-    ShClTransferCtxDestroy(&pClient->Transfers.Ctx);
+        ShClSvcTransferDestroy(pClient, pTransfer);
+}
+
+/**
+ * Reads a root list header from the guest, asynchronous version.
+ *
+ * @returns VBox status code.
+ * @param   pClient             Client to read from.
+ * @param   idTransfer          Transfer ID to read root list header for.
+ * @param   ppEvent             Where to return the event to wait for.
+ *                              Must be released by the caller with ShClEventRelease().
+ */
+int ShClSvcTransferGHRootListReadHdrAsync(PSHCLCLIENT pClient, SHCLTRANSFERID idTransfer, PSHCLEVENT *ppEvent)
+{
+    LogFlowFuncEnter();
+
+    int rc;
+
+    PSHCLCLIENTMSG pMsgHdr = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_HDR_READ,
+                                             VBOX_SHCL_CPARMS_ROOT_LIST_HDR_READ_REQ);
+    if (pMsgHdr)
+    {
+        PSHCLEVENT pEvent;
+        rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
+        if (RT_SUCCESS(rc))
+        {
+            HGCMSvcSetU64(&pMsgHdr->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
+                                                                        idTransfer, pEvent->idEvent));
+            HGCMSvcSetU32(&pMsgHdr->aParms[1], 0 /* fRoots */);
+
+            shClSvcClientLock(pClient);
+
+            shClSvcMsgAdd(pClient, pMsgHdr, true /* fAppend */);
+            rc = shClSvcClientWakeup(pClient);
+
+            shClSvcClientUnlock(pClient);
+
+            /* Remove event from list if caller did not request event handle or in case
+             * of failure (in this case caller should not release event). */
+            if (   RT_FAILURE(rc)
+                || !ppEvent)
+            {
+                ShClEventRelease(pEvent);
+                pEvent = NULL;
+            }
+            else if (ppEvent)
+                *ppEvent = pEvent;
+        }
+        else
+        {
+            shClSvcMsgFree(pClient, pMsgHdr);
+            rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
+        }
+    }
+    else
+        rc = VERR_NO_MEMORY;
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+/**
+ * Reads a root list header from the guest.
+ *
+ * @returns VBox status code.
+ * @param   pClient             Client to read from.
+ * @param   idTransfer          Transfer ID to read root list header for.
+ * @param   pHdr                Where to store the root list header on succeess.
+ */
+int ShClSvcTransferGHRootListReadHdr(PSHCLCLIENT pClient, SHCLTRANSFERID idTransfer, PSHCLLISTHDR pHdr)
+{
+    PSHCLEVENT pEvent;
+    int rc = ShClSvcTransferGHRootListReadHdrAsync(pClient, idTransfer, &pEvent);
+    if (RT_SUCCESS(rc))
+    {
+        PSHCLEVENTPAYLOAD pPayload;
+        rc = ShClEventWait(pEvent, SHCL_TIMEOUT_DEFAULT_MS, &pPayload);
+        if (RT_SUCCESS(rc))
+        {
+            Assert(pPayload->cbData == sizeof(SHCLLISTHDR));
+
+            memcpy(pHdr, (PSHCLLISTHDR)pPayload->pvData, sizeof(SHCLLISTHDR));
+
+            LogFlowFunc(("cRoots=%RU32, fFeatures=0x%x\n", pHdr->cEntries, pHdr->fFeatures));
+
+            ShClPayloadFree(pPayload);
+        }
+
+        ShClEventRelease(pEvent);
+        pEvent = NULL;
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+/**
+ * Reads a root list entry from the guest, asynchronous version.
+ *
+ * @returns VBox status code.
+ * @param   pClient             Client to read from.
+ * @param   idTransfer          Transfer ID to read root list header for.
+ * @param   idxEntry            Index of entry to read.
+ * @param   ppEvent             Where to return the event to wait for.
+ *                              Must be released by the caller with ShClEventRelease().
+ */
+int ShClSvcTransferGHRootListReadEntryAsync(PSHCLCLIENT pClient, SHCLTRANSFERID idTransfer, uint64_t idxEntry,
+                                            PSHCLEVENT *ppEvent)
+{
+    LogFlowFuncEnter();
+
+    PSHCLCLIENTMSG pMsgEntry = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_ENTRY_READ,
+                                               VBOX_SHCL_CPARMS_ROOT_LIST_ENTRY_READ_REQ);
+
+    PSHCLEVENT pEvent;
+    int rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
+    if (RT_SUCCESS(rc))
+    {
+        HGCMSvcSetU64(&pMsgEntry->aParms[0],
+                      VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uClientID, idTransfer, pEvent->idEvent));
+        HGCMSvcSetU32(&pMsgEntry->aParms[1], 0 /* fFeatures */);
+        HGCMSvcSetU64(&pMsgEntry->aParms[2], idxEntry /* uIndex */);
+
+        shClSvcClientLock(pClient);
+
+        shClSvcMsgAdd(pClient, pMsgEntry, true /* fAppend */);
+        rc = shClSvcClientWakeup(pClient);
+
+        shClSvcClientUnlock(pClient);
+
+        /* Remove event from list if caller did not request event handle or in case
+         * of failure (in this case caller should not release event). */
+        if (   RT_FAILURE(rc)
+            || !ppEvent)
+        {
+            ShClEventRelease(pEvent);
+            pEvent = NULL;
+        }
+        else if (ppEvent)
+            *ppEvent = pEvent;
+    }
+    else
+        rc = VERR_NO_MEMORY;
+
+    LogFlowFuncLeave();
+    return rc;
+}
+
+/**
+ * Reads a root list entry from the guest.
+ *
+ * @returns VBox status code.
+ * @param   pClient             Client to read from.
+ * @param   idTransfer          Transfer ID to read root list header for.
+ * @param   idxEntry            Index of entry to read.
+ * @param   ppListEntry         Where to return the allocated root list entry.
+ */
+int ShClSvcTransferGHRootListReadEntry(PSHCLCLIENT pClient, SHCLTRANSFERID idTransfer, uint64_t idxEntry,
+                                       PSHCLLISTENTRY *ppListEntry)
+{
+    AssertPtrReturn(ppListEntry, VERR_INVALID_POINTER);
+
+    PSHCLEVENT pEvent;
+    int rc = ShClSvcTransferGHRootListReadEntryAsync(pClient, idTransfer, idxEntry, &pEvent);
+    if (RT_SUCCESS(rc))
+    {
+        PSHCLEVENTPAYLOAD pPayload;
+        rc = ShClEventWait(pEvent, SHCL_TIMEOUT_DEFAULT_MS, &pPayload);
+        if (RT_SUCCESS(rc))
+        {
+            *ppListEntry = (PSHCLLISTENTRY)pPayload->pvData; /* ppLisEntry own pPayload-pvData now. */
+
+        }
+
+        ShClEventRelease(pEvent);
+        pEvent = NULL;
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
 }
 
@@ -98,5 +271,5 @@
 
 /** @copydoc SHCLTXPROVIDERIFACE::pfnRootListRead */
-DECLCALLBACK(int) shClSvcTransferIfaceRootListRead(PSHCLTXPROVIDERCTX pCtx)
+DECLCALLBACK(int) shClSvcTransferIfaceGHRootListRead(PSHCLTXPROVIDERCTX pCtx)
 {
     LogFlowFuncEnter();
@@ -105,96 +278,21 @@
     AssertPtr(pClient);
 
-    int rc;
-
-    PSHCLCLIENTMSG pMsgHdr = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_HDR_READ,
-                                             VBOX_SHCL_CPARMS_ROOT_LIST_HDR_READ_REQ);
-    if (pMsgHdr)
-    {
-        PSHCLEVENT pEvent;
-        rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
-        if (RT_SUCCESS(rc))
-        {
-            HGCMSvcSetU64(&pMsgHdr->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
-                                                                        pCtx->pTransfer->State.uID, pEvent->idEvent));
-            HGCMSvcSetU32(&pMsgHdr->aParms[1], 0 /* fRoots */);
-
-            shClSvcClientLock(pClient);
-
-            shClSvcMsgAdd(pClient, pMsgHdr, true /* fAppend */);
-            rc = shClSvcClientWakeup(pClient);
-
-            shClSvcClientUnlock(pClient);
-
+    SHCLTRANSFERID const idTransfer = ShClTransferGetID(pCtx->pTransfer);
+
+    SHCLLISTHDR Hdr;
+    int rc = ShClSvcTransferGHRootListReadHdr(pClient, idTransfer, &Hdr);
+    if (RT_SUCCESS(rc))
+    {
+        for (uint64_t i = 0; i < Hdr.cEntries; i++)
+        {
+            PSHCLLISTENTRY pEntry;
+            rc = ShClSvcTransferGHRootListReadEntry(pClient, idTransfer, i, &pEntry);
             if (RT_SUCCESS(rc))
-            {
-                PSHCLEVENTPAYLOAD pPayloadHdr;
-                rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayloadHdr);
-                if (RT_SUCCESS(rc))
-                {
-                    PSHCLLISTHDR pRootListHdr = (PSHCLLISTHDR)pPayloadHdr->pvData;
-                    Assert(pPayloadHdr->cbData == sizeof(SHCLLISTHDR));
-
-                    LogFlowFunc(("cRoots=%RU32, fFeatures=0x%x\n", pRootListHdr->cEntries, pRootListHdr->fFeatures));
-
-                    for (uint32_t i = 0; i < pRootListHdr->cEntries; i++)
-                    {
-                        PSHCLCLIENTMSG pMsgEntry = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_ENTRY_READ,
-                                                                   VBOX_SHCL_CPARMS_ROOT_LIST_ENTRY_READ_REQ);
-
-                        PSHCLEVENT pEventRootEntry;
-                        rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEventRootEntry);
-                        if (RT_SUCCESS(rc))
-                        {
-                            HGCMSvcSetU64(&pMsgEntry->aParms[0],
-                                          VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uClientID,
-                                                                   pCtx->pTransfer->State.uID, pEventRootEntry->idEvent));
-                            HGCMSvcSetU32(&pMsgEntry->aParms[1], 0 /* fFeatures */);
-                            HGCMSvcSetU32(&pMsgEntry->aParms[2], i /* uIndex */);
-
-                            shClSvcClientLock(pClient);
-                            shClSvcMsgAdd(pClient, pMsgEntry, true /* fAppend */);
-                            shClSvcClientUnlock(pClient);
-
-                            PSHCLEVENTPAYLOAD pPayloadEntry;
-                            rc = ShClEventWait(pEventRootEntry, pCtx->pTransfer->uTimeoutMs, &pPayloadEntry);
-                            if (RT_FAILURE(rc))
-                                break;
-
-                            PSHCLLISTENTRY pRootListEntry = (PSHCLLISTENTRY)pPayloadEntry->pvData;
-                            Assert(pPayloadEntry->cbData == sizeof(SHCLLISTENTRY));
-
-                            rc = ShClTransferListAddEntry(&pCtx->pTransfer->lstRoots, pRootListEntry, true /* fAppend */);
-                            if (RT_FAILURE(rc))
-                                ShClPayloadFree(pPayloadEntry);
-                            /* else don't call ShClPayloadFree() here, as pRootList own the data now. */
-                            pPayloadEntry = NULL;
-
-                            ShClEventRelease(pEventRootEntry);
-                            pEventRootEntry = NULL;
-                        }
-                        else
-                        {
-                            shClSvcMsgFree(pClient, pMsgEntry);
-                            rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
-                        }
-
-                        if (RT_FAILURE(rc))
-                            break;
-                    }
-
-                    ShClPayloadFree(pPayloadHdr);
-                }
-            }
-
-            ShClEventRelease(pEvent);
-        }
-        else
-        {
-            shClSvcMsgFree(pClient, pMsgHdr);
-            rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
-        }
-    }
-    else
-        rc = VERR_NO_MEMORY;
+                rc = ShClTransferListAddEntry(&pCtx->pTransfer->lstRoots, pEntry, true /* fAppend */);
+
+            if (RT_FAILURE(rc))
+                break;
+        }
+    }
 
     LogFlowFuncLeave();
@@ -203,6 +301,6 @@
 
 /** @copydoc SHCLTXPROVIDERIFACE::pfnListOpen */
-DECLCALLBACK(int) shClSvcTransferIfaceListOpen(PSHCLTXPROVIDERCTX pCtx,
-                                               PSHCLLISTOPENPARMS pOpenParms, PSHCLLISTHANDLE phList)
+DECLCALLBACK(int) shClSvcTransferIfaceGHListOpen(PSHCLTXPROVIDERCTX pCtx,
+                                                 PSHCLLISTOPENPARMS pOpenParms, PSHCLLISTHANDLE phList)
 {
     LogFlowFuncEnter();
@@ -245,5 +343,5 @@
                         AssertPtr(pReply);
 
-                        Assert(pReply->uType == VBOX_SHCL_REPLYMSGTYPE_LIST_OPEN);
+                        Assert(pReply->uType == VBOX_SHCL_TX_REPLYMSGTYPE_LIST_OPEN);
 
                         LogFlowFunc(("hList=%RU64\n", pReply->u.ListOpen.uHandle));
@@ -272,5 +370,5 @@
 
 /** @copydoc SHCLTXPROVIDERIFACE::pfnListClose */
-DECLCALLBACK(int) shClSvcTransferIfaceListClose(PSHCLTXPROVIDERCTX pCtx, SHCLLISTHANDLE hList)
+DECLCALLBACK(int) shClSvcTransferIfaceGHListClose(PSHCLTXPROVIDERCTX pCtx, SHCLLISTHANDLE hList)
 {
     LogFlowFuncEnter();
@@ -327,6 +425,6 @@
 
 /** @copydoc SHCLTXPROVIDERIFACE::pfnListHdrRead */
-DECLCALLBACK(int) shClSvcTransferIfaceListHdrRead(PSHCLTXPROVIDERCTX pCtx,
-                                                  SHCLLISTHANDLE hList, PSHCLLISTHDR pListHdr)
+DECLCALLBACK(int) shClSvcTransferIfaceGHListHdrRead(PSHCLTXPROVIDERCTX pCtx,
+                                                    SHCLLISTHANDLE hList, PSHCLLISTHDR pListHdr)
 {
     LogFlowFuncEnter();
@@ -387,6 +485,6 @@
 
 /** @copydoc SHCLTXPROVIDERIFACE::pfnListHdrWrite */
-DECLCALLBACK(int) shClSvcTransferIfaceListHdrWrite(PSHCLTXPROVIDERCTX pCtx,
-                                                   SHCLLISTHANDLE hList, PSHCLLISTHDR pListHdr)
+DECLCALLBACK(int) shClSvcTransferIfaceHGListHdrWrite(PSHCLTXPROVIDERCTX pCtx,
+                                                     SHCLLISTHANDLE hList, PSHCLLISTHDR pListHdr)
 {
     RT_NOREF(pCtx, hList, pListHdr);
@@ -398,6 +496,6 @@
 
 /** @copydoc SHCLTXPROVIDERIFACE::pfnListEntryRead */
-DECLCALLBACK(int) shClSvcTransferIfaceListEntryRead(PSHCLTXPROVIDERCTX pCtx,
-                                                    SHCLLISTHANDLE hList, PSHCLLISTENTRY pListEntry)
+DECLCALLBACK(int) shClSvcTransferIfaceGHListEntryRead(PSHCLTXPROVIDERCTX pCtx,
+                                                      SHCLLISTHANDLE hList, PSHCLLISTENTRY pListEntry)
 {
     LogFlowFuncEnter();
@@ -458,6 +556,6 @@
 
 /** @copydoc SHCLTXPROVIDERIFACE::pfnListEntryWrite */
-DECLCALLBACK(int) shClSvcTransferIfaceListEntryWrite(PSHCLTXPROVIDERCTX pCtx,
-                                                     SHCLLISTHANDLE hList, PSHCLLISTENTRY pListEntry)
+DECLCALLBACK(int) shClSvcTransferIfaceHGListEntryWrite(PSHCLTXPROVIDERCTX pCtx,
+                                                       SHCLLISTHANDLE hList, PSHCLLISTENTRY pListEntry)
 {
     RT_NOREF(pCtx, hList, pListEntry);
@@ -469,5 +567,5 @@
 
 /** @copydoc SHCLTXPROVIDERIFACE::pfnObjOpen */
-DECLCALLBACK(int) shClSvcTransferIfaceObjOpen(PSHCLTXPROVIDERCTX pCtx, PSHCLOBJOPENCREATEPARMS pCreateParms, PSHCLOBJHANDLE phObj)
+DECLCALLBACK(int) shClSvcTransferIfaceGHObjOpen(PSHCLTXPROVIDERCTX pCtx, PSHCLOBJOPENCREATEPARMS pCreateParms, PSHCLOBJHANDLE phObj)
 {
     LogFlowFuncEnter();
@@ -514,5 +612,5 @@
                     AssertPtr(pReply);
 
-                    Assert(pReply->uType == VBOX_SHCL_REPLYMSGTYPE_OBJ_OPEN);
+                    Assert(pReply->uType == VBOX_SHCL_TX_REPLYMSGTYPE_OBJ_OPEN);
 
                     LogFlowFunc(("hObj=%RU64\n", pReply->u.ObjOpen.uHandle));
@@ -540,5 +638,5 @@
 
 /** @copydoc SHCLTXPROVIDERIFACE::pfnObjClose */
-DECLCALLBACK(int) shClSvcTransferIfaceObjClose(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj)
+DECLCALLBACK(int) shClSvcTransferIfaceGHObjClose(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj)
 {
     LogFlowFuncEnter();
@@ -579,5 +677,5 @@
                     AssertPtr(pReply);
 
-                    Assert(pReply->uType == VBOX_SHCL_REPLYMSGTYPE_OBJ_CLOSE);
+                    Assert(pReply->uType == VBOX_SHCL_TX_REPLYMSGTYPE_OBJ_CLOSE);
 
                     LogFlowFunc(("hObj=%RU64\n", pReply->u.ObjClose.uHandle));
@@ -603,6 +701,6 @@
 
 /** @copydoc SHCLTXPROVIDERIFACE::pfnObjRead */
-DECLCALLBACK(int) shClSvcTransferIfaceObjRead(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj,
-                                              void *pvData, uint32_t cbData, uint32_t fFlags, uint32_t *pcbRead)
+DECLCALLBACK(int) shClSvcTransferIfaceGHObjRead(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj,
+                                                void *pvData, uint32_t cbData, uint32_t fFlags, uint32_t *pcbRead)
 {
     LogFlowFuncEnter();
@@ -672,6 +770,6 @@
 
 /** @copydoc SHCLTXPROVIDERIFACE::pfnObjWrite */
-DECLCALLBACK(int) shClSvcTransferIfaceObjWrite(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj,
-                                               void *pvData, uint32_t cbData, uint32_t fFlags, uint32_t *pcbWritten)
+DECLCALLBACK(int) shClSvcTransferIfaceHGObjWrite(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj,
+                                                 void *pvData, uint32_t cbData, uint32_t fFlags, uint32_t *pcbWritten)
 {
     LogFlowFuncEnter();
@@ -851,5 +949,5 @@
             switch (pReply->uType)
             {
-                case VBOX_SHCL_REPLYMSGTYPE_TRANSFER_STATUS:
+                case VBOX_SHCL_TX_REPLYMSGTYPE_TRANSFER_STATUS:
                 {
                     if (cParms > idxParm)
@@ -860,5 +958,5 @@
                 }
 
-                case VBOX_SHCL_REPLYMSGTYPE_LIST_OPEN:
+                case VBOX_SHCL_TX_REPLYMSGTYPE_LIST_OPEN:
                 {
                     if (cParms > idxParm)
@@ -869,5 +967,5 @@
                 }
 
-                case VBOX_SHCL_REPLYMSGTYPE_LIST_CLOSE:
+                case VBOX_SHCL_TX_REPLYMSGTYPE_LIST_CLOSE:
                 {
                     if (cParms > idxParm)
@@ -878,5 +976,5 @@
                 }
 
-                case VBOX_SHCL_REPLYMSGTYPE_OBJ_OPEN:
+                case VBOX_SHCL_TX_REPLYMSGTYPE_OBJ_OPEN:
                 {
                     if (cParms > idxParm)
@@ -887,5 +985,5 @@
                 }
 
-                case VBOX_SHCL_REPLYMSGTYPE_OBJ_CLOSE:
+                case VBOX_SHCL_TX_REPLYMSGTYPE_OBJ_CLOSE:
                 {
                     if (cParms > idxParm)
@@ -1249,14 +1347,13 @@
  * @returns VBox status code.
  * @param   pClient             Pointer to associated client.
- * @param   pTransfer           Pointer to transfer to handle guest reply for.
+ * @param   idTransfer          Transfer ID supplied from the guest.
  * @param   cParms              Number of function parameters supplied.
  * @param   aParms              Array function parameters supplied.
  */
-static int shClSvcTransferHandleReply(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer,
-                                      uint32_t cParms, VBOXHGCMSVCPARM aParms[])
-{
-    RT_NOREF(pClient, pTransfer);
-
+static int shClSvcTransferHandleReply(PSHCLCLIENT pClient, SHCLTRANSFERID idTransfer, uint32_t cParms, VBOXHGCMSVCPARM aParms[])
+{
     int rc;
+
+    PSHCLTRANSFER pTransfer = NULL;
 
     uint32_t   cbReply = sizeof(SHCLREPLY);
@@ -1267,4 +1364,27 @@
         if (RT_SUCCESS(rc))
         {
+            if (   pReply->uType                    == VBOX_SHCL_TX_REPLYMSGTYPE_TRANSFER_STATUS
+                && pReply->u.TransferStatus.uStatus == SHCLTRANSFERSTATUS_REQUESTED)
+            {
+                /* SHCLTRANSFERSTATUS_REQUESTED is special, as it doesn't provide a transfer ID. */
+            }
+            else /* Everything else needs a valid transfer ID. */
+            {
+                pTransfer = ShClTransferCtxGetTransferById(&pClient->Transfers.Ctx, idTransfer);
+                if (!pTransfer)
+                {
+                    LogRel2(("Shared Clipboard: Transfer with ID %RU16 not found\n", idTransfer));
+                    rc = VERR_SHCLPB_TRANSFER_ID_NOT_FOUND;
+                }
+            }
+
+            if (RT_FAILURE(rc))
+            {
+                RTMemFree(pReply);
+                pReply = NULL;
+
+                return rc;
+            }
+
             PSHCLEVENTPAYLOAD pPayload
                 = (PSHCLEVENTPAYLOAD)RTMemAlloc(sizeof(SHCLEVENTPAYLOAD));
@@ -1276,17 +1396,106 @@
                 switch (pReply->uType)
                 {
-                    case VBOX_SHCL_REPLYMSGTYPE_TRANSFER_STATUS:
+                    case VBOX_SHCL_TX_REPLYMSGTYPE_TRANSFER_STATUS:
                     {
-                        LogRel(("Shared Clipboard: Guest reported status %s for transfer %RU32\n",
-                                ShClTransferStatusToStr(pReply->u.TransferStatus.uStatus), pTransfer->State.uID));
+                        /* SHCLTRANSFERSTATUS_REQUESTED is special, as it doesn't provide a transfer ID. */
+                        if (SHCLTRANSFERSTATUS_REQUESTED == pReply->u.TransferStatus.uStatus)
+                        {
+                            LogRel2(("Shared Clipboard: Guest requested a new host -> guest transfer\n"));
+                        }
+                        else
+                            LogRel2(("Shared Clipboard: Guest reported status %s for transfer %RU32\n",
+                                     ShClTransferStatusToStr(pReply->u.TransferStatus.uStatus), idTransfer));
 
                         switch (pReply->u.TransferStatus.uStatus)
                         {
-                            case SHCLTRANSFERSTATUS_INITIALIZED: /* Initialized -> Started */
-                                rc = shClSvcTransferStart(pClient, pTransfer);
+                            case SHCLTRANSFERSTATUS_REQUESTED: /* Guest requests a H->G transfer. */
+                            {
+                                uint32_t const uMode = ShClSvcGetMode();
+                                if (   uMode == VBOX_SHCL_MODE_HOST_TO_GUEST
+                                    || uMode == VBOX_SHCL_MODE_BIDIRECTIONAL)
+                                {
+                                    /* We only create (but not initialize) the transfer here. This is the most lightweight form of
+                                     * having a pending transfer around. Report back the new transfer ID to the guest then. */
+                                    if (pTransfer == NULL) /* Must not exist yet. */
+                                    {
+                                        rc = ShClSvcTransferCreate(pClient, SHCLTRANSFERDIR_TO_REMOTE, SHCLSOURCE_LOCAL,
+                                                                   NIL_SHCLTRANSFERID /* Creates a new transfer ID */,
+                                                                   &pTransfer);
+                                        if (RT_SUCCESS(rc))
+                                        {
+                                            shClSvcClientLock(pClient);
+
+                                            rc = shClSvcTransferSendStatusAsyncInternal(pClient, pTransfer,
+                                                                                        SHCLTRANSFERSTATUS_REQUESTED, VINF_SUCCESS,
+                                                                                        NULL);
+                                            shClSvcClientUnlock(pClient);
+                                        }
+                                    }
+                                    else
+                                        rc = VERR_WRONG_ORDER;
+                                }
+                                else
+                                    rc = VERR_INVALID_PARAMETER;
+
                                 break;
-
-                            case SHCLTRANSFERSTATUS_STARTED:
+                            }
+
+                            case SHCLTRANSFERSTATUS_INITIALIZED: /* Guest reports the transfer as being initialized. */
+                            {
+                                switch (ShClTransferGetDir(pTransfer))
+                                {
+                                    case SHCLTRANSFERDIR_FROM_REMOTE: /* G->H */
+                                        /* Already done locally when creating the transfer. */
+                                        break;
+
+                                    case SHCLTRANSFERDIR_TO_REMOTE:   /* H->G */
+                                    {
+                                        /* Initialize the transfer on the host side. */
+                                        rc = ShClSvcTransferInit(pClient, pTransfer);
+                                        break;
+                                    }
+
+                                    default:
+                                        AssertFailed();
+                                        break;
+                                }
+
                                 break;
+                            }
+                            case SHCLTRANSFERSTATUS_STARTED:     /* Guest has started the transfer on its side. */
+                            {
+                                /* We only need to start for H->G transfers here.
+                                 * For G->H transfers we start this as soon as the host clipboard requests data. */
+                                if (ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_TO_REMOTE)
+                                {
+                                    /* Start the transfer on the host side. */
+                                    rc = ShClSvcTransferStart(pClient, pTransfer);
+                                }
+                                break;
+                            }
+
+                            case SHCLTRANSFERSTATUS_CANCELED:
+                                RT_FALL_THROUGH();
+                            case SHCLTRANSFERSTATUS_KILLED:
+                            {
+                                LogRel(("Shared Clipboard: Guest has %s transfer %RU32\n",
+                                        pReply->uType == SHCLTRANSFERSTATUS_CANCELED ? "canceled" : "killed", pTransfer->State.uID));
+
+                                rc = ShClSvcTransferStop(pClient, pTransfer, false /* fWaitForGuest */);
+
+                                /* Regardless of whether the guest was able to report back and/or stop the transfer, remove the transfer on the host
+                                 * so that we don't risk of having stale transfers here. */
+                                ShClSvcTransferDestroy(pClient, pTransfer);
+                                pTransfer = NULL;
+                                break;
+                            }
+
+                            case SHCLTRANSFERSTATUS_STOPPED:
+                            {
+                                LogRel(("Shared Clipboard: Guest has stopped transfer %RU32\n", pTransfer->State.uID));
+
+                                rc = ShClSvcTransferStop(pClient, pTransfer, false /* fWaitForGuest */);
+                                break;
+                            }
 
                             case SHCLTRANSFERSTATUS_ERROR:
@@ -1294,23 +1503,32 @@
                                 LogRel(("Shared Clipboard: Guest reported error %Rrc for transfer %RU32\n",
                                         pReply->rc, pTransfer->State.uID));
-
-                                rc = shClSvcTransferStop(pClient, pTransfer, false /* fWaitForGuest */);
+                                RT_FALL_THROUGH();
+                            }
+                            default:
+                            {
+                                /* Regardless of whether the guest was able to report back and/or stop the transfer, remove the transfer on the host
+                                 * so that we don't risk of having stale transfers here. */
+                                ShClSvcTransferDestroy(pClient, pTransfer);
+                                pTransfer = NULL;
                                 break;
                             }
-
-                            default:
-                                rc = shClSvcTransferStop(pClient, pTransfer, true /* fWaitForGuest */);
-                                break;
                         }
+
+                        /* Tell the backend. */
+                        int rc2 = ShClBackendTransferHandleStatusReply(pClient->pBackend, pClient, pTransfer,
+                                                                       SHCLSOURCE_REMOTE, pReply->u.TransferStatus.uStatus,
+                                                                       pReply->rc);
+                        if (RT_SUCCESS(rc))
+                            rc = rc2;
 
                         RT_FALL_THROUGH();
                     }
-                    case VBOX_SHCL_REPLYMSGTYPE_LIST_OPEN:
+                    case VBOX_SHCL_TX_REPLYMSGTYPE_LIST_OPEN:
                         RT_FALL_THROUGH();
-                    case VBOX_SHCL_REPLYMSGTYPE_LIST_CLOSE:
+                    case VBOX_SHCL_TX_REPLYMSGTYPE_LIST_CLOSE:
                         RT_FALL_THROUGH();
-                    case VBOX_SHCL_REPLYMSGTYPE_OBJ_OPEN:
+                    case VBOX_SHCL_TX_REPLYMSGTYPE_OBJ_OPEN:
                         RT_FALL_THROUGH();
-                    case VBOX_SHCL_REPLYMSGTYPE_OBJ_CLOSE:
+                    case VBOX_SHCL_TX_REPLYMSGTYPE_OBJ_CLOSE:
                     {
                         uint64_t uCID;
@@ -1391,41 +1609,31 @@
     int rc = VERR_INVALID_PARAMETER; /* Play safe by default. */
 
+    if (cParms < 1)
+        return rc;
+    ASSERT_GUEST_RETURN(aParms[0].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
+
+    uint64_t uCID  = 0; /* Context ID */
+    rc = HGCMSvcGetU64(&aParms[0], &uCID);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    const SHCLTRANSFERID idTransfer = VBOX_SHCL_CONTEXTID_GET_TRANSFER(uCID);
+
     /*
      * Pre-check: For certain messages we need to make sure that a (right) transfer is present.
      */
-    uint64_t      uCID      = 0; /* Context ID */
     PSHCLTRANSFER pTransfer = NULL;
-
-    shClSvcClientLock(pClient);
-    ASSERT_GUEST_MSG_RETURN(pClient->Pending.uType == 0, ("Already pending! (idClient=%RU32)\n",
-                                                          pClient->State.uClientID), VERR_RESOURCE_BUSY);
-    shClSvcClientUnlock(pClient);
-
     switch (u32Function)
     {
+        case VBOX_SHCL_GUEST_FN_REPLY:
+            /* Function does its own lookup. */
+            break;
+
         default:
         {
-            if (!ShClTransferCtxGetTotalTransfers(&pClient->Transfers.Ctx))
-            {
-                LogFunc(("No transfers found\n"));
-                rc = VERR_SHCLPB_TRANSFER_ID_NOT_FOUND;
-                break;
-            }
-
-            if (cParms < 1)
-                break;
-
-            ASSERT_GUEST_RETURN(aParms[0].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
-
-            rc = HGCMSvcGetU64(&aParms[0], &uCID);
-            if (RT_FAILURE(rc))
-                break;
-
-            const SHCLTRANSFERID uTransferID = VBOX_SHCL_CONTEXTID_GET_TRANSFER(uCID);
-
-            pTransfer = ShClTransferCtxGetTransferById(&pClient->Transfers.Ctx, uTransferID);
+            pTransfer = ShClTransferCtxGetTransferById(&pClient->Transfers.Ctx, idTransfer);
             if (!pTransfer)
             {
-                LogFunc(("Transfer with ID %RU16 not found\n", uTransferID));
+                LogRel(("Shared Clipboard: Transfer with ID %RU16 not found\n", idTransfer));
                 rc = VERR_SHCLPB_TRANSFER_ID_NOT_FOUND;
             }
@@ -1443,5 +1651,5 @@
         case VBOX_SHCL_GUEST_FN_REPLY:
         {
-            rc = shClSvcTransferHandleReply(pClient, pTransfer, cParms, aParms);
+            rc = shClSvcTransferHandleReply(pClient, idTransfer, cParms, aParms);
             break;
         }
@@ -1458,6 +1666,7 @@
                 && ShClTransferGetDir(pTransfer)    == SHCLTRANSFERDIR_TO_REMOTE)
             {
-                /* Get roots if this is a local write transfer (host -> guest). */
-                rc = ShClBackendTransferGetRoots(pClient->pBackend, pClient, pTransfer);
+                rc = ShClBackendTransferHGRootListRead(pClient->pBackend, pClient, pTransfer);
+                if (RT_FAILURE(rc))
+                    break;
             }
             else
@@ -1473,6 +1682,4 @@
             HGCMSvcSetU32(&aParms[1], rootListHdr.fFeatures);
             HGCMSvcSetU64(&aParms[2], rootListHdr.cEntries);
-
-            rc = VINF_SUCCESS;
             break;
         }
@@ -1830,4 +2037,20 @@
     }
 
+    /* If anything wrong has happened, make sure to unregister the transfer again (if not done already) and tell the guest. */
+    if (   RT_FAILURE(rc)
+        && pTransfer)
+    {
+        shClSvcClientLock(pClient);
+
+        /* Let the guest know. */
+        int rc2 = shClSvcTransferSendStatusAsyncInternal(pClient, pTransfer,
+                                                         SHCLTRANSFERSTATUS_ERROR, rc, NULL /* ppEvent */);
+        AssertRC(rc2);
+
+        ShClSvcTransferDestroy(pClient, pTransfer);
+
+        shClSvcClientUnlock(pClient);
+    }
+
     LogFlowFunc(("[Client %RU32] Returning rc=%Rrc\n", pClient->State.uClientID, rc));
     return rc;
@@ -1879,4 +2102,69 @@
             break;
     }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+/**
+ * Reports a transfer status to the guest.
+ *
+ * @returns VBox status code.
+ * @param   pClient             Client that owns the transfer.
+ * @param   idTransfer          Transfer ID to report status for.
+ * @param   enmDir              Transfer direction to report status for.
+ * @param   uStatus             Status to report.
+ * @param   rcTransfer          Result code to report. Optional and depending on status.
+ * @param   ppEvent             Where to return the wait event on success. Optional.
+ *                              Must be released by the caller with ShClEventRelease().
+ *
+ * @note    Caller must enter the client's critical section.
+ */
+static int shClSvcTransferSendStatusExAsync(PSHCLCLIENT pClient, SHCLTRANSFERID idTransfer, SHCLTRANSFERDIR enmDir, SHCLTRANSFERSTATUS uStatus,
+                                            int rcTransfer, PSHCLEVENT *ppEvent)
+{
+    AssertPtrReturn(pClient, VERR_INVALID_POINTER);
+    AssertReturn(idTransfer != NIL_SHCLTRANSFERID, VERR_INVALID_PARAMETER);
+    /* ppEvent is optional. */
+
+    PSHCLCLIENTMSG pMsgReadData = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_STATUS,
+                                                  VBOX_SHCL_CPARMS_TRANSFER_STATUS);
+    if (!pMsgReadData)
+        return VERR_NO_MEMORY;
+
+    PSHCLEVENT pEvent;
+    int rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
+    if (RT_SUCCESS(rc))
+    {
+        HGCMSvcSetU64(&pMsgReadData->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID, idTransfer, pEvent->idEvent));
+        HGCMSvcSetU32(&pMsgReadData->aParms[1], enmDir);
+        HGCMSvcSetU32(&pMsgReadData->aParms[2], uStatus);
+        HGCMSvcSetU32(&pMsgReadData->aParms[3], (uint32_t)rcTransfer); /** @todo uint32_t vs. int. */
+        HGCMSvcSetU32(&pMsgReadData->aParms[4], 0 /* fFlags, unused */);
+
+        shClSvcMsgAdd(pClient, pMsgReadData, true /* fAppend */);
+
+        rc = shClSvcClientWakeup(pClient);
+        if (RT_SUCCESS(rc))
+        {
+            LogRel2(("Shared Clipboard: Reported status %s (rc=%Rrc) of transfer %RU32 to guest\n",
+                     ShClTransferStatusToStr(uStatus), rcTransfer, idTransfer));
+
+            if (ppEvent)
+            {
+                *ppEvent = pEvent; /* Takes ownership. */
+            }
+            else /* If event is not consumed by the caller, release the event again. */
+                ShClEventRelease(pEvent);
+        }
+        else
+            ShClEventRelease(pEvent);
+    }
+    else
+        rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
+
+    if (RT_FAILURE(rc))
+        LogRel(("Shared Clipboard: Reporting status %s (%Rrc) for transfer %RU32 to guest failed with %Rrc\n",
+                ShClTransferStatusToStr(uStatus), rcTransfer, idTransfer, rc));
 
     LogFlowFuncLeaveRC(rc);
@@ -1895,8 +2183,8 @@
  *                              Must be released by the caller with ShClEventRelease().
  *
- * @note    Caller must enter critical section.
- */
-int shClSvcTransferSendStatus(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, SHCLTRANSFERSTATUS uStatus,
-                              int rcTransfer, PSHCLEVENT *ppEvent)
+ * @note    Caller must enter the client's critical section.
+ */
+static int shClSvcTransferSendStatusAsyncInternal(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, SHCLTRANSFERSTATUS uStatus,
+                                                  int rcTransfer, PSHCLEVENT *ppEvent)
 {
     AssertPtrReturn(pClient,   VERR_INVALID_POINTER);
@@ -1904,51 +2192,22 @@
     /* ppEvent is optional. */
 
-    PSHCLCLIENTMSG pMsgReadData = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_STATUS,
-                                                  VBOX_SHCL_CPARMS_TRANSFER_STATUS);
-    if (!pMsgReadData)
-        return VERR_NO_MEMORY;
-
-    PSHCLEVENT pEvent;
-    int rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
-    if (RT_SUCCESS(rc))
-    {
-        HGCMSvcSetU64(&pMsgReadData->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
-                                                                         pTransfer->State.uID, pEvent->idEvent));
-        HGCMSvcSetU32(&pMsgReadData->aParms[1], pTransfer->State.enmDir);
-        HGCMSvcSetU32(&pMsgReadData->aParms[2], uStatus);
-        HGCMSvcSetU32(&pMsgReadData->aParms[3], (uint32_t)rcTransfer); /** @todo uint32_t vs. int. */
-        HGCMSvcSetU32(&pMsgReadData->aParms[4], 0 /* fFlags, unused */);
-
-        shClSvcMsgAdd(pClient, pMsgReadData, true /* fAppend */);
-
-        rc = shClSvcClientWakeup(pClient);
-        if (RT_SUCCESS(rc))
-        {
-            LogRel2(("Shared Clipboard: Reported status %s (rc=%Rrc) of transfer %RU32 to guest\n",
-                     ShClTransferStatusToStr(uStatus), rcTransfer, pTransfer->State.uID));
-
-            if (ppEvent)
-            {
-                *ppEvent = pEvent; /* Takes ownership. */
-            }
-            else /* If event is not consumed by the caller, release the event again. */
-                ShClEventRelease(pEvent);
-        }
-        else
-            ShClEventRelease(pEvent);
-    }
-    else
-        rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
-
-    if (RT_FAILURE(rc))
-        LogRel(("Shared Clipboard: Reporting status %s (%Rrc) for transfer %RU32 to guest failed with %Rrc\n",
-                ShClTransferStatusToStr(uStatus), rcTransfer, pTransfer->State.uID, rc));
-
-    LogFlowFuncLeaveRC(rc);
-    return rc;
-}
-
-/**
- * Cleans up (unregisters and destroys) all transfers not in the 'started' state (anymore).
+   return shClSvcTransferSendStatusExAsync(pClient, ShClTransferGetID(pTransfer), ShClTransferGetDir(pTransfer),
+                                           uStatus, rcTransfer, ppEvent);
+}
+
+int ShClSvcTransferSendStatusAsync(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, SHCLTRANSFERSTATUS uStatus,
+                                   int rcTransfer, PSHCLEVENT *ppEvent)
+{
+    shClSvcClientLock(pClient);
+
+    int rc = shClSvcTransferSendStatusAsyncInternal(pClient, pTransfer, uStatus, rcTransfer, ppEvent);
+
+    shClSvcClientUnlock(pClient);
+
+    return rc;
+}
+
+/**
+ * Cleans up (unregisters and destroys) all transfers not in started state (anymore).
  *
  * @param   pClient             Client to clean up transfers for.
@@ -1956,22 +2215,24 @@
  * @note    Caller needs to take the critical section.
  */
-void shClSvcTransferCleanupAllUnused(PSHCLCLIENT pClient)
-{
+static void shClSvcTransferCleanupAllUnused(PSHCLCLIENT pClient)
+{
+    Assert(RTCritSectIsOwner(&pClient->CritSect));
+
     LogFlowFuncEnter();
 
     PSHCLTRANSFERCTX pTxCtx = &pClient->Transfers.Ctx;
 
-    /* Remove all transfers which are not in a running state (e.g. only announced). */
     PSHCLTRANSFER pTransfer, pTransferNext;
     RTListForEachSafe(&pTxCtx->List, pTransfer, pTransferNext, SHCLTRANSFER, Node)
     {
-        if (ShClTransferGetStatus(pTransfer) != SHCLTRANSFERSTATUS_STARTED)
+        SHCLTRANSFERSTATUS const enmSts = ShClTransferGetStatus(pTransfer);
+        if (enmSts != SHCLTRANSFERSTATUS_STARTED)
         {
             /* Let the guest know. */
-            int rc2 = shClSvcTransferSendStatus(pClient, pTransfer,
-                                                SHCLTRANSFERSTATUS_UNINITIALIZED, VINF_SUCCESS, NULL /* ppEvent */);
+            int rc2 = shClSvcTransferSendStatusAsyncInternal(pClient, pTransfer,
+                                                             SHCLTRANSFERSTATUS_UNINITIALIZED, VINF_SUCCESS, NULL /* ppEvent */);
             AssertRC(rc2);
 
-            ShClTransferCtxTransferUnregisterById(pTxCtx, pTransfer->State.uID);
+            ShClTransferCtxUnregisterById(pTxCtx, pTransfer->State.uID);
 
             ShClTransferDestroy(pTransfer);
@@ -1984,20 +2245,16 @@
 
 /**
- * Initializes a new transfer and sends the status to the guest.
- *
- * @note Assumes that the client's critical section is taken.
+ * Creates a new transfer on the host.
  *
  * @returns VBox status code.
  * @param   pClient             Client that owns the transfer.
- * @param   enmDir              Transfer direction to start.
- * @param   enmSource           Transfer source to start.
- * @param   ppTransfer          Where to return the created transfer on success. Optional.
- */
-int shClSvcTransferInit(PSHCLCLIENT pClient,
-                        SHCLTRANSFERDIR enmDir, SHCLSOURCE enmSource,
-                        PSHCLTRANSFER *ppTransfer)
-{
-    Assert(RTCritSectIsOwner(&pClient->CritSect));
-
+ * @param   enmDir              Transfer direction to create.
+ * @param   enmSource           Transfer source to create.
+ * @param   idTransfer          Transfer ID to use for creation.
+ *                              If set to NIL_SHCLTRANSFERID, a new transfer ID will be created.
+ * @param   ppTransfer          Where to return the created transfer on success. Optional and can be NULL.
+ */
+int ShClSvcTransferCreate(PSHCLCLIENT pClient, SHCLTRANSFERDIR enmDir, SHCLSOURCE enmSource, SHCLTRANSFERID idTransfer, PSHCLTRANSFER *ppTransfer)
+{
     AssertPtrReturn(pClient, VERR_INVALID_POINTER);
     /* ppTransfer is optional. */
@@ -2005,97 +2262,157 @@
     LogFlowFuncEnter();
 
+    shClSvcClientLock(pClient);
+
+    /* When creating a new transfer, this is a good time to clean up old stuff we don't need anymore. */
     shClSvcTransferCleanupAllUnused(pClient);
 
+    PSHCLTRANSFER pTransfer;
+    int rc = ShClTransferCreate(enmDir, enmSource, &pTransfer);
+    if (RT_SUCCESS(rc))
+    {
+        if (idTransfer == NIL_SHCLTRANSFERID)
+            rc = ShClTransferCtxRegister(&pClient->Transfers.Ctx, pTransfer, &idTransfer);
+        else
+            rc = ShClTransferCtxRegisterById(&pClient->Transfers.Ctx, pTransfer, idTransfer);
+        if (RT_SUCCESS(rc))
+        {
+            if (ppTransfer)
+                *ppTransfer = pTransfer;
+        }
+    }
+
+    shClSvcClientUnlock(pClient);
+
+    if (RT_FAILURE(rc))
+    {
+        ShClTransferDestroy(pTransfer);
+
+        RTMemFree(pTransfer);
+        pTransfer = NULL;
+    }
+
+    if (RT_FAILURE(rc))
+       LogRel(("Shared Clipboard: Creating transfer failed with %Rrc\n", rc));
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+/**
+ * Destroys a transfer on the host.
+ *
+ * @param   pClient             Client to destroy transfer for.
+ * @param   pTransfer           Transfer to destroy.
+ *                              The pointer will be invalid after return.
+ */
+void ShClSvcTransferDestroy(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
+{
+    if (!pTransfer)
+        return;
+
+    LogFlowFuncEnter();
+
     PSHCLTRANSFERCTX pTxCtx = &pClient->Transfers.Ctx;
 
+    ShClTransferCtxUnregisterById(pTxCtx, pTransfer->State.uID);
+
+    ShClTransferDestroy(pTransfer);
+
+    RTMemFree(pTransfer);
+    pTransfer = NULL;
+
+    LogFlowFuncLeave();
+}
+
+/**
+ * Initializes a (created) transfer on the host.
+ *
+ * @returns VBox status code.
+ * @param   pClient             Client that owns the transfer.
+ * @param   pTransfer           Transfer to initialize.
+ */
+int ShClSvcTransferInit(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
+{
+    AssertPtrReturn(pClient, VERR_INVALID_POINTER);
+
+    LogFlowFuncEnter();
+
+    shClSvcClientLock(pClient);
+
+    Assert(ShClTransferGetStatus(pTransfer) == SHCLTRANSFERSTATUS_NONE);
+
+    PSHCLTRANSFERCTX pTxCtx = &pClient->Transfers.Ctx;
+
     int rc;
 
-    if (!ShClTransferCtxTransfersMaximumReached(pTxCtx))
-    {
+    if (!ShClTransferCtxIsMaximumReached(pTxCtx))
+    {
+        SHCLTRANSFERDIR const enmDir = ShClTransferGetDir(pTransfer);
+
         LogRel2(("Shared Clipboard: Initializing %s transfer ...\n",
                  enmDir == SHCLTRANSFERDIR_FROM_REMOTE ? "guest -> host" : "host -> guest"));
 
-        PSHCLTRANSFER pTransfer;
-        rc = ShClTransferCreate(&pTransfer);
-        if (RT_SUCCESS(rc))
-        {
-            SHCLTXPROVIDER Provider;
-            RT_ZERO(Provider);
-
-            /* Assign local provider first and overwrite interface methods below if needed. */
-            VBClTransferProviderLocalQueryInterface(&Provider);
-
-            if (enmDir == SHCLTRANSFERDIR_FROM_REMOTE) /* Guest -> Host. */
-            {
-                Provider.Interface.pfnRootListRead  = shClSvcTransferIfaceRootListRead;
-
-                Provider.Interface.pfnListOpen      = shClSvcTransferIfaceListOpen;
-                Provider.Interface.pfnListClose     = shClSvcTransferIfaceListClose;
-                Provider.Interface.pfnListHdrRead   = shClSvcTransferIfaceListHdrRead;
-                Provider.Interface.pfnListEntryRead = shClSvcTransferIfaceListEntryRead;
-
-                Provider.Interface.pfnObjOpen       = shClSvcTransferIfaceObjOpen;
-                Provider.Interface.pfnObjClose      = shClSvcTransferIfaceObjClose;
-                Provider.Interface.pfnObjRead       = shClSvcTransferIfaceObjRead;
-            }
-            else if (enmDir == SHCLTRANSFERDIR_TO_REMOTE) /* Host -> Guest. */
-            {
-                Provider.Interface.pfnListHdrWrite   = shClSvcTransferIfaceListHdrWrite;
-                Provider.Interface.pfnListEntryWrite = shClSvcTransferIfaceListEntryWrite;
-                Provider.Interface.pfnObjWrite       = shClSvcTransferIfaceObjWrite;
-            }
-            else
-                AssertFailed();
-
-            Provider.enmSource = pClient->State.enmSource;
-            Provider.pvUser    = pClient;
-
-            rc = ShClTransferSetProvider(pTransfer, &Provider);
+        SHCLTXPROVIDER Provider;
+        RT_ZERO(Provider);
+
+        /* Assign local provider first and overwrite interface methods below if needed. */
+        ShClTransferProviderLocalQueryInterface(&Provider);
+
+        if (enmDir == SHCLTRANSFERDIR_FROM_REMOTE) /* Guest -> Host. */
+        {
+            Provider.Interface.pfnRootListRead  = shClSvcTransferIfaceGHRootListRead;
+
+            Provider.Interface.pfnListOpen      = shClSvcTransferIfaceGHListOpen;
+            Provider.Interface.pfnListClose     = shClSvcTransferIfaceGHListClose;
+            Provider.Interface.pfnListHdrRead   = shClSvcTransferIfaceGHListHdrRead;
+            Provider.Interface.pfnListEntryRead = shClSvcTransferIfaceGHListEntryRead;
+
+            Provider.Interface.pfnObjOpen       = shClSvcTransferIfaceGHObjOpen;
+            Provider.Interface.pfnObjClose      = shClSvcTransferIfaceGHObjClose;
+            Provider.Interface.pfnObjRead       = shClSvcTransferIfaceGHObjRead;
+        }
+        else if (enmDir == SHCLTRANSFERDIR_TO_REMOTE) /* Host -> Guest. */
+        {
+            Provider.Interface.pfnListHdrWrite   = shClSvcTransferIfaceHGListHdrWrite;
+            Provider.Interface.pfnListEntryWrite = shClSvcTransferIfaceHGListEntryWrite;
+            Provider.Interface.pfnObjWrite       = shClSvcTransferIfaceHGObjWrite;
+        }
+        else
+            AssertFailed();
+
+        Provider.enmSource = pClient->State.enmSource;
+        Provider.pvUser    = pClient;
+
+        rc = ShClTransferSetProvider(pTransfer, &Provider);
+        if (RT_SUCCESS(rc))
+        {
+            ShClTransferSetCallbacks(pTransfer, &pClient->Transfers.Callbacks);
+
+            rc = ShClTransferInit(pTransfer);
             if (RT_SUCCESS(rc))
             {
-                ShClTransferSetCallbacks(pTransfer, &pClient->Transfers.Callbacks);
-
-                rc = ShClTransferInit(pTransfer, enmDir, enmSource);
-                if (RT_SUCCESS(rc))
-                {
-                    SHCLTRANSFERID uTransferID = 0;
-                    rc = ShClTransferCtxTransferRegister(pTxCtx, pTransfer, &uTransferID);
-                    if (RT_SUCCESS(rc))
-                    {
-                        rc = ShClBackendTransferCreate(pClient->pBackend, pClient, pTransfer);
-                        if (RT_SUCCESS(rc))
-                        {
-                            /* Tell the guest that we've initialized the transfer locally. */
-                            rc = shClSvcTransferSendStatus(pClient, pTransfer,
-                                                           SHCLTRANSFERSTATUS_INITIALIZED, VINF_SUCCESS, NULL /* ppEvent */);
-                        }
-                    }
-
-                    if (RT_FAILURE(rc))
-                        ShClTransferCtxTransferUnregisterById(pTxCtx, uTransferID);
-                }
-            }
-
-            if (RT_FAILURE(rc))
-            {
-                ShClBackendTransferDestroy(pClient->pBackend, pClient, pTransfer);
-                ShClTransferDestroy(pTransfer);
-
-                RTMemFree(pTransfer);
-                pTransfer = NULL;
-            }
-            else
-            {
-                if (ppTransfer)
-                    *ppTransfer = pTransfer;
-            }
-        }
-
-        if (RT_FAILURE(rc))
-            LogRel(("Shared Clipboard: Starting transfer failed with %Rrc\n", rc));
+                /* Sanity: Make sure that the transfer we're gonna report as INITIALIZED to the guest
+                 *         actually has some root entries set, as the guest can query for those at any time then. */
+                if (enmDir == SHCLTRANSFERDIR_TO_REMOTE)
+                    AssertMsgStmt(ShClTransferRootsCount(pTransfer), ("Transfer has no root entries set\n"), rc = VERR_WRONG_ORDER);
+            }
+        }
     }
     else
         rc = VERR_SHCLPB_MAX_TRANSFERS_REACHED;
 
+    /* Tell the guest the outcome. */
+    int rc2 = shClSvcTransferSendStatusAsyncInternal(pClient, pTransfer,
+                                                       RT_SUCCESS(rc)
+                                                     ? SHCLTRANSFERSTATUS_INITIALIZED : SHCLTRANSFERSTATUS_ERROR, rc,
+                                                     NULL /* ppEvent */);
+    if (RT_SUCCESS(rc))
+        rc2 = rc;
+
+    if (RT_FAILURE(rc))
+        LogRel(("Shared Clipboard: Initializing transfer failed with %Rrc\n", rc));
+
+    shClSvcClientUnlock(pClient);
+
     LogFlowFuncLeaveRC(rc);
     return rc;
@@ -2107,7 +2424,7 @@
  * @returns VBox status code.
  * @param   pClient             Client that owns the transfer.
- * @param   pTransfer           Transfer to stop.
- */
-int shClSvcTransferStart(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
+ * @param   pTransfer           Transfer to start.
+ */
+int ShClSvcTransferStart(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
 {
     LogRel2(("Shared Clipboard: Starting transfer %RU32 ...\n", pTransfer->State.uID));
@@ -2119,6 +2436,7 @@
     /* Let the guest know in any case
      * (so that it can tear down the transfer on error as well). */
-    int rc2 = shClSvcTransferSendStatus(pClient, pTransfer,
-                                        RT_SUCCESS(rc) ? SHCLTRANSFERSTATUS_STARTED : SHCLTRANSFERSTATUS_ERROR, rc, NULL /* ppEvent */);
+    int rc2 = shClSvcTransferSendStatusAsyncInternal(pClient, pTransfer,
+                                                     RT_SUCCESS(rc) ? SHCLTRANSFERSTATUS_STARTED : SHCLTRANSFERSTATUS_ERROR, rc,
+                                                     NULL /* ppEvent */);
     if (RT_SUCCESS(rc))
         rc = rc2;
@@ -2136,5 +2454,5 @@
  * @param   fWaitForGuest       Set to \c true to wait for acknowledgement from guest, or \c false to skip waiting.
  */
-int shClSvcTransferStop(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, bool fWaitForGuest)
+int ShClSvcTransferStop(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, bool fWaitForGuest)
 {
     LogRel2(("Shared Clipboard: Stopping transfer %RU32 ...\n", pTransfer->State.uID));
@@ -2143,6 +2461,6 @@
 
     PSHCLEVENT pEvent;
-    int rc = shClSvcTransferSendStatus(pClient, pTransfer,
-                                       SHCLTRANSFERSTATUS_STOPPED, VINF_SUCCESS, &pEvent);
+    int rc = shClSvcTransferSendStatusAsyncInternal(pClient, pTransfer,
+                                                    SHCLTRANSFERSTATUS_STOPPED, VINF_SUCCESS, &pEvent);
     if (   RT_SUCCESS(rc)
         && fWaitForGuest)
@@ -2165,21 +2483,4 @@
                 pTransfer->State.uID, rc));
 
-    /* Regardless of whether the guest was able to report back and/or stop the transfer, remove the transfer on the host
-     * so that we don't risk of having stale transfers here. */
-    int rc2 = ShClTransferCtxTransferUnregisterById(&pClient->Transfers.Ctx, ShClTransferGetID(pTransfer));
-    if (RT_SUCCESS(rc2))
-    {
-        ShClBackendTransferDestroy(pClient->pBackend, pClient, pTransfer);
-        ShClTransferDestroy(pTransfer);
-        pTransfer = NULL;
-    }
-
-    if (RT_SUCCESS(rc))
-        rc = rc2;
-
-    if (RT_FAILURE(rc))
-        LogRel(("Shared Clipboard: Stopping transfer %RU32 failed with rc=%Rrc\n",
-                pTransfer->State.uID, rc));
-
     shClSvcClientUnlock(pClient);
 
@@ -2213,5 +2514,5 @@
             AssertPtr(pClient);
 
-            shClSvcClientTransfersReset(pClient);
+            shClSvcTransferDestroyAll(pClient);
 
             ++itClient;
Index: trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-transfers.h
===================================================================
--- trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-transfers.h	(revision 100291)
+++ trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-transfers.h	(revision 100367)
@@ -32,4 +32,15 @@
 #endif
 
+int shClSvcTransferModeSet(uint32_t fMode);
+int ShClSvcTransferCreate(PSHCLCLIENT pClient, SHCLTRANSFERDIR enmDir, SHCLSOURCE enmSource, SHCLTRANSFERID idTransfer, PSHCLTRANSFER *ppTransfer);
+void ShClSvcTransferDestroy(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer);
+int ShClSvcTransferInit(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer);
+int ShClSvcTransferStart(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer);
+int ShClSvcTransferStop(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, bool fWaitForGuest);
+int ShClSvcTransferSendStatusAsync(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, SHCLTRANSFERSTATUS uStatus, int rcTransfer, PSHCLEVENT *ppEvent);
+int ShClSvcTransferRootListReadFromGuest(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer);
+bool shClSvcTransferMsgIsAllowed(uint32_t uMode, uint32_t uMsg);
+void shClSvcTransferDestroyAll(PSHCLCLIENT pClient);
+
 #endif /* !VBOX_INCLUDED_SRC_SharedClipboard_VBoxSharedClipboardSvc_transfers_h */
 
Index: trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-win.cpp
===================================================================
--- trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-win.cpp	(revision 100291)
+++ trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-win.cpp	(revision 100367)
@@ -166,23 +166,5 @@
     LogFlowFunc(("uFmt=%#x\n", uFmt));
 
-    int rc;
-
-    PSHCLEVENT pEvent;
-    rc = ShClSvcReadDataFromGuestAsync(pCtx->pClient, uFmt, &pEvent);
-    if (RT_SUCCESS(rc))
-    {
-        PSHCLEVENTPAYLOAD pPayload;
-        rc = ShClEventWait(pEvent, SHCL_TIMEOUT_DEFAULT_MS, &pPayload);
-        if (RT_SUCCESS(rc))
-        {
-            if (ppvData)
-                *ppvData = pPayload ? pPayload->pvData : NULL;
-            if (pcbData)
-                *pcbData = pPayload ? pPayload->cbData : 0;
-        }
-
-        ShClEventRelease(pEvent);
-    }
-
+    int rc = ShClSvcReadDataFromGuest(pCtx->pClient, uFmt, &pEvent);
     if (RT_FAILURE(rc))
         LogRel(("Shared Clipboard: Reading guest clipboard data for Windows host failed with %Rrc\n", rc));
@@ -231,11 +213,11 @@
 #ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
 /**
- * @copydoc SHCLTRANSFERCALLBACKS::pfnOnStart
- *
- * Called on transfer start to notify the "in-flight" IDataObject about a started transfer.
+ * @copydoc SHCLTRANSFERCALLBACKS::pfnOnInitialized
+ *
+ * Called on transfer intialization to notify the "in-flight" IDataObject about a data transfer.
  *
  * @thread  Service main thread.
  */
-static DECLCALLBACK(void) vboxClipboardSvcWinTransferStartedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
+static DECLCALLBACK(void) vboxClipboardSvcWinTransferInitializedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
 {
     PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
@@ -259,5 +241,5 @@
             SharedClipboardWinDataObject *pObj = pCtx->Win.pDataObjInFlight;
             AssertPtrReturnVoid(pObj);
-            rc = pObj->SetAndStartTransfer(pTransfer);
+            rc = pObj->SetTransfer(pTransfer);
 
             pCtx->Win.pDataObjInFlight = NULL; /* Hand off to Windows. */
@@ -272,4 +254,40 @@
 
     LogFlowFunc(("LEAVE: idTransfer=%RU32, rc=%Rrc\n", ShClTransferGetID(pTransfer), rc));
+}
+
+/**
+ * @copydoc SHCLTRANSFERCALLBACKS::pfnOnDestroy
+ *
+ * @thread  Service main thread.
+ */
+static DECLCALLBACK(void) vboxClipboardSvcWinTransferDestroyCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
+{
+    PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
+    AssertPtr(pCtx);
+
+    PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
+    AssertPtr(pTransfer);
+
+    SharedClipboardWinTransferDestroy(&pCtx->Win, pTransfer);
+}
+
+/**
+ * @copydoc SHCLTRANSFERCALLBACKS::pfnOnStarted
+ *
+ * @thread  Service main thread.
+ */
+static DECLCALLBACK(void) vboxClipboardSvcWinTransferStartedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
+{
+    PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
+    AssertPtr(pCtx);
+
+    PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
+    AssertPtr(pTransfer);
+
+    if (ShClTransferGetDir(pTransfer) ==  SHCLTRANSFERDIR_FROM_REMOTE) /* G->H */
+    {
+        /* Report to the guest that we now entered the STARTED state. */
+        ShClSvcTransferStart(pCtx->pClient, pTransfer);
+    }
 }
 #endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
@@ -385,5 +403,5 @@
                 void    *pvData = NULL;
                 uint32_t cbData = 0;
-                int rc = vboxClipboardSvcWinReadDataFromGuest(pCtx, uFormat, &pvData, &cbData);
+                int rc = ShClSvcReadDataFromGuest(pCtx->pClient, uFormat, &pvData, &cbData);
                 if (   RT_SUCCESS(rc)
                     && pvData
@@ -440,27 +458,4 @@
 
             int rc = SharedClipboardWinClearAndAnnounceFormats(pWinCtx, fFormats, hWnd);
-#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
-            if (   RT_SUCCESS(rc)
-                && fFormats & VBOX_SHCL_FMT_URI_LIST)
-            {
-                /*
-                 * The host requested data from the guest as URI list.
-                 *
-                 * This means on Windows we
-                 * - need to first create an own IDataObject and push it on the clipboard
-                 * - register a transfer locally so that we have a valid ID for it
-                 *
-                 * That way Windows will recognize that there is a data transfer "in flight".
-                 *
-                 * As soon as the user requests to "paste" the data (transfer), the IDataObject will try to read data via
-                 * the pfnOnRequestDataFromSource callback.
-                 */
-                SHCLCALLBACKS Callbacks;
-                RT_ZERO(Callbacks);
-                Callbacks.pfnOnRequestDataFromSource = vboxClipboardSvcWinRequestDataFromSourceCallback;
-
-                rc = SharedClipboardWinTransferCreateAndSetDataObject(pWinCtx, pCtx, &Callbacks);
-            }
-#endif
             if (RT_FAILURE(rc))
                 LogRel(("Shared Clipboard: Reporting clipboard formats %#x to Windows host failed with %Rrc\n", fFormats, rc));
@@ -739,5 +734,7 @@
          */
         RT_ZERO(pClient->Transfers.Callbacks);
-        pClient->Transfers.Callbacks.pfnOnStarted = vboxClipboardSvcWinTransferStartedCallback;
+        pClient->Transfers.Callbacks.pfnOnInitialized = vboxClipboardSvcWinTransferInitializedCallback;
+        pClient->Transfers.Callbacks.pfnOnStarted     = vboxClipboardSvcWinTransferStartedCallback;
+        pClient->Transfers.Callbacks.pfnOnDestroy     = vboxClipboardSvcWinTransferDestroyCallback;
 
         pClient->Transfers.Callbacks.pvUser = pCtx; /* Assign context as user-provided callback data. */
@@ -973,25 +970,5 @@
 
 #ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
-int ShClBackendTransferCreate(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
-{
-    RT_NOREF(pBackend, pClient, pTransfer);
-
-    LogFlowFuncEnter();
-
-    return VINF_SUCCESS;
-}
-
-int ShClBackendTransferDestroy(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
-{
-    RT_NOREF(pBackend);
-
-    LogFlowFuncEnter();
-
-    SharedClipboardWinTransferDestroy(&pClient->State.pCtx->Win, pTransfer);
-
-    return VINF_SUCCESS;
-}
-
-int ShClBackendTransferGetRoots(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
+int ShClBackendTransferHGRootListRead(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
 {
     RT_NOREF(pBackend);
Index: trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-x11.cpp
===================================================================
--- trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-x11.cpp	(revision 100291)
+++ trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-x11.cpp	(revision 100367)
@@ -44,9 +44,15 @@
 #include <iprt/errcore.h>
 
-#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
 # include <VBox/GuestHost/SharedClipboard-transfers.h>
+# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
+#  include <VBox/GuestHost/SharedClipboard-transfers.h>
+# endif
 #endif
 
 #include "VBoxSharedClipboardSvc-internal.h"
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+# include "VBoxSharedClipboardSvc-transfers.h"
+#endif
 
 /* Number of currently extablished connections. */
@@ -81,4 +87,10 @@
 static DECLCALLBACK(int) shClSvcX11RequestDataFromSourceCallback(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser);
 
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+static DECLCALLBACK(void) shClSvcX11OnTransferInitCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx);
+static DECLCALLBACK(void) shClSvcX11OnTransferDestroyCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx);
+static DECLCALLBACK(void) shClSvcX11OnTransferUnregisteredCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx, PSHCLTRANSFERCTX pTransferCtx);
+#endif
+
 
 int ShClBackendInit(PSHCLBACKEND pBackend, VBOXHGCMSVCFNTABLE *pTable)
@@ -146,4 +158,21 @@
                 pClient->State.pCtx = pCtx;
                 pCtx->pClient = pClient;
+
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+                /*
+                 * Set callbacks.
+                 * Those will be registered within ShClSvcTransferInit() when a new transfer gets initialized.
+                 *
+                 * Used for starting / stopping the HTTP server.
+                 */
+                RT_ZERO(pClient->Transfers.Callbacks);
+
+                pClient->Transfers.Callbacks.pvUser = pCtx; /* Assign context as user-provided callback data. */
+                pClient->Transfers.Callbacks.cbUser = sizeof(SHCLCONTEXT);
+
+                pClient->Transfers.Callbacks.pfnOnInitialized  = shClSvcX11OnTransferInitCallback;
+                pClient->Transfers.Callbacks.pfnOnDestroy      = shClSvcX11OnTransferDestroyCallback;
+                pClient->Transfers.Callbacks.pfnOnUnregistered = shClSvcX11OnTransferUnregisteredCallback;
+#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
 
                 rc = ShClX11ThreadStart(&pCtx->X11, true /* grab shared clipboard */);
@@ -352,4 +381,158 @@
 }
 
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+/**
+ * @copydoc SHCLTRANSFERCALLBACKS::pfnOnInitialized
+ *
+ * This starts the HTTP server if not done yet and registers the transfer with it.
+ *
+ * @thread Service main thread.
+ */
+static DECLCALLBACK(void) shClSvcX11OnTransferInitCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
+{
+    RT_NOREF(pCbCtx);
+
+#if 1
+    LogFlowFuncEnter();
+
+    PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
+    AssertPtr(pCtx);
+
+    PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
+    AssertPtr(pTransfer);
+
+    switch (ShClTransferGetDir(pTransfer))
+    {
+        case SHCLTRANSFERDIR_FROM_REMOTE: /* G->H */
+        {
+# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
+            /* We only need to start the HTTP server when we actually receive data from the remote (host). */
+            ShClTransferHttpServerMaybeStart(&pCtx->X11.HttpCtx);
+# endif
+            break;
+        }
+
+        case SHCLTRANSFERDIR_TO_REMOTE: /* H->G */
+        {
+            PSHCLEVENT pEvent;
+            int rc = ShClEventSourceGenerateAndRegisterEvent(&pCtx->pClient->EventSrc, &pEvent);
+            if (RT_SUCCESS(rc))
+            {
+                rc = ShClX11ReadDataFromX11Async(&pCtx->X11, VBOX_SHCL_FMT_URI_LIST, UINT32_MAX, pEvent);
+                if (RT_SUCCESS(rc))
+                {
+                    /* X supplies the data asynchronously, so we need to wait for data to arrive first. */
+                    PSHCLEVENTPAYLOAD pPayload;
+                    rc = ShClEventWait(pEvent, SHCL_TIMEOUT_DEFAULT_MS, &pPayload);
+                    if (RT_SUCCESS(rc))
+                    {
+                        if (pPayload)
+                        {
+                            Assert(pPayload->cbData == sizeof(SHCLX11RESPONSE));
+                            AssertPtr(pPayload->pvData);
+                            PSHCLX11RESPONSE pResp = (PSHCLX11RESPONSE)pPayload->pvData;
+
+                            rc = ShClTransferRootsInitFromStringList(pTransfer,
+                                                                     (char *)pResp->Read.pvData, pResp->Read.cbData + 1 /* Include zero terminator */);
+                            //if (RT_SUCCESS(rc))
+                            //    rc = ShClTransferRootListRead(pTransfer);
+
+                            if (RT_SUCCESS(rc))
+                                LogRel2(("Shared Clipboard: Host reported %RU64 X11 root entries for transfer to guest\n", ShClTransferRootsCount(pTransfer)));
+
+                            RTMemFree(pResp->Read.pvData);
+                            pResp->Read.cbData = 0;
+
+                            ShClPayloadFree(pPayload);
+                            pPayload = NULL;
+                        }
+                        else
+                            rc = VERR_NO_DATA; /* No payload. */
+                    }
+                }
+
+                ShClEventRelease(pEvent);
+            }
+            else
+                rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
+            break;
+        }
+
+        default:
+            break;
+    }
+
+    LogFlowFuncLeave();
+}
+
+/**
+ * @copydoc SHCLTRANSFERCALLBACKS::pfnOnDestroy
+ *
+ * This stops the HTTP server if not done yet.
+ *
+ * @thread Service main thread.
+ */
+static DECLCALLBACK(void) shClSvcX11OnTransferDestroyCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
+{
+    LogFlowFuncEnter();
+
+# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
+    PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
+    AssertPtr(pCtx);
+
+    PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
+    AssertPtr(pTransfer);
+
+    if (ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_FROM_REMOTE)
+        ShClTransferHttpServerMaybeStop(&pCtx->X11.HttpCtx);
+# else
+    RT_NOREF(pCbCtx);
+# endif
+
+    LogFlowFuncLeave();
+}
+
+/**
+ * Unregisters a transfer from a HTTP server.
+ *
+ * This also stops the HTTP server if no active transfers are found anymore.
+ *
+ * @param   pCtx                Shared clipboard context to unregister transfer for.
+ * @param   pTransfer           Transfer to unregister.
+ *
+ * @thread Clipboard main thread.
+ */
+static void shClSvcX11HttpTransferUnregister(PSHCLCONTEXT pCtx, PSHCLTRANSFER pTransfer)
+{
+    if (ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_FROM_REMOTE)
+    {
+# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
+        if (ShClTransferHttpServerIsInitialized(&pCtx->X11.HttpCtx.HttpServer))
+        {
+            ShClTransferHttpServerUnregisterTransfer(&pCtx->X11.HttpCtx.HttpServer, pTransfer);
+            ShClTransferHttpServerMaybeStop(&pCtx->X11.HttpCtx);
+        }
+# else
+        RT_NOREF(pCtx);
+# endif
+    }
+
+    //ShClTransferRelease(pTransfer);
+}
+
+/**
+ * @copydoc SHCLTRANSFERCALLBACKS::pfnOnUnregistered
+ *
+ * Unregisters a (now) unregistered transfer from the HTTP server.
+ *
+ * @thread Clipboard main thread.
+ */
+static DECLCALLBACK(void) shClSvcX11OnTransferUnregisteredCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx, PSHCLTRANSFERCTX pTransferCtx)
+{
+    RT_NOREF(pTransferCtx);
+    shClSvcX11HttpTransferUnregister((PSHCLCONTEXT)pCbCtx->pvUser, pCbCtx->pTransfer);
+}
+#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
+
 /**
  * @copydoc SHCLCALLBACKS::pfnOnRequestDataFromSource
@@ -373,59 +556,70 @@
     }
 
-    int rc = VINF_SUCCESS;
-
-#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
+    PSHCLCLIENT const pClient = pCtx->pClient;
+    int rc = ShClSvcReadDataFromGuest(pClient, uFmt, ppv, pcb);
+    if (RT_FAILURE(rc))
+        return rc;
+
     /*
-     * Note: We always return a generic URI list here.
+     * Note: We always return a generic URI list (as HTTP links) here.
      *       As we don't know which Atom target format was requested by the caller, the X11 clipboard codes needs
      *       to decide & transform the list into the actual clipboard Atom target format the caller wanted.
      */
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
     if (uFmt == VBOX_SHCL_FMT_URI_LIST)
     {
-        PSHCLTRANSFER pTransfer = ShClTransferHttpServerGetTransferFirst(&pCtx->X11.HttpCtx.HttpServer);
-        if (pTransfer)
-        {
-            if (RT_SUCCESS(rc))
-                rc = ShClTransferRootListRead(pTransfer);
-        }
-
-        /** @todo BUGBUG IMPLEMENT THIS! */
-
-        *ppv = NULL;
-        *pcb = 0;
-
-        rc = VERR_NO_DATA;
-    }
-#endif
-
-    if (RT_SUCCESS(rc))
-    {
-        /* Request data from the guest and for data to arrive. */
-        PSHCLEVENT pEvent;
-        rc = ShClSvcReadDataFromGuestAsync(pCtx->pClient, uFmt, &pEvent);
+        PSHCLTRANSFER pTransfer;
+        rc = ShClSvcTransferCreate(pClient, SHCLTRANSFERDIR_FROM_REMOTE, SHCLSOURCE_REMOTE,
+                                   NIL_SHCLTRANSFERID /* Creates a new transfer ID */, &pTransfer);
         if (RT_SUCCESS(rc))
         {
-            PSHCLEVENTPAYLOAD pPayload;
-            rc = ShClEventWait(pEvent, SHCL_TIMEOUT_DEFAULT_MS, &pPayload);
+            /* Initialize the transfer on the host side. */
+            rc = ShClSvcTransferInit(pClient, pTransfer);
+            if (RT_FAILURE(rc))
+                 ShClSvcTransferDestroy(pClient, pTransfer);
+        }
+
+        if (RT_SUCCESS(rc))
+        {
+            /* We have to wait for the guest reporting the transfer as being initialized.
+             * Only then we can start reading stuff. */
+            rc = ShClTransferWaitForStatus(pTransfer, SHCL_TIMEOUT_DEFAULT_MS, SHCLTRANSFERSTATUS_INITIALIZED);
             if (RT_SUCCESS(rc))
             {
-                if (   !pPayload
-                    || !pPayload->cbData)
+                rc = ShClTransferRootListRead(pTransfer);
+                if (RT_SUCCESS(rc))
                 {
-                    rc = VERR_NO_DATA;
-                }
-                else
-                {
-                    *ppv = pPayload->pvData;
-                    *pcb = pPayload->cbData;
+# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
+                    /* As soon as we register the transfer with the HTTP server, the transfer needs to have its roots set. */
+                    PSHCLHTTPSERVER const pHttpSrv = &pCtx->X11.HttpCtx.HttpServer;
+                    rc = ShClTransferHttpServerRegisterTransfer(pHttpSrv, pTransfer);
+                    if (RT_SUCCESS(rc))
+                    {
+                        char *pszURL = ShClTransferHttpServerGetUrlA(pHttpSrv, pTransfer->State.uID);
+                        if (pszURL)
+                        {
+                            *ppv = pszURL;
+                            *pcb = strlen(pszURL) + 1 /* Include terminator */;
+
+                            LogFlowFunc(("URL is '%s'\n", pszURL));
+
+                            /* ppv has ownership of pszURL. */
+
+                            rc = VINF_SUCCESS;
+                        }
+                        else
+                            rc = VERR_NO_MEMORY;
+                    }
+# else
+                    rc = VERR_NOT_SUPPORTED;
+# endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP */
                 }
             }
-
-            ShClEventRelease(pEvent);
-        }
-    }
+        }
+    }
+#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
 
     if (RT_FAILURE(rc))
-        LogRel(("Shared Clipboard: Requesting data in format %#x for X11 host failed with %Rrc\n", uFmt, rc));
+        LogRel(("Shared Clipboard: Requesting X11 data in format %#x from guest failed with %Rrc\n", uFmt, rc));
 
     LogFlowFuncLeaveRC(rc);
@@ -434,35 +628,48 @@
 
 #ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
-
-int ShClBackendTransferCreate(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
+/**
+ * Handles transfer status replies from the guest.
+ */
+int ShClBackendTransferHandleStatusReply(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, SHCLSOURCE enmSource, SHCLTRANSFERSTATUS enmStatus, int rcStatus)
+{
+    RT_NOREF(pBackend, pClient, enmSource, rcStatus);
+
+    PSHCLCONTEXT pCtx = pClient->State.pCtx; RT_NOREF(pCtx);
+
+    if (ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_FROM_REMOTE) /* Guest -> Host */
+    {
+        switch (enmStatus)
+        {
+            case SHCLTRANSFERSTATUS_INITIALIZED:
+            {
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
+                int rc2 = ShClTransferHttpServerMaybeStart(&pCtx->X11.HttpCtx);
+                if (RT_SUCCESS(rc2))
+                {
+
+                }
+
+                if (RT_FAILURE(rc2))
+                    LogRel(("Shared Clipboard: Registering HTTP transfer failed: %Rrc\n", rc2));
+#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP */
+                break;
+            }
+
+            default:
+                break;
+        }
+    }
+
+    return VINF_SUCCESS;
+}
+
+int ShClBackendTransferHGRootListRead(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
 {
     RT_NOREF(pBackend);
-#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
-    /* We only need to start the HTTP server (and register the transfer to it) when we actually receive data from the guest. */
-    if (ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_FROM_REMOTE)
-        return ShClTransferHttpServerMaybeStart(&pClient->State.pCtx->X11.HttpCtx);
+
+#if 1
+    RT_NOREF(pClient, pTransfer);
+    int rc = 0;
 #else
-    RT_NOREF(pClient, pTransfer);
-#endif
-    return VINF_SUCCESS;
-}
-
-int ShClBackendTransferDestroy(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
-{
-    RT_NOREF(pBackend);
-#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
-    /* See comment in ShClBackendTransferCreate(). */
-    if (ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_FROM_REMOTE)
-        return ShClTransferHttpServerMaybeStop(&pClient->State.pCtx->X11.HttpCtx);
-#else
-    RT_NOREF(pClient, pTransfer);
-#endif
-    return VINF_SUCCESS;
-}
-
-int ShClBackendTransferGetRoots(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
-{
-    RT_NOREF(pBackend);
-
     LogFlowFuncEnter();
 
@@ -508,4 +715,5 @@
     else
         rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
+#endif
 
     LogFlowFuncLeaveRC(rc);
Index: trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp
===================================================================
--- trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp	(revision 100291)
+++ trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp	(revision 100367)
@@ -614,5 +614,6 @@
 
     /* Make sure to send a quit message to the guest so that it can terminate gracefully. */
-    RTCritSectEnter(&pClient->CritSect);
+    shClSvcClientLock(pClient);
+
     if (pClient->Pending.uType)
     {
@@ -627,7 +628,13 @@
         pClient->Pending.paParms = NULL;
     }
-    RTCritSectLeave(&pClient->CritSect);
+
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+    shClSvcTransferDestroyAll(pClient);
+    ShClTransferCtxDestroy(&pClient->Transfers.Ctx);
+#endif
 
     ShClEventSourceDestroy(&pClient->EventSrc);
+
+    shClSvcClientUnlock(pClient);
 
     shClSvcClientStateDestroy(&pClient->State);
@@ -635,7 +642,5 @@
     PSHCLCLIENTLEGACYCID pCidIter, pCidIterNext;
     RTListForEachSafe(&pClient->Legacy.lstCID, pCidIter, pCidIterNext, SHCLCLIENTLEGACYCID, Node)
-    {
         RTMemFree(pCidIter);
-    }
 
     int rc2 = RTCritSectDelete(&pClient->CritSect);
@@ -686,5 +691,5 @@
 
 #ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
-    shClSvcClientTransfersReset(pClient);
+    shClSvcTransferDestroyAll(pClient);
 #endif
 
@@ -1220,5 +1225,5 @@
 
 /**
- * Requests to read clipboard data from the guest.
+ * Reads clipboard data from the guest, asynchronous version.
  *
  * @returns VBox status code.
@@ -1330,16 +1335,21 @@
                 }
 
-#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+#if 0 /* ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
                 /*
                  * When the host wants to read URI data from the guest:
                  *   - Initialize a transfer locally.
                  *   - Request URI data from the guest; this tells the guest to initialize a transfer on the guest side.
-                 *   - Start the transfer locally once we receive the transfer STARTED status from the guest via VBOX_SHCL_GUEST_FN_REPLY.
+                 *   - Start the transfer locally once we receive the transfer INITIALIZED status from the guest via VBOX_SHCL_GUEST_FN_REPLY.
+                 *
+                 * When this function returns, the X11 clipboard will try reading the data, so we need to make sure that
+                 *   - the local HTTP server is up and running (will be done in ShClBackendTransferCreate() on X11)
+                 *   - the right (HTTP) URL data is filled into the clipboard
+                 * by then.
                  */
                 if (   RT_SUCCESS(rc)
-                    && fFormat == VBOX_SHCL_FMT_URI_LIST)
+                    && (fFormat & VBOX_SHCL_FMT_URI_LIST))
                 {
-                    rc = shClSvcTransferInit(pClient, SHCLTRANSFERDIR_FROM_REMOTE, SHCLSOURCE_REMOTE,
-                                             NULL /* pTransfer */);
+                    PSHCLTRANSFER pTransfer;
+                    rc = ShClSvcTransferInit(pClient, SHCLTRANSFERDIR_FROM_REMOTE, SHCLSOURCE_REMOTE, &pTransfer);
                     if (RT_SUCCESS(rc))
                         rc = shClSvcSetSource(pClient, SHCLSOURCE_REMOTE);
@@ -1381,4 +1391,49 @@
 
 /**
+ * Reads clipboard data from the guest.
+ *
+ * @returns VBox status code.
+ * @param   pClient             Client to request to read data form.
+ * @param   fFormats            The formats being requested, OR'ed together (VBOX_SHCL_FMT_XXX).
+ * @param   ppv                 Where to return the allocated data read.
+ *                              Must be free'd by the caller.
+ * @param   pcb                 Where to return number of bytes read.
+ */
+int ShClSvcReadDataFromGuest(PSHCLCLIENT pClient, SHCLFORMAT fFormats, void **ppv, uint32_t *pcb)
+{
+    LogFlowFuncEnter();
+
+    /* Request data from the guest and wait for data to arrive. */
+    PSHCLEVENT pEvent;
+    int rc = ShClSvcReadDataFromGuestAsync(pClient, fFormats, &pEvent);
+    if (RT_SUCCESS(rc))
+    {
+        PSHCLEVENTPAYLOAD pPayload;
+        rc = ShClEventWait(pEvent, SHCL_TIMEOUT_DEFAULT_MS, &pPayload);
+        if (RT_SUCCESS(rc))
+        {
+            if (   !pPayload
+                || !pPayload->cbData)
+            {
+                rc = VERR_NO_DATA;
+            }
+            else
+            {
+                *ppv = pPayload->pvData;
+                *pcb = pPayload->cbData;
+            }
+        }
+
+        ShClEventRelease(pEvent);
+    }
+
+    if (RT_FAILURE(rc))
+        LogRel(("Shared Clipboard: Reading data from guest failed with %Rrc\n", rc));
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+/**
  * Signals the host that clipboard data from the guest has been received.
  *
@@ -1575,42 +1630,38 @@
      */
     int rc;
-    if (!fFormats && pClient->State.enmSource != SHCLSOURCE_REMOTE)
+    if (!fFormats)
         rc = VINF_SUCCESS;
     else
     {
-        rc = shClSvcSetSource(pClient, SHCLSOURCE_REMOTE);
+        rc = RTCritSectEnter(&g_CritSect);
         if (RT_SUCCESS(rc))
         {
-            rc = RTCritSectEnter(&g_CritSect);
-            if (RT_SUCCESS(rc))
+            if (g_ExtState.pfnExtension)
             {
-                if (g_ExtState.pfnExtension)
-                {
-                    SHCLEXTPARMS parms;
-                    RT_ZERO(parms);
-                    parms.uFormat = fFormats;
-
-                    g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE, &parms, sizeof(parms));
-                }
-                else
-                {
-#ifdef LOG_ENABLED
-                    char *pszFmts = ShClFormatsToStrA(fFormats);
-                    if (pszFmts)
-                    {
-                        LogRel2(("Shared Clipboard: Guest reported formats '%s' to host\n", pszFmts));
-                        RTStrFree(pszFmts);
-                    }
-#endif
-                    rc = ShClBackendReportFormats(&g_ShClBackend, pClient, fFormats);
-                    if (RT_FAILURE(rc))
-                        LogRel(("Shared Clipboard: Reporting guest clipboard formats to the host failed with %Rrc\n", rc));
-                }
-
-                RTCritSectLeave(&g_CritSect);
+                SHCLEXTPARMS parms;
+                RT_ZERO(parms);
+                parms.uFormat = fFormats;
+
+                g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE, &parms, sizeof(parms));
             }
             else
-                LogRel2(("Shared Clipboard: Unable to take internal lock while receiving guest clipboard announcement: %Rrc\n", rc));
-        }
+            {
+#ifdef LOG_ENABLED
+                char *pszFmts = ShClFormatsToStrA(fFormats);
+                if (pszFmts)
+                {
+                    LogRel2(("Shared Clipboard: Guest reported formats '%s' to host\n", pszFmts));
+                    RTStrFree(pszFmts);
+                }
+#endif
+                rc = ShClBackendReportFormats(&g_ShClBackend, pClient, fFormats);
+                if (RT_FAILURE(rc))
+                    LogRel(("Shared Clipboard: Reporting guest clipboard formats to the host failed with %Rrc\n", rc));
+            }
+
+            RTCritSectLeave(&g_CritSect);
+        }
+        else
+            LogRel2(("Shared Clipboard: Unable to take internal lock while receiving guest clipboard announcement: %Rrc\n", rc));
     }
 
@@ -1762,27 +1813,4 @@
         else
             LogRel(("Shared Clipboard: Reading host clipboard data failed with %Rrc\n", rc));
-
-#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
-        /*
-         * When we receive a read request for URI data:
-         *   - Initialize a transfer locally.
-         *   - Tell the guest to initialize a transfer on the guest side.
-         *   - Start the transfer locally once we receive the transfer STARTED status from the guest via VBOX_SHCL_GUEST_FN_REPLY.
-         */
-        if (uFormat == VBOX_SHCL_FMT_URI_LIST) /* Only init a transfer if we supply an URI list. */
-        {
-            shClSvcClientLock(pClient);
-
-            rc = shClSvcTransferInit(pClient, SHCLTRANSFERDIR_TO_REMOTE, SHCLSOURCE_LOCAL,
-                                     NULL /* pTransfer */);
-            if (RT_SUCCESS(rc))
-                rc = shClSvcSetSource(pClient, SHCLSOURCE_LOCAL);
-
-            if (RT_FAILURE(rc))
-                LogRel(("Shared Clipboard: Initializing host -> guest transfer failed with %Rrc\n", rc));
-
-            shClSvcClientUnlock(pClient);
-        }
-#endif
     }
 
@@ -1998,26 +2026,4 @@
 }
 
-/**
- * Sets the transfer source type of a Shared Clipboard client.
- *
- * @returns VBox status code.
- * @param   pClient             Client to set transfer source type for.
- * @param   enmSource           Source type to set.
- */
-int shClSvcSetSource(PSHCLCLIENT pClient, SHCLSOURCE enmSource)
-{
-    if (!pClient) /* If no client connected (anymore), bail out. */
-        return VINF_SUCCESS;
-
-    int rc = VINF_SUCCESS;
-
-    pClient->State.enmSource = enmSource;
-
-    LogFlowFunc(("Source of client %RU32 is now %RU32\n", pClient->State.uClientID, pClient->State.enmSource));
-
-    LogFlowFuncLeaveRC(rc);
-    return rc;
-}
-
 static int svcInit(VBOXHGCMSVCFNTABLE *pTable)
 {
@@ -2069,8 +2075,4 @@
         g_ExtState.uClientID = 0;
     }
-
-#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
-    shClSvcClientTransfersReset(pClient);
-#endif
 
     ShClBackendDisconnect(&g_ShClBackend, pClient);
@@ -2250,5 +2252,6 @@
 
 #ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
-                shClSvcClientTransfersReset(pClient);
+                /* Reset transfer state. */
+                shClSvcTransferDestroyAll(pClient);
 #endif
                 shClSvcClientUnlock(pClient);
Index: trunk/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardServiceHost.cpp
===================================================================
--- trunk/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardServiceHost.cpp	(revision 100291)
+++ trunk/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardServiceHost.cpp	(revision 100367)
@@ -330,7 +330,6 @@
 
 #ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
-int ShClBackendTransferCreate(PSHCLBACKEND, PSHCLCLIENT, PSHCLTRANSFER) { return VINF_SUCCESS; }
-int ShClBackendTransferDestroy(PSHCLBACKEND, PSHCLCLIENT, PSHCLTRANSFER) { return VINF_SUCCESS; }
-int ShClBackendTransferGetRoots(PSHCLBACKEND, PSHCLCLIENT, PSHCLTRANSFER) { return VINF_SUCCESS; }
+int ShClBackendTransferHandleStatusReply(PSHCLBACKEND, PSHCLCLIENT, PSHCLTRANSFER, SHCLSOURCE, SHCLTRANSFERSTATUS, int) { return VINF_SUCCESS; }
+int ShClBackendTransferHGRootListRead(PSHCLBACKEND, PSHCLCLIENT, PSHCLTRANSFER) { return VINF_SUCCESS; }
 #endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
 
Index: trunk/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardTransfers.cpp
===================================================================
--- trunk/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardTransfers.cpp	(revision 100291)
+++ trunk/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardTransfers.cpp	(revision 100367)
@@ -212,9 +212,9 @@
 {
     PSHCLTRANSFER pTransfer;
-    int rc = ShClTransferCreate(&pTransfer);
+    int rc = ShClTransferCreate(SHCLTRANSFERDIR_TO_REMOTE, SHCLSOURCE_LOCAL, &pTransfer);
     RTTESTI_CHECK_RC_OK(rc);
 
     SHCLTXPROVIDER Provider;
-    RTTESTI_CHECK(VBClTransferProviderLocalQueryInterface(&Provider) != NULL);
+    RTTESTI_CHECK(ShClTransferProviderLocalQueryInterface(&Provider) != NULL);
     RTTESTI_CHECK_RC_OK(ShClTransferSetProvider(pTransfer, &Provider));
 
@@ -246,14 +246,14 @@
 
     PSHCLTRANSFER pTransfer;
-    int rc = ShClTransferCreate(&pTransfer);
+    int rc = ShClTransferCreate(SHCLTRANSFERDIR_TO_REMOTE, SHCLSOURCE_LOCAL, &pTransfer);
     RTTESTI_CHECK_RC_OK(rc);
 
     SHCLTXPROVIDER Provider;
-    VBClTransferProviderLocalQueryInterface(&Provider);
+    ShClTransferProviderLocalQueryInterface(&Provider);
 
     rc = ShClTransferSetProvider(pTransfer, &Provider);
     RTTESTI_CHECK_RC_OK(rc);
 
-    rc = ShClTransferInit(pTransfer, SHCLTRANSFERDIR_FROM_REMOTE, SHCLSOURCE_LOCAL);
+    rc = ShClTransferInit(pTransfer);
     RTTESTI_CHECK_RC_OK(rc);
 
@@ -309,5 +309,5 @@
     RTTESTI_CHECK_RC_OK(rc);
     PSHCLTRANSFER pTransfer;
-    rc = ShClTransferCreate(&pTransfer);
+    rc = ShClTransferCreate(SHCLTRANSFERDIR_TO_REMOTE, SHCLSOURCE_LOCAL, &pTransfer);
     RTTESTI_CHECK_RC_OK(rc);
     rc = ShClTransferDestroy(pTransfer);
@@ -318,5 +318,5 @@
     PSHCLLIST pList = ShClTransferListAlloc();
     RTTESTI_CHECK(pList != NULL);
-    rc = ShClTransferCreate(&pTransfer);
+    rc = ShClTransferCreate(SHCLTRANSFERDIR_TO_REMOTE, SHCLSOURCE_LOCAL, &pTransfer);
     RTTESTI_CHECK_RC_OK(rc);
     ShClTransferListFree(pList);
