VirtualBox

Changeset 99937 in vbox for trunk


Ignore:
Timestamp:
May 23, 2023 3:38:52 PM (16 months ago)
Author:
vboxsync
Message:

Shared Clipboard: Added new testcase for the HTTP server in combination with the Shared Clipboard API, various updates and general improvements. bugref:9437

Location:
trunk
Files:
1 added
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/VBox/GuestHost/SharedClipboard-transfers.h

    r99405 r99937  
    117117/** Pointer to a Shared Clipboard list handle. */
    118118typedef SHCLLISTHANDLE *PSHCLLISTHANDLE;
    119 /** Specifies an invalid Shared Clipboard list handle.
    120  * @todo r=bird: The convention is NIL_SHCLLISTHANDLE. */
    121 #define SHCLLISTHANDLE_INVALID        ((SHCLLISTHANDLE)UINT64_MAX)
     119/** Specifies an invalid Shared Clipboard list handle. */
     120#define NIL_SHCLLISTHANDLE ((SHCLLISTHANDLE)UINT64_MAX)
    122121
    123122/** A Shared Clipboard object handle. */
     
    125124/** Pointer to a Shared Clipboard object handle. */
    126125typedef SHCLOBJHANDLE *PSHCLOBJHANDLE;
    127 /** Specifies an invalid Shared Clipboard object handle.
    128  * @todo r=bird: The convention is NIL_SHCLOBJHANDLE. */
    129 #define SHCLOBJHANDLE_INVALID         ((SHCLOBJHANDLE)UINT64_MAX)
     126/** Specifies an invalid Shared Clipboard object handle. */
     127#define NIL_SHCLOBJHANDLE ((SHCLOBJHANDLE)UINT64_MAX)
    130128
    131129/** @} */
     
    473471    /** Entry name. */
    474472    char    *pszName;
    475     /** Size (in bytes) of entry name. */
     473    /** Size (in bytes) of entry name.
     474     *  Includes terminator. */
    476475    uint32_t cbName;
    477476    /** Information flag(s). */
     
    485484typedef SHCLLISTENTRY *PSHCLLISTENTRY;
    486485
    487 /** Maximum length (in UTF-8 characters) of a list entry name. */
     486/** Maximum length (in UTF-8 characters) of a list entry name. Includes terminator. */
    488487#define SHCLLISTENTRY_MAX_NAME     RTPATH_MAX /** @todo Improve this to be more dynamic. */
    489488
     
    827826    /** The node member for using this struct in a RTList. */
    828827    RTLISTNODE                Node;
     828    /** Number of references to this transfer. */
     829    uint32_t                  cRefs;
    829830    /** The transfer's state (for SSM, later). */
    830831    SHCLTRANSFERSTATE         State;
     
    893894
    894895#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
     896/**
     897 * Structure for keeping a Shared Clipboard HTTP server instance.
     898 */
    895899typedef struct _SHCLHTTPSERVER
    896900{
     
    905909    /** Number of registered HTTP transfers. */
    906910    uint32_t            cTransfers;
     911    /** Number of files served (via GET) so far.
     912     *  Only complete downloads count (i.e. no aborted). */
     913    uint32_t            cDownloaded;
    907914    /** Cached response data. */
    908915    RTHTTPSERVERRESP    Resp;
     
    911918typedef SHCLHTTPSERVER *PSHCLHTTPSERVER;
    912919
     920/**
     921 * Structure for keeping a Shared Clipboard HTTP context around.
     922 *
     923 * This contains the HTTP server instance, among other things.
     924 */
    913925typedef struct _SHCLHTTPCONTEXT
    914926{
     
    923935/**
    924936 * Structure for keeping Shared Clipboard transfer context around.
     937 *
     938 * A transfer context contains a list of (grouped) transfers for book keeping.
    925939 */
    926940struct SHCLTRANSFERCTX
     
    938952    /** Number of total transfers (in list). */
    939953    uint16_t                    cTransfers;
    940 #ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
    941     /** HTTP server instance for this transfer context. */
    942     SHCLHTTPSERVER              HttpServer;
    943 #endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP */
    944954};
    945955
     956/** @name Shared Clipboard transfer object API.
     957 *  @{
     958 */
    946959int ShClTransferObjCtxInit(PSHCLCLIENTTRANSFEROBJCTX pObjCtx);
    947960void ShClTransferObjCtxDestroy(PSHCLCLIENTTRANSFEROBJCTX pObjCtx);
     
    957970int ShClTransferObjOpen(PSHCLTRANSFER pTransfer, PSHCLOBJOPENCREATEPARMS pOpenCreateParms, PSHCLOBJHANDLE phObj);
    958971int ShClTransferObjClose(PSHCLTRANSFER pTransfer, SHCLOBJHANDLE hObj);
     972bool ShClTransferObjIsComplete(PSHCLTRANSFER pTransfer, SHCLOBJHANDLE hObj);
    959973int ShClTransferObjRead(PSHCLTRANSFER pTransfer, SHCLOBJHANDLE hObj, void *pvBuf, uint32_t cbBuf, uint32_t fFlags, uint32_t *pcbRead);
    960974int ShClTransferObjWrite(PSHCLTRANSFER pTransfer, SHCLOBJHANDLE hObj, void *pvBuf, uint32_t cbBuf, uint32_t fFlags, uint32_t *pcbWritten);
     
    963977void ShClTransferObjDataChunkDestroy(PSHCLOBJDATACHUNK pDataChunk);
    964978void ShClTransferObjDataChunkFree(PSHCLOBJDATACHUNK pDataChunk);
    965 
     979/** @} */
     980
     981/** @name Shared Clipboard transfer API.
     982 *  @{
     983 */
    966984int ShClTransferCreateEx(uint32_t cbMaxChunkSize, uint32_t cMaxListHandles, uint32_t cMaxObjHandles, PSHCLTRANSFER *ppTransfer);
    967985int ShClTransferCreate(PSHCLTRANSFER *ppTransfer);
    968986int ShClTransferInit(PSHCLTRANSFER pTransfer, SHCLTRANSFERDIR enmDir, SHCLSOURCE enmSource);
    969987int ShClTransferDestroy(PSHCLTRANSFER pTransfer);
     988
     989int ShClTransferRun(PSHCLTRANSFER pTransfer, PFNRTTHREAD pfnThreadFunc, void *pvUser);
     990int ShClTransferStart(PSHCLTRANSFER pTransfer);
     991
     992uint32_t ShClTransferAcquire(PSHCLTRANSFER pTransfer);
     993uint32_t ShClTransferRelease(PSHCLTRANSFER pTransfer);
     994
     995SHCLTRANSFERID ShClTransferGetID(PSHCLTRANSFER pTransfer);
     996SHCLTRANSFERDIR ShClTransferGetDir(PSHCLTRANSFER pTransfer);
     997int ShClTransferGetRootPathAbs(PSHCLTRANSFER pTransfer, char *pszPath, size_t cbPath);
     998SHCLSOURCE ShClTransferGetSource(PSHCLTRANSFER pTransfer);
     999SHCLTRANSFERSTATUS ShClTransferGetStatus(PSHCLTRANSFER pTransfer);
    9701000
    9711001int ShClTransferListOpen(PSHCLTRANSFER pTransfer, PSHCLLISTOPENPARMS pOpenParms, PSHCLLISTHANDLE phList);
     
    10201050int ShClTransferSetProviderIface(PSHCLTRANSFER pTransfer, PSHCLTXPROVIDERCREATIONCTX pCreationCtx);
    10211051int ShClTransferRootsSet(PSHCLTRANSFER pTransfer, const char *pszRoots, size_t cbRoots);
     1052int ShClTransferRootsSetAsFile(PSHCLTRANSFER pTransfer, const char *pszFile);
    10221053void ShClTransferReset(PSHCLTRANSFER pTransfer);
    10231054
     
    10251056int ShClTransferRootsEntry(PSHCLTRANSFER pTransfer, uint64_t uIndex, PSHCLROOTLISTENTRY pEntry);
    10261057int ShClTransferRootsGet(PSHCLTRANSFER pTransfer, PSHCLROOTLIST *ppRootList);
    1027 
    1028 SHCLTRANSFERID ShClTransferGetID(PSHCLTRANSFER pTransfer);
    1029 SHCLTRANSFERDIR ShClTransferGetDir(PSHCLTRANSFER pTransfer);
    1030 SHCLSOURCE ShClTransferGetSource(PSHCLTRANSFER pTransfer);
    1031 SHCLTRANSFERSTATUS ShClTransferGetStatus(PSHCLTRANSFER pTransfer);
    1032 int ShClTransferRun(PSHCLTRANSFER pTransfer, PFNRTTHREAD pfnThreadFunc, void *pvUser);
    1033 int ShClTransferStart(PSHCLTRANSFER pTransfer);
    1034 
     1058/** @} */
     1059
     1060/** @name Shared Clipboard transfer context API.
     1061 *  @{
     1062 */
    10351063int ShClTransferCtxInit(PSHCLTRANSFERCTX pTransferCtx);
    10361064void ShClTransferCtxDestroy(PSHCLTRANSFERCTX pTransferCtx);
     
    10451073int ShClTransferCtxTransferRegisterById(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer, SHCLTRANSFERID idTransfer);
    10461074int ShClTransferCtxTransferUnregister(PSHCLTRANSFERCTX pTransferCtx, SHCLTRANSFERID idTransfer);
     1075/** @} */
    10471076
    10481077#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
    1049 int ShClHttpTransferRegister(PSHCLHTTPCONTEXT pCtx, PSHCLTRANSFER pTransfer);
    1050 int ShClHttpTransferUnregister(PSHCLHTTPCONTEXT pCtx, PSHCLTRANSFER pTransfer);
    1051 
    1052 int ShClTransferHttpServerCreate(PSHCLHTTPSERVER pSrv, uint16_t *puPort);
     1078/** Namespace used as a prefix for HTTP(S) transfer URLs. */
     1079#define SHCL_HTTPT_URL_NAMESPACE "vbcl"
     1080
     1081/** @name Shared Clipboard HTTP context API.
     1082 *  @{
     1083 */
     1084int ShClHttpTransferRegisterAndMaybeStart(PSHCLHTTPCONTEXT pCtx, PSHCLTRANSFER pTransfer);
     1085int ShClHttpTransferUnregisterAndMaybeStop(PSHCLHTTPCONTEXT pCtx, PSHCLTRANSFER pTransfer);
     1086/** @} */
     1087
     1088/** @name Shared Clipboard HTTP server API.
     1089 *  @{
     1090 */
     1091int ShClTransferHttpServerCreate(PSHCLHTTPSERVER pSrv, unsigned cMaxAttempts, uint16_t *puPort);
    10531092int ShClTransferHttpServerCreateEx(PSHCLHTTPSERVER pSrv, uint16_t uPort);
    10541093int ShClTransferHttpServerDestroy(PSHCLHTTPSERVER pSrv);
     
    10621101char *ShClTransferHttpServerGetUrlA(PSHCLHTTPSERVER pSrv, SHCLTRANSFERID idTransfer);
    10631102bool ShClTransferHttpServerIsRunning(PSHCLHTTPSERVER pSrv);
     1103/** @} */
    10641104#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP */
    10651105
    10661106void ShClFsObjFromIPRT(PSHCLFSOBJINFO pDst, PCRTFSOBJINFO pSrc);
    10671107
     1108/** @name Shared Clipboard MIME functions.
     1109 *  @{
     1110 */
    10681111bool ShClMIMEHasFileURLs(const char *pcszFormat, size_t cchFormatMax);
    10691112bool ShClMIMENeedsCache(const char *pcszFormat, size_t cchFormatMax);
     1113/** @} */
    10701114
    10711115const char *ShClTransferStatusToStr(SHCLTRANSFERSTATUS enmStatus);
  • trunk/include/iprt/http-server.h

    r98103 r99937  
    7373    /** Request body data. */
    7474    RTHTTPBODY       Body;
     75    /** User-supplied (opaque) pointer.
     76     *  Can be used for faster lookups between callbacks. */
     77    void            *pvUser;
    7578} RTHTTPSERVERREQ;
    7679/** Pointer to a HTTP client request. */
     
    134137{
    135138    /**
    136      * Called before a given URL will be retrieved by the GET method.
     139     * Called before beginning to process a request. Guaranteed.
     140     *
     141     * @returns VBox status code.
     142     * @param   pData           Pointer to HTTP callback data.
     143     * @param   pReq            Pointer to request to handle.
     144     */
     145    DECLCALLBACKMEMBER(int, pfnRequestBegin,(PRTHTTPCALLBACKDATA pData, PRTHTTPSERVERREQ pReq));
     146    /**
     147     * Called after processing a request. Guaranteed.
     148     *
     149     * @returns VBox status code.
     150     * @param   pData           Pointer to HTTP callback data.
     151     * @param   pReq            Pointer to request to handle.
     152     */
     153    DECLCALLBACKMEMBER(int, pfnRequestEnd,(PRTHTTPCALLBACKDATA pData, PRTHTTPSERVERREQ pReq));
     154    /**
     155     * Opens a resource for a GET request.
     156     *
     157     * Will be called as as a second function for a GET request.
    137158     *
    138159     * Note: High level function, not being called when pfnOnGetRequest is implemented.
     
    145166    DECLCALLBACKMEMBER(int, pfnOpen,(PRTHTTPCALLBACKDATA pData, PRTHTTPSERVERREQ pReq, void **ppvHandle));
    146167    /**
    147      * Called when a given URL will be retrieved by the GET method.
     168     * Reads a resource for a GET request.
    148169     *
    149170     * Note:  High level function, not being called when pfnOnGetRequest is implemented.
     
    152173     * @returns VBox status code.
    153174     * @param   pData           Pointer to HTTP callback data.
     175     * @param   pReq            Pointer to request to handle.
    154176     * @param   pvHandle        Opaque handle for object identification.
    155177     * @param   pvBuf           Pointer to buffer where to store the read data.
     
    157179     * @param   pcbRead         Where to return the amount (in bytes) of read data. Optional and can be NULL.
    158180     */
    159     DECLCALLBACKMEMBER(int, pfnRead,(PRTHTTPCALLBACKDATA pData, void *pvHandle, void *pvBuf, size_t cbBuf, size_t *pcbRead));
    160     /**
    161      * Called when a given URL is done retrieving by the GET method.
     181    DECLCALLBACKMEMBER(int, pfnRead,(PRTHTTPCALLBACKDATA pData, PRTHTTPSERVERREQ pReq, void *pvHandle, void *pvBuf, size_t cbBuf, size_t *pcbRead));
     182    /**
     183     * Closes a resouce for a GET a request.
    162184     *
    163185     * Note: High level function, not being called when pfnOnGetRequest is implemented.
     
    165187     * @returns VBox status code.
    166188     * @param   pData           Pointer to HTTP callback data.
    167      * @param   pszUrl          URL to handle.
     189     * @param   pReq            Pointer to request to handle.
    168190     * @param   pvHandle        Opaque handle for object identification.
    169191     */
    170     DECLCALLBACKMEMBER(int, pfnClose,(PRTHTTPCALLBACKDATA pData, void *pvHandle));
     192    DECLCALLBACKMEMBER(int, pfnClose,(PRTHTTPCALLBACKDATA pData, PRTHTTPSERVERREQ pReq, void *pvHandle));
    171193    /**
    172194     * Queries information about a given URL.
    173195     *
    174      * Will be called with GET or HEAD request.
     196     * Will be called as first function for a GET or HEAD request.
     197     *
     198     * Note: High level function, not being called when pfnOnGetRequest is implemented.*
    175199     *
    176200     * @returns VBox status code.
  • trunk/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibClipboard.cpp

    r98218 r99937  
    718718    if (pRootList)
    719719    {
    720         SHCLROOTLISTHDR srcRootListHdr;
    721         rc = vbglR3ClipboardRootListHdrRead(pCtx, &srcRootListHdr);
    722         if (RT_SUCCESS(rc))
    723         {
    724             pRootList->Hdr.cRoots = srcRootListHdr.cRoots;
    725             pRootList->Hdr.fRoots = 0; /** @todo Implement this. */
    726 
    727             if (srcRootListHdr.cRoots)
     720        rc = vbglR3ClipboardRootListHdrRead(pCtx, &pRootList->Hdr);
     721        if (RT_SUCCESS(rc))
     722        {
     723            if (pRootList->Hdr.cRoots)
    728724            {
    729725                pRootList->paEntries =
    730                     (PSHCLROOTLISTENTRY)RTMemAllocZ(srcRootListHdr.cRoots * sizeof(SHCLROOTLISTENTRY));
     726                    (PSHCLROOTLISTENTRY)RTMemAllocZ(pRootList->Hdr.cRoots * sizeof(SHCLROOTLISTENTRY));
    731727                if (pRootList->paEntries)
    732728                {
    733                     for (uint32_t i = 0; i < srcRootListHdr.cRoots; i++)
     729                    for (uint32_t i = 0; i < pRootList->Hdr.cRoots; i++)
    734730                    {
    735731                        SHCLROOTLISTENTRY *pEntry = &pRootList->paEntries[i];
     
    21162112                        LogFlowFunc(("pszPath=%s\n", openParmsList.pszPath));
    21172113
    2118                         SHCLLISTHANDLE hList = SHCLLISTHANDLE_INVALID;
     2114                        SHCLLISTHANDLE hList = NIL_SHCLLISTHANDLE;
    21192115                        rc = ShClTransferListOpen(pTransfer, &openParmsList, &hList);
    21202116
     
    21542150                /** @todo Handle filter + list features. */
    21552151
    2156                 SHCLLISTHANDLE hList  = SHCLLISTHANDLE_INVALID;
     2152                SHCLLISTHANDLE hList  = NIL_SHCLLISTHANDLE;
    21572153                uint32_t       fFlags = 0;
    21582154                rc = VbglR3ClipboardListHdrReadRecvReq(pCmdCtx, &hList, &fFlags);
  • trunk/src/VBox/GuestHost/SharedClipboard/clipboard-transfers-http.cpp

    r98103 r99937  
    4545#include <iprt/initterm.h>
    4646#include <iprt/list.h>
    47 #define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
    48 #include <iprt/log.h>
    4947#include <iprt/mem.h>
    5048#include <iprt/message.h>
     
    5654#include <iprt/uuid.h>
    5755#include <iprt/vfs.h>
     56
     57#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
     58#include <iprt/log.h>
    5859
    5960#include <VBox/HostServices/VBoxClipboardSvc.h>
     
    7879    /** The handle we're going to use for this HTTP transfer. */
    7980    SHCLOBJHANDLE       hObj;
    80     /** The virtual path of the HTTP server's root directory for this transfer. */
     81    /** The virtual path of the HTTP server's root directory for this transfer.
     82     *  Always has to start with a "/". */
    8183    char                szPathVirtual[RTPATH_MAX];
    8284} SHCLHTTPSERVERTRANSFER;
     
    8991static int shClTransferHttpServerDestroyInternal(PSHCLHTTPSERVER pThis);
    9092static const char *shClTransferHttpServerGetHost(PSHCLHTTPSERVER pSrv);
     93static int shClTransferHttpServerDestroyTransfer(PSHCLHTTPSERVER pSrv, PSHCLHTTPSERVERTRANSFER pSrvTx);
    9194
    9295
    9396/*********************************************************************************************************************************
    94 *   Public Shared Clipboard HTTP transfer functions                                                                              *
     97*   Internal Shared Clipboard HTTP transfer functions                                                                            *
    9598*********************************************************************************************************************************/
    9699
    97 /**
    98  * Registers a Shared Clipboard transfer to a HTTP context.
     100DECLINLINE(void) shClHttpTransferLock(PSHCLHTTPSERVERTRANSFER pSrvTx)
     101{
     102    int rc2 = RTCritSectEnter(&pSrvTx->CritSect);
     103    AssertRC(rc2);
     104}
     105
     106DECLINLINE(void) shClHttpTransferUnlock(PSHCLHTTPSERVERTRANSFER pSrvTx)
     107{
     108    int rc2 = RTCritSectLeave(&pSrvTx->CritSect);
     109    AssertRC(rc2);
     110}
     111
     112/**
     113 * Return the HTTP server transfer for a specific transfer ID.
     114 *
     115 * @returns Pointer to HTTP server transfer if found, NULL if not found.
     116 * @param   pSrv                HTTP server instance.
     117 * @param   idTransfer          Transfer ID to return HTTP server transfer for.
     118 */
     119static PSHCLHTTPSERVERTRANSFER shClTransferHttpServerGetTransferById(PSHCLHTTPSERVER pSrv, SHCLTRANSFERID idTransfer)
     120{
     121    PSHCLHTTPSERVERTRANSFER pSrvTx;
     122    RTListForEach(&pSrv->lstTransfers, pSrvTx, SHCLHTTPSERVERTRANSFER, Node) /** @todo Slow O(n) lookup, but does it for now. */
     123    {
     124        if (pSrvTx->pTransfer->State.uID == idTransfer)
     125            return pSrvTx;
     126    }
     127
     128    return NULL;
     129}
     130
     131/**
     132 * Returns a HTTP server transfer from a given URL.
     133 *
     134 * @returns Pointer to HTTP server transfer if found, NULL if not found.
     135 * @param   pThis               HTTP server instance data.
     136 * @param   pszUrl              URL to validate.
     137 */
     138DECLINLINE(PSHCLHTTPSERVERTRANSFER) shClTransferHttpGetTransferFromUrl(PSHCLHTTPSERVER pThis, const char *pszUrl)
     139{
     140    AssertPtrReturn(pszUrl, NULL);
     141
     142    PSHCLHTTPSERVERTRANSFER pSrvTx = NULL;
     143
     144    PSHCLHTTPSERVERTRANSFER pSrvTxCur;
     145    RTListForEach(&pThis->lstTransfers, pSrvTxCur, SHCLHTTPSERVERTRANSFER, Node)
     146    {
     147        AssertPtr(pSrvTxCur->pTransfer);
     148
     149        LogFlowFunc(("pSrvTxCur=%s\n", pSrvTxCur->szPathVirtual));
     150
     151        /* Be picky here, do a case sensitive comparison. */
     152        if (RTStrStartsWith(pszUrl, pSrvTxCur->szPathVirtual))
     153        {
     154            pSrvTx = pSrvTxCur;
     155            break;
     156        }
     157    }
     158
     159    if (!pSrvTx)
     160        LogRel2(("Shared Clipboard: HTTP URL '%s' not valid\n", pszUrl));
     161
     162    LogFlowFunc(("pszUrl=%s, pSrvTx=%p\n", pszUrl, pSrvTx));
     163    return pSrvTx;
     164}
     165
     166/**
     167 * Returns a HTTP server transfer from an internal HTTP handle.
     168 *
     169 * @returns Pointer to HTTP server transfer if found, NULL if not found.
     170 * @param   pThis               HTTP server instance data.
     171 * @param   pvHandle            Handle to return transfer for.
     172 */
     173DECLINLINE(PSHCLHTTPSERVERTRANSFER) shClTransferHttpGetTransferFromHandle(PSHCLHTTPSERVER pThis, void *pvHandle)
     174{
     175    AssertPtrReturn(pvHandle, NULL);
     176
     177    const SHCLTRANSFERID uHandle = *(uint16_t *)pvHandle;
     178
     179    /** @ŧodo Use a handle lookup table (map) later. */
     180    PSHCLHTTPSERVERTRANSFER pSrvTxCur;
     181    RTListForEach(&pThis->lstTransfers, pSrvTxCur, SHCLHTTPSERVERTRANSFER, Node)
     182    {
     183        AssertPtr(pSrvTxCur->pTransfer);
     184
     185        if (pSrvTxCur->pTransfer->State.uID == uHandle) /** @ŧodo We're using the transfer ID as handle for now. */
     186            return pSrvTxCur;
     187    }
     188
     189    return NULL;
     190}
     191
     192static int shClTransferHttpGetTransferRoots(PSHCLHTTPSERVER pThis, PSHCLHTTPSERVERTRANSFER pSrvTx)
     193{
     194    RT_NOREF(pThis);
     195
     196    int rc = VINF_SUCCESS;
     197
     198    if (pSrvTx->pRootList == NULL)
     199    {
     200        AssertMsgReturn(ShClTransferRootsCount(pSrvTx->pTransfer) == 1,
     201                        ("At the moment only single files are supported!\n"), VERR_NOT_SUPPORTED);
     202
     203        rc = ShClTransferRootsGet(pSrvTx->pTransfer, &pSrvTx->pRootList);
     204    }
     205
     206    return rc;
     207}
     208
     209
     210/*********************************************************************************************************************************
     211*   HTTP server callback implementations                                                                                         *
     212*********************************************************************************************************************************/
     213
     214/** @copydoc RTHTTPSERVERCALLBACKS::pfnRequestBegin */
     215static DECLCALLBACK(int) shClTransferHttpBegin(PRTHTTPCALLBACKDATA pData, PRTHTTPSERVERREQ pReq)
     216{
     217    PSHCLHTTPSERVER pThis = (PSHCLHTTPSERVER)pData->pvUser; RT_NOREF(pThis);
     218    Assert(pData->cbUser == sizeof(SHCLHTTPSERVER));
     219
     220    LogRel2(("Shared Clipboard: HTTP request begin\n"));
     221
     222    PSHCLHTTPSERVERTRANSFER pSrvTx = shClTransferHttpGetTransferFromUrl(pThis, pReq->pszUrl);
     223    if (pSrvTx)
     224    {
     225        shClHttpTransferLock(pSrvTx);
     226        pReq->pvUser = pSrvTx;
     227    }
     228
     229    return VINF_SUCCESS;
     230}
     231
     232/** @copydoc RTHTTPSERVERCALLBACKS::pfnRequestEnd */
     233static DECLCALLBACK(int) shClTransferHttpEnd(PRTHTTPCALLBACKDATA pData, PRTHTTPSERVERREQ pReq)
     234{
     235    PSHCLHTTPSERVER pThis = (PSHCLHTTPSERVER)pData->pvUser; RT_NOREF(pThis);
     236    Assert(pData->cbUser == sizeof(SHCLHTTPSERVER));
     237
     238    LogRel2(("Shared Clipboard: HTTP request end\n"));
     239
     240    PSHCLHTTPSERVERTRANSFER pSrvTx = (PSHCLHTTPSERVERTRANSFER)pReq->pvUser;
     241    if (pSrvTx)
     242    {
     243        shClHttpTransferUnlock(pSrvTx);
     244        pReq->pvUser = NULL;
     245    }
     246
     247    return VINF_SUCCESS;
     248
     249}
     250
     251/** @copydoc RTHTTPSERVERCALLBACKS::pfnOpen */
     252static DECLCALLBACK(int) shClTransferHttpOpen(PRTHTTPCALLBACKDATA pData, PRTHTTPSERVERREQ pReq, void **ppvHandle)
     253{
     254    PSHCLHTTPSERVER pThis = (PSHCLHTTPSERVER)pData->pvUser; RT_NOREF(pThis);
     255    Assert(pData->cbUser == sizeof(SHCLHTTPSERVER));
     256
     257    int rc;
     258
     259    AssertPtr(pReq->pvUser);
     260    PSHCLHTTPSERVERTRANSFER pSrvTx = (PSHCLHTTPSERVERTRANSFER)pReq->pvUser;
     261    if (pSrvTx)
     262    {
     263        LogRel2(("Shared Clipboard: HTTP transfer (handle %RU64) started ...\n", pSrvTx->hObj));
     264
     265        Assert(pSrvTx->hObj != NIL_SHCLOBJHANDLE);
     266        *ppvHandle = &pSrvTx->hObj;
     267    }
     268    else
     269        rc = VERR_NOT_FOUND;
     270
     271    if (RT_FAILURE(rc))
     272        LogRel(("Shared Clipboard: Error starting HTTP transfer for '%s', rc=%Rrc\n", pReq->pszUrl, rc));
     273
     274    LogFlowFuncLeaveRC(rc);
     275    return rc;
     276}
     277
     278/** @copydoc RTHTTPSERVERCALLBACKS::pfnRead */
     279static DECLCALLBACK(int) shClTransferHttpRead(PRTHTTPCALLBACKDATA pData, PRTHTTPSERVERREQ pReq,
     280                                              void *pvHandle, void *pvBuf, size_t cbBuf, size_t *pcbRead)
     281{
     282    RT_NOREF(pData);
     283
     284    int rc;
     285
     286    LogRel3(("Shared Clipboard: Reading %RU32 bytes from HTTP ...\n", cbBuf));
     287
     288    AssertPtr(pReq->pvUser);
     289    PSHCLHTTPSERVERTRANSFER pSrvTx = (PSHCLHTTPSERVERTRANSFER)pReq->pvUser;
     290    if (pSrvTx)
     291    {
     292        PSHCLOBJHANDLE phObj = (PSHCLOBJHANDLE)pvHandle;
     293        if (phObj)
     294        {
     295            uint32_t cbRead;
     296            rc = ShClTransferObjRead(pSrvTx->pTransfer, *phObj, pvBuf, cbBuf, 0 /* fFlags */, &cbRead);
     297            if (RT_SUCCESS(rc))
     298                *pcbRead = (uint32_t)cbRead;
     299
     300            if (RT_FAILURE(rc))
     301                LogRel(("Shared Clipboard: Error reading HTTP transfer (handle %RU64), rc=%Rrc\n", *phObj, rc));
     302        }
     303        else
     304            rc = VERR_NOT_FOUND;
     305    }
     306    else
     307        rc = VERR_NOT_FOUND;
     308
     309    LogFlowFuncLeaveRC(rc);
     310    return rc;
     311}
     312
     313/** @copydoc RTHTTPSERVERCALLBACKS::pfnClose */
     314static DECLCALLBACK(int) shClTransferHttpClose(PRTHTTPCALLBACKDATA pData, PRTHTTPSERVERREQ pReq, void *pvHandle)
     315{
     316    RT_NOREF(pData);
     317
     318    int rc;
     319
     320    AssertPtr(pReq->pvUser);
     321    PSHCLHTTPSERVERTRANSFER pSrvTx = (PSHCLHTTPSERVERTRANSFER)pReq->pvUser;
     322    if (pSrvTx)
     323    {
     324        PSHCLOBJHANDLE phObj = (PSHCLOBJHANDLE)pvHandle;
     325        if (phObj)
     326        {
     327            Assert(*phObj != NIL_SHCLOBJHANDLE);
     328            rc = ShClTransferObjClose(pSrvTx->pTransfer, *phObj);
     329            if (RT_SUCCESS(rc))
     330            {
     331                pSrvTx->hObj = NIL_SHCLOBJHANDLE;
     332                LogRel2(("Shared Clipboard: HTTP transfer %RU16 done\n", pSrvTx->pTransfer->State.uID));
     333            }
     334
     335            if (RT_FAILURE(rc))
     336                LogRel(("Shared Clipboard: Error closing HTTP transfer (handle %RU64), rc=%Rrc\n", *phObj, rc));
     337        }
     338        else
     339            rc = VERR_NOT_FOUND;
     340    }
     341    else
     342        rc = VERR_NOT_FOUND;
     343
     344    LogFlowFuncLeaveRC(rc);
     345    return rc;
     346}
     347
     348/** @copydoc RTHTTPSERVERCALLBACKS::pfnQueryInfo */
     349static DECLCALLBACK(int) shClTransferHttpQueryInfo(PRTHTTPCALLBACKDATA pData,
     350                                                   PRTHTTPSERVERREQ pReq, PRTFSOBJINFO pObjInfo, char **ppszMIMEHint)
     351{
     352    RT_NOREF(ppszMIMEHint);
     353
     354    PSHCLHTTPSERVER pThis = (PSHCLHTTPSERVER)pData->pvUser;
     355    Assert(pData->cbUser == sizeof(SHCLHTTPSERVER));
     356
     357    int rc;
     358
     359    PSHCLHTTPSERVERTRANSFER pSrvTx = (PSHCLHTTPSERVERTRANSFER)pReq->pvUser;
     360    if (pSrvTx)
     361    {
     362        rc = shClTransferHttpGetTransferRoots(pThis, pSrvTx);
     363        if (RT_SUCCESS(rc))
     364        {
     365            SHCLOBJOPENCREATEPARMS openParms;
     366            rc = ShClTransferObjOpenParmsInit(&openParms);
     367            if (RT_SUCCESS(rc))
     368            {
     369                openParms.fCreate = SHCL_OBJ_CF_ACCESS_READ
     370                                  | SHCL_OBJ_CF_ACCESS_DENYWRITE;
     371
     372                PSHCLTRANSFER pTx = pSrvTx->pTransfer;
     373                AssertPtr(pTx);
     374
     375                /* For now we only serve single files, hence index 0 below. */
     376                SHCLROOTLISTENTRY ListEntry;
     377                rc = ShClTransferRootsEntry(pTx, 0 /* First file */, &ListEntry);
     378                if (RT_SUCCESS(rc))
     379                {
     380                    rc = RTStrCopy(openParms.pszPath, openParms.cbPath, ListEntry.pszName);
     381                    if (RT_SUCCESS(rc))
     382                    {
     383                        rc = ShClTransferObjOpen(pTx, &openParms, &pSrvTx->hObj);
     384                        if (RT_SUCCESS(rc))
     385                        {
     386                            char szPath[RTPATH_MAX];
     387                            rc = ShClTransferGetRootPathAbs(pTx, szPath, sizeof(szPath));
     388                            if (RT_SUCCESS(rc))
     389                            {
     390                                rc = RTPathAppend(szPath, sizeof(szPath), openParms.pszPath);
     391                                if (RT_SUCCESS(rc))
     392                                {
     393                                    /* Now that the object is locked, query information that we can return. */
     394                                    rc = RTPathQueryInfo(szPath, pObjInfo, RTFSOBJATTRADD_NOTHING);
     395                                }
     396                            }
     397                        }
     398                    }
     399                }
     400
     401                ShClTransferObjOpenParmsDestroy(&openParms);
     402            }
     403        }
     404    }
     405    else
     406        rc = VERR_NOT_FOUND;
     407
     408    LogFlowFuncLeaveRC(rc);
     409    return rc;
     410}
     411
     412/** @copydoc RTHTTPSERVERCALLBACKS::pfnDestroy */
     413static DECLCALLBACK(int) shClTransferHttpDestroy(PRTHTTPCALLBACKDATA pData)
     414{
     415    PSHCLHTTPSERVER pThis = (PSHCLHTTPSERVER)pData->pvUser;
     416    Assert(pData->cbUser == sizeof(SHCLHTTPSERVER));
     417
     418    return shClTransferHttpServerDestroyInternal(pThis);
     419}
     420
     421
     422/*********************************************************************************************************************************
     423*   Internal Shared Clipboard HTTP server functions                                                                              *
     424*********************************************************************************************************************************/
     425
     426/**
     427 * Destroys a Shared Clipboard HTTP server instance, internal version.
     428 *
     429 * @returns VBox status code.
     430 * @param   pSrv                Shared Clipboard HTTP server instance to destroy.
     431 */
     432static int shClTransferHttpServerDestroyInternal(PSHCLHTTPSERVER pSrv)
     433{
     434    int rc = VINF_SUCCESS;
     435
     436    PSHCLHTTPSERVERTRANSFER pSrvTx, pSrvTxNext;
     437    RTListForEachSafe(&pSrv->lstTransfers, pSrvTx, pSrvTxNext, SHCLHTTPSERVERTRANSFER, Node)
     438    {
     439        int rc2 = shClTransferHttpServerDestroyTransfer(pSrv, pSrvTx);
     440        if (RT_SUCCESS(rc))
     441            rc = rc2;
     442    }
     443
     444    RTHttpServerResponseDestroy(&pSrv->Resp);
     445
     446    pSrv->hHTTPServer = NIL_RTHTTPSERVER;
     447
     448    if (RTCritSectIsInitialized(&pSrv->CritSect))
     449    {
     450        int rc2 = RTCritSectDelete(&pSrv->CritSect);
     451        if (RT_SUCCESS(rc))
     452            rc = rc2;
     453    }
     454
     455    return rc;
     456}
     457
     458/**
     459 * Locks the critical section of a Shared Clipboard HTTP server instance.
     460 *
     461 * @param   pSrv                Shared Clipboard HTTP server instance to lock.
     462 */
     463DECLINLINE(void) shClTransferHttpServerLock(PSHCLHTTPSERVER pSrv)
     464{
     465    int rc2 = RTCritSectEnter(&pSrv->CritSect);
     466    AssertRC(rc2);
     467}
     468
     469/**
     470 * Unlocks the critical section of a Shared Clipboard HTTP server instance.
     471 *
     472 * @param   pSrv                Shared Clipboard HTTP server instance to unlock.
     473 */
     474DECLINLINE(void) shClTransferHttpServerUnlock(PSHCLHTTPSERVER pSrv)
     475{
     476    int rc2 = RTCritSectLeave(&pSrv->CritSect);
     477    AssertRC(rc2);
     478}
     479
     480/**
     481 * Initializes a new Shared Clipboard HTTP server instance.
     482 *
     483 * @param   pSrv                HTTP server instance to initialize.
     484 */
     485static void shClTransferHttpServerInitInternal(PSHCLHTTPSERVER pSrv)
     486{
     487    pSrv->hHTTPServer = NIL_RTHTTPSERVER;
     488    pSrv->uPort       = 0;
     489    RTListInit(&pSrv->lstTransfers);
     490    pSrv->cTransfers  = 0;
     491    int rc2 = RTHttpServerResponseInit(&pSrv->Resp);
     492    AssertRC(rc2);
     493}
     494
     495
     496/*********************************************************************************************************************************
     497*   Public Shared Clipboard HTTP server functions                                                                                *
     498*********************************************************************************************************************************/
     499
     500/**
     501 * Initializes a new Shared Clipboard HTTP server instance.
     502 *
     503 * @param   pSrv                HTTP server instance to initialize.
     504 */
     505void ShClTransferHttpServerInit(PSHCLHTTPSERVER pSrv)
     506{
     507    AssertPtrReturnVoid(pSrv);
     508
     509    shClTransferHttpServerInitInternal(pSrv);
     510}
     511
     512/**
     513 * Returns whether a given TCP port is known to be buggy or not.
     514 *
     515 * @returns \c true if the given port is known to be buggy, or \c false if not.
     516 * @param   uPort               TCP port to check.
     517 */
     518static bool shClTransferHttpServerPortIsBuggy(uint16_t uPort)
     519{
     520    uint16_t const aBuggyPorts[] = {
     521#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
     522        /* GNOME Nautilus ("Files") v43 is unable download HTTP files from this port. */
     523        8080
     524#else   /* Prevents zero-sized arrays. */
     525        0
     526#endif
     527    };
     528
     529    for (size_t i = 0; i < RT_ELEMENTS(aBuggyPorts); i++)
     530        if (uPort == aBuggyPorts[i])
     531            return true;
     532    return false;
     533}
     534
     535/**
     536 * Creates a new Shared Clipboard HTTP server instance, extended version.
     537 *
     538 * @returns VBox status code.
     539 * @return  VERR_ADDRESS_CONFLICT if the port is already taken or the port is known to be buggy.
     540 * @param   pSrv                HTTP server instance to create.
     541 * @param   uPort               TCP port number to use.
     542 */
     543int ShClTransferHttpServerCreateEx(PSHCLHTTPSERVER pSrv, uint16_t uPort)
     544{
     545    AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
     546    AssertReturn(uPort, VERR_INVALID_PARAMETER);
     547
     548    AssertReturn(!shClTransferHttpServerPortIsBuggy(uPort), VERR_ADDRESS_CONFLICT);
     549
     550    RTHTTPSERVERCALLBACKS Callbacks;
     551    RT_ZERO(Callbacks);
     552
     553    Callbacks.pfnRequestBegin  = shClTransferHttpBegin;
     554    Callbacks.pfnRequestEnd    = shClTransferHttpEnd;
     555    Callbacks.pfnOpen          = shClTransferHttpOpen;
     556    Callbacks.pfnRead          = shClTransferHttpRead;
     557    Callbacks.pfnClose         = shClTransferHttpClose;
     558    Callbacks.pfnQueryInfo     = shClTransferHttpQueryInfo;
     559    Callbacks.pfnDestroy       = shClTransferHttpDestroy;
     560
     561    /* Note: The server always and *only* runs against the localhost interface. */
     562    int rc = RTHttpServerCreate(&pSrv->hHTTPServer, "localhost", uPort, &Callbacks,
     563                                pSrv, sizeof(SHCLHTTPSERVER));
     564    if (RT_SUCCESS(rc))
     565    {
     566        rc = RTCritSectInit(&pSrv->CritSect);
     567        AssertRCReturn(rc, rc);
     568
     569        pSrv->uPort = uPort;
     570
     571        LogRel2(("Shared Clipboard: HTTP server running at port %RU16\n", pSrv->uPort));
     572    }
     573    else
     574    {
     575        int rc2 = shClTransferHttpServerDestroyInternal(pSrv);
     576        AssertRC(rc2);
     577    }
     578
     579    if (RT_FAILURE(rc))
     580        LogRel(("Shared Clipboard: HTTP server failed to run, rc=%Rrc\n", rc));
     581
     582    return rc;
     583}
     584
     585/**
     586 * Creates a new Shared Clipboard HTTP server instance using a random port (>= 1024).
     587 *
     588 * This does automatic probing of TCP ports if a port already is being used.
     589 *
     590 * @returns VBox status code.
     591 * @param   pSrv                HTTP server instance to create.
     592 * @param   cMaxAttempts        Maximum number of attempts to create a HTTP server.
     593 * @param   puPort              Where to return the TCP port number being used on success. Optional.
     594 */
     595int ShClTransferHttpServerCreate(PSHCLHTTPSERVER pSrv, unsigned cMaxAttempts, uint16_t *puPort)
     596{
     597    AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
     598    AssertReturn(cMaxAttempts, VERR_INVALID_PARAMETER);
     599    /* puPort is optional. */
     600
     601    RTRAND hRand;
     602    int rc = RTRandAdvCreateSystemFaster(&hRand); /* Should be good enough for this task. */
     603    if (RT_SUCCESS(rc))
     604    {
     605        uint16_t uPort;
     606        unsigned i = 0;
     607        for (i; i < cMaxAttempts; i++)
     608        {
     609            /* Try some random ports above 1024 (i.e. "unprivileged ports") -- required, as VBoxClient runs as a user process
     610             * on the guest. */
     611            uPort = RTRandAdvU32Ex(hRand, 1024, UINT16_MAX);
     612
     613            /* If the port selected turns is known to be buggy for whatever reason, skip it and try another one. */
     614            if (shClTransferHttpServerPortIsBuggy(uPort))
     615                continue;
     616
     617            rc = ShClTransferHttpServerCreateEx(pSrv, (uint32_t)uPort);
     618            if (RT_SUCCESS(rc))
     619            {
     620                if (puPort)
     621                    *puPort = uPort;
     622                break;
     623            }
     624        }
     625
     626        if (   RT_FAILURE(rc)
     627            && i == cMaxAttempts)
     628            LogRel(("Shared Clipboard: Maximum attempts to create HTTP server reached (%u), giving up\n", cMaxAttempts));
     629
     630        RTRandAdvDestroy(hRand);
     631    }
     632
     633    return rc;
     634}
     635
     636/**
     637 * Destroys a Shared Clipboard HTTP server instance.
     638 *
     639 * @returns VBox status code.
     640 * @param   pSrv                HTTP server instance to destroy.
     641 */
     642int ShClTransferHttpServerDestroy(PSHCLHTTPSERVER pSrv)
     643{
     644    AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
     645
     646    if (pSrv->hHTTPServer == NIL_RTHTTPSERVER)
     647        return VINF_SUCCESS;
     648
     649    int rc = RTHttpServerDestroy(pSrv->hHTTPServer);
     650    if (RT_SUCCESS(rc))
     651        rc = shClTransferHttpServerDestroyInternal(pSrv);
     652
     653    if (RT_SUCCESS(rc))
     654        LogRel2(("Shared Clipboard: HTTP server stopped\n"));
     655    else
     656        LogRel(("Shared Clipboard: HTTP server failed to stop, rc=%Rrc\n", rc));
     657
     658    return rc;
     659}
     660
     661/**
     662 * Returns the host name (scheme) of a HTTP server instance.
     663 *
     664 * @returns Host name (scheme).
     665 * @param   pSrv                HTTP server instance to return host name (scheme) for.
     666 *
     667 * @note    This is hardcoded to "localhost" for now.
     668 */
     669static const char *shClTransferHttpServerGetHost(PSHCLHTTPSERVER pSrv)
     670{
     671    RT_NOREF(pSrv);
     672    return "http://localhost"; /* Hardcoded for now. */
     673}
     674
     675/**
     676 * Destroys a server transfer, internal version.
     677 *
     678 * @returns VBox status code.
     679 * @param   pSrv                HTTP server instance to unregister transfer from.
     680 * @param   pTransfer           Server transfer to destroy
     681 *                              The pointer will be invalid on success.
     682 */
     683static int shClTransferHttpServerDestroyTransfer(PSHCLHTTPSERVER pSrv, PSHCLHTTPSERVERTRANSFER pSrvTx)
     684{
     685    RTListNodeRemove(&pSrvTx->Node);
     686
     687    Assert(pSrv->cTransfers);
     688    pSrv->cTransfers--;
     689
     690    LogFunc(("pTransfer=%p, idTransfer=%RU16, szPath=%s -> %RU32 transfers\n",
     691             pSrvTx->pTransfer, pSrvTx->pTransfer->State.uID, pSrvTx->szPathVirtual, pSrv->cTransfers));
     692
     693    LogRel2(("Shared Clipboard: Destroyed HTTP transfer %RU16, now %RU32 HTTP transfers total\n",
     694             pSrvTx->pTransfer->State.uID, pSrv->cTransfers));
     695
     696    int rc = RTCritSectDelete(&pSrvTx->CritSect);
     697    AssertRCReturn(rc, rc);
     698
     699    RTMemFree(pSrvTx);
     700    pSrvTx = NULL;
     701
     702    return rc;
     703}
     704
     705
     706/*********************************************************************************************************************************
     707*   Public Shared Clipboard HTTP server functions                                                                                *
     708*********************************************************************************************************************************/
     709
     710/**
     711 * Registers a Shared Clipboard transfer to a HTTP server instance.
     712 *
     713 * @returns VBox status code.
     714 * @param   pSrv                HTTP server instance to register transfer for.
     715 * @param   pTransfer           Transfer to register. Needs to be on the heap.
     716 */
     717int ShClTransferHttpServerRegisterTransfer(PSHCLHTTPSERVER pSrv, PSHCLTRANSFER pTransfer)
     718{
     719    AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
     720    AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
     721
     722    AssertMsgReturn(pTransfer->State.uID, ("Transfer needs to be registered with a transfer context first\n"),
     723                    VERR_INVALID_PARAMETER);
     724
     725    uint32_t const cRoots = ShClTransferRootsCount(pTransfer);
     726    AssertMsgReturn(cRoots  > 0, ("Transfer has no root entries\n"), VERR_INVALID_PARAMETER);
     727    AssertMsgReturn(cRoots == 1, ("Only single files are supported for now\n"), VERR_NOT_SUPPORTED);
     728    /** @todo Check for directories? */
     729
     730    shClTransferHttpServerLock(pSrv);
     731
     732    PSHCLHTTPSERVERTRANSFER pSrvTx = (PSHCLHTTPSERVERTRANSFER)RTMemAllocZ(sizeof(SHCLHTTPSERVERTRANSFER));
     733    AssertPtrReturn(pSrvTx, VERR_NO_MEMORY);
     734
     735    RTUUID Uuid;
     736    int rc = RTUuidCreate(&Uuid);
     737    if (RT_SUCCESS(rc))
     738    {
     739        char szUuid[64];
     740        rc = RTUuidToStr(&Uuid, szUuid, sizeof(szUuid));
     741        if (RT_SUCCESS(rc))
     742        {
     743            rc = RTCritSectInit(&pSrvTx->CritSect);
     744            AssertRC(rc);
     745
     746            SHCLROOTLISTENTRY ListEntry;
     747            rc = ShClTransferRootsEntry(pTransfer, 0 /* First file */, &ListEntry);
     748            if (RT_SUCCESS(rc))
     749            {
     750#ifdef DEBUG_andy
     751                /* Create the virtual HTTP path for the transfer.
     752                 * Every transfer has a dedicated HTTP path (but live in the same URL namespace). */
     753                ssize_t cch = RTStrPrintf2(pSrvTx->szPathVirtual, sizeof(pSrvTx->szPathVirtual), "/%s/uuid/%RU32/%s",
     754                                           SHCL_HTTPT_URL_NAMESPACE, pSrv->cTransfers, ListEntry.pszName);
     755#else
     756                /* Create the virtual HTTP path for the transfer.
     757                 * Every transfer has a dedicated HTTP path (but live in the same URL namespace). */
     758                ssize_t cch = RTStrPrintf2(pSrvTx->szPathVirtual, sizeof(pSrvTx->szPathVirtual), "/%s/%s/%RU16/%s",
     759                                           SHCL_HTTPT_URL_NAMESPACE, szUuid, pTransfer->State.uID, ListEntry.pszName);
     760#endif
     761                AssertReturn(cch, VERR_BUFFER_OVERFLOW);
     762
     763                pSrvTx->pTransfer = pTransfer;
     764                pSrvTx->pRootList = NULL;
     765                pSrvTx->hObj      = NIL_SHCLOBJHANDLE;
     766
     767                RTListAppend(&pSrv->lstTransfers, &pSrvTx->Node);
     768                pSrv->cTransfers++;
     769
     770                LogFunc(("pTransfer=%p, idTransfer=%RU16, szPath=%s -> %RU32 transfers\n",
     771                         pSrvTx->pTransfer, pSrvTx->pTransfer->State.uID, pSrvTx->szPathVirtual, pSrv->cTransfers));
     772
     773                LogRel2(("Shared Clipboard: Registered HTTP transfer %RU16, now %RU32 HTTP transfers total\n",
     774                         pTransfer->State.uID, pSrv->cTransfers));
     775            }
     776        }
     777    }
     778
     779    if (RT_FAILURE(rc))
     780        RTMemFree(pSrvTx);
     781
     782    shClTransferHttpServerUnlock(pSrv);
     783
     784    LogFlowFuncLeaveRC(rc);
     785    return rc;
     786}
     787
     788/**
     789 * Unregisters a formerly registered Shared Clipboard transfer.
     790 *
     791 * @returns VBox status code.
     792 * @param   pSrv                HTTP server instance to unregister transfer from.
     793 * @param   pTransfer           Transfer to unregister.
     794 */
     795int ShClTransferHttpServerUnregisterTransfer(PSHCLHTTPSERVER pSrv, PSHCLTRANSFER pTransfer)
     796{
     797    AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
     798    AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
     799
     800    shClTransferHttpServerLock(pSrv);
     801
     802    AssertReturn(pSrv->cTransfers, VERR_WRONG_ORDER);
     803
     804    int rc = VINF_SUCCESS;
     805
     806    PSHCLHTTPSERVERTRANSFER pSrvTx;
     807    RTListForEach(&pSrv->lstTransfers, pSrvTx, SHCLHTTPSERVERTRANSFER, Node)
     808    {
     809        AssertPtr(pSrvTx->pTransfer);
     810        if (pSrvTx->pTransfer->State.uID == pTransfer->State.uID)
     811        {
     812            rc = shClTransferHttpServerDestroyTransfer(pSrv, pSrvTx);
     813            break;
     814        }
     815    }
     816
     817    shClTransferHttpServerUnlock(pSrv);
     818
     819    LogFlowFuncLeaveRC(rc);
     820    return rc;
     821}
     822
     823/**
     824 * Returns whether a specific transfer ID is registered with a HTTP server instance or not.
     825 *
     826 * @returns \c true if the transfer ID is registered, \c false if not.
     827 * @param   pSrv                HTTP server instance.
     828 * @param   idTransfer          Transfer ID to check for.
     829 */
     830bool ShClTransferHttpServerHasTransfer(PSHCLHTTPSERVER pSrv, SHCLTRANSFERID idTransfer)
     831{
     832    AssertPtrReturn(pSrv, false);
     833
     834    shClTransferHttpServerLock(pSrv);
     835
     836    const bool fRc = shClTransferHttpServerGetTransferById(pSrv, idTransfer) != NULL;
     837
     838    shClTransferHttpServerUnlock(pSrv);
     839
     840    return fRc;
     841}
     842
     843/**
     844 * Returns the used TCP port number of a HTTP server instance.
     845 *
     846 * @returns TCP port number. 0 if not specified yet.
     847 * @param   pSrv                HTTP server instance to return port for.
     848 */
     849uint16_t ShClTransferHttpServerGetPort(PSHCLHTTPSERVER pSrv)
     850{
     851    AssertPtrReturn(pSrv, 0);
     852
     853    shClTransferHttpServerLock(pSrv);
     854
     855    const uint16_t uPort = pSrv->uPort;
     856
     857    shClTransferHttpServerUnlock(pSrv);
     858
     859    return uPort;
     860}
     861
     862/**
     863 * Returns the number of registered HTTP server transfers of a HTTP server instance.
     864 *
     865 * @returns Number of registered transfers.
     866 * @param   pSrv                HTTP server instance to return registered transfers for.
     867 */
     868uint32_t ShClTransferHttpServerGetTransferCount(PSHCLHTTPSERVER pSrv)
     869{
     870    AssertPtrReturn(pSrv, 0);
     871
     872    shClTransferHttpServerLock(pSrv);
     873
     874    const uint32_t cTransfers = pSrv->cTransfers;
     875
     876    shClTransferHttpServerUnlock(pSrv);
     877
     878    return cTransfers;
     879}
     880
     881/**
     882 * Returns an allocated string with a HTTP server instance's address.
     883 *
     884 * @returns Allocated string with a HTTP server instance's address, or NULL on OOM.
     885 *          Needs to be free'd by the caller using RTStrFree().
     886 * @param   pSrv                HTTP server instance to return address for.
     887 */
     888char *ShClTransferHttpServerGetAddressA(PSHCLHTTPSERVER pSrv)
     889{
     890    AssertPtrReturn(pSrv, NULL);
     891
     892    shClTransferHttpServerLock(pSrv);
     893
     894    char *pszAddress = RTStrAPrintf2("%s:%RU16", shClTransferHttpServerGetHost(pSrv), pSrv->uPort);
     895    AssertPtr(pszAddress);
     896
     897    shClTransferHttpServerUnlock(pSrv);
     898
     899    return pszAddress;
     900}
     901
     902/**
     903 * Returns an allocated string with the URL of a given Shared Clipboard transfer ID.
     904 *
     905 * @returns Allocated string with the URL of a given Shared Clipboard transfer ID, or NULL if not found.
     906 *          Needs to be free'd by the caller using RTStrFree().
     907 * @param   pSrv                HTTP server instance to return URL for.
     908 * @param   idTransfer          Transfer ID to return the URL for.
     909 */
     910char *ShClTransferHttpServerGetUrlA(PSHCLHTTPSERVER pSrv, SHCLTRANSFERID idTransfer)
     911{
     912    AssertPtrReturn(pSrv, NULL);
     913    AssertReturn(idTransfer != NIL_SHCLTRANSFERID, NULL);
     914
     915    shClTransferHttpServerLock(pSrv);
     916
     917    PSHCLHTTPSERVERTRANSFER pSrvTx = shClTransferHttpServerGetTransferById(pSrv, idTransfer);
     918    if (!pSrvTx)
     919    {
     920        AssertFailed();
     921        shClTransferHttpServerUnlock(pSrv);
     922        return NULL;
     923    }
     924
     925    AssertReturn(RTStrNLen(pSrvTx->szPathVirtual, RTPATH_MAX), NULL);
     926    char *pszUrl = RTStrAPrintf2("%s:%RU16%s", shClTransferHttpServerGetHost(pSrv), pSrv->uPort, pSrvTx->szPathVirtual);
     927    AssertPtr(pszUrl);
     928
     929    shClTransferHttpServerUnlock(pSrv);
     930
     931    return pszUrl;
     932}
     933
     934/**
     935 * Returns whether a given HTTP server instance is running or not.
     936 *
     937 * @returns \c true if running, or \c false if not.
     938 * @param   pSrv                HTTP server instance to check running state for.
     939 */
     940bool ShClTransferHttpServerIsRunning(PSHCLHTTPSERVER pSrv)
     941{
     942    AssertPtrReturn(pSrv, false);
     943
     944    return (pSrv->hHTTPServer != NIL_RTHTTPSERVER); /* Seems enough for now. */
     945}
     946
     947
     948/*********************************************************************************************************************************
     949*   Public Shared Clipboard HTTP context functions                                                                               *
     950*********************************************************************************************************************************/
     951
     952/**
     953 * Registers a Shared Clipboard transfer to a HTTP context and starts the HTTP server, if not started already.
    99954 *
    100955 * @returns VBox status code.
     
    102957 * @param   pTransfer           Transfer to register.
    103958 */
    104 int ShClHttpTransferRegister(PSHCLHTTPCONTEXT pCtx, PSHCLTRANSFER pTransfer)
     959int ShClHttpTransferRegisterAndMaybeStart(PSHCLHTTPCONTEXT pCtx, PSHCLTRANSFER pTransfer)
    105960{
    106961    int rc = VINF_SUCCESS;
     
    108963    /* Start the built-in HTTP server to serve file(s). */
    109964    if (!ShClTransferHttpServerIsRunning(&pCtx->HttpServer)) /* Only one HTTP server per transfer context. */
    110         rc = ShClTransferHttpServerCreate(&pCtx->HttpServer, NULL /* puPort */);
     965        rc = ShClTransferHttpServerCreate(&pCtx->HttpServer, 32 /* cMaxAttempts */, NULL /* puPort */);
    111966
    112967    if (RT_SUCCESS(rc))
     
    117972
    118973/**
    119  * Unregisters a formerly registered Shared Clipboard transfer.
     974 * Unregisters a formerly registered Shared Clipboard transfer and stops the HTTP server, if no transfers are left.
    120975 *
    121976 * @returns VBox status code.
     
    123978 * @param   pTransfer           Transfer to unregister.
    124979 */
    125 int ShClHttpTransferUnregister(PSHCLHTTPCONTEXT pCtx, PSHCLTRANSFER pTransfer)
     980int ShClHttpTransferUnregisterAndMaybeStop(PSHCLHTTPCONTEXT pCtx, PSHCLTRANSFER pTransfer)
    126981{
    127982    int rc = VINF_SUCCESS;
     
    143998}
    144999
    145 
    146 /*********************************************************************************************************************************
    147 *   Internal Shared Clipboard HTTP transfer functions                                                                            *
    148 *********************************************************************************************************************************/
    149 
    150 DECLINLINE(void) shClHttpTransferLock(PSHCLHTTPSERVERTRANSFER pSrvTx)
    151 {
    152     int rc2 = RTCritSectEnter(&pSrvTx->CritSect);
    153     AssertRC(rc2);
    154 }
    155 
    156 DECLINLINE(void) shClHttpTransferUnlock(PSHCLHTTPSERVERTRANSFER pSrvTx)
    157 {
    158     int rc2 = RTCritSectEnter(&pSrvTx->CritSect);
    159     AssertRC(rc2);
    160 }
    161 
    162 /**
    163  * Return the HTTP server transfer for a specific transfer ID.
    164  *
    165  * @returns Pointer to HTTP server transfer if found, NULL if not found.
    166  * @param   pSrv                HTTP server instance.
    167  * @param   idTransfer          Transfer ID to return HTTP server transfer for.
    168  */
    169 static PSHCLHTTPSERVERTRANSFER shClTransferHttpServerGetTransferById(PSHCLHTTPSERVER pSrv, SHCLTRANSFERID idTransfer)
    170 {
    171     PSHCLHTTPSERVERTRANSFER pSrvTx;
    172     RTListForEach(&pSrv->lstTransfers, pSrvTx, SHCLHTTPSERVERTRANSFER, Node) /** @todo Slow O(n) lookup, but does it for now. */
    173     {
    174         if (pSrvTx->pTransfer->State.uID == idTransfer)
    175             return pSrvTx;
    176     }
    177 
    178     return NULL;
    179 }
    180 
    181 /**
    182  * Returns a HTTP server transfer from a given URL.
    183  *
    184  * @returns Pointer to HTTP server transfer if found, NULL if not found.
    185  * @param   pThis               HTTP server instance data.
    186  * @param   pszUrl              URL to validate.
    187  */
    188 DECLINLINE(PSHCLHTTPSERVERTRANSFER) shClTransferHttpGetTransferFromUrl(PSHCLHTTPSERVER pThis, const char *pszUrl)
    189 {
    190     AssertPtrReturn(pszUrl, NULL);
    191 
    192     PSHCLHTTPSERVERTRANSFER pSrvTx = NULL;
    193 
    194     PSHCLHTTPSERVERTRANSFER pSrvTxCur;
    195     RTListForEach(&pThis->lstTransfers, pSrvTxCur, SHCLHTTPSERVERTRANSFER, Node)
    196     {
    197         AssertPtr(pSrvTxCur->pTransfer);
    198 
    199         LogFlowFunc(("pSrvTxCur=%s\n", pSrvTxCur->szPathVirtual));
    200 
    201         /* Be picky here, do a case sensitive comparison. */
    202         if (RTStrStartsWith(pszUrl, pSrvTxCur->szPathVirtual))
    203         {
    204             pSrvTx = pSrvTxCur;
    205             break;
    206         }
    207     }
    208 
    209     if (!pSrvTx)
    210         LogRel2(("Shared Clipboard: HTTP URL '%s' not valid\n", pszUrl));
    211 
    212     LogFlowFunc(("pszUrl=%s, pSrvTx=%p\n", pszUrl, pSrvTx));
    213     return pSrvTx;
    214 }
    215 
    216 /**
    217  * Returns a HTTP server transfer from an internal HTTP handle.
    218  *
    219  * @returns Pointer to HTTP server transfer if found, NULL if not found.
    220  * @param   pThis               HTTP server instance data.
    221  * @param   pvHandle            Handle to return transfer for.
    222  */
    223 DECLINLINE(PSHCLHTTPSERVERTRANSFER) shClTransferHttpGetTransferFromHandle(PSHCLHTTPSERVER pThis, void *pvHandle)
    224 {
    225     AssertPtrReturn(pvHandle, NULL);
    226 
    227     const SHCLTRANSFERID uHandle = *(uint16_t *)pvHandle;
    228 
    229     /** @ŧodo Use a handle lookup table (map) later. */
    230     PSHCLHTTPSERVERTRANSFER pSrvTxCur;
    231     RTListForEach(&pThis->lstTransfers, pSrvTxCur, SHCLHTTPSERVERTRANSFER, Node)
    232     {
    233         AssertPtr(pSrvTxCur->pTransfer);
    234 
    235         if (pSrvTxCur->pTransfer->State.uID == uHandle) /** @ŧodo We're using the transfer ID as handle for now. */
    236             return pSrvTxCur;
    237     }
    238 
    239     return NULL;
    240 }
    241 
    242 static int shClTransferHttpGetTransferRoots(PSHCLHTTPSERVER pThis, PSHCLHTTPSERVERTRANSFER pSrvTx)
    243 {
    244     RT_NOREF(pThis);
    245 
    246     int rc = VINF_SUCCESS;
    247 
    248     if (pSrvTx->pRootList == NULL)
    249     {
    250         AssertPtr(pSrvTx->pTransfer);
    251         rc = ShClTransferRootsGet(pSrvTx->pTransfer, &pSrvTx->pRootList);
    252     }
    253 
    254     return rc;
    255 }
    256 
    257 
    258 /*********************************************************************************************************************************
    259 *   HTTP server callback implementations                                                                                         *
    260 *********************************************************************************************************************************/
    261 
    262 /** @copydoc RTHTTPSERVERCALLBACKS::pfnOpen */
    263 static DECLCALLBACK(int) shClTransferHttpOpen(PRTHTTPCALLBACKDATA pData, PRTHTTPSERVERREQ pReq, void **ppvHandle)
    264 {
    265     PSHCLHTTPSERVER pThis = (PSHCLHTTPSERVER)pData->pvUser;
    266     Assert(pData->cbUser == sizeof(SHCLHTTPSERVER));
    267 
    268     int rc;
    269 
    270     PSHCLHTTPSERVERTRANSFER pSrvTx = shClTransferHttpGetTransferFromUrl(pThis, pReq->pszUrl);
    271     if (pSrvTx)
    272     {
    273         shClHttpTransferLock(pSrvTx);
    274 
    275         AssertPtr(pSrvTx->pTransfer);
    276 
    277         SHCLOBJOPENCREATEPARMS openParms;
    278         rc = ShClTransferObjOpenParmsInit(&openParms);
    279         if (RT_SUCCESS(rc))
    280         {
    281             openParms.fCreate = SHCL_OBJ_CF_ACCESS_READ
    282                               | SHCL_OBJ_CF_ACCESS_DENYWRITE;
    283 
    284             rc = RTStrCopy(openParms.pszPath, openParms.cbPath, "foo"); /** @ŧodo BUGBUG !!!! */
    285             if (RT_SUCCESS(rc))
    286             {
    287                 rc = ShClTransferObjOpen(pSrvTx->pTransfer, &openParms, &pSrvTx->hObj);
    288                 if (RT_SUCCESS(rc))
    289                 {
    290                     *ppvHandle = &pSrvTx->hObj;
    291                     LogRel2(("Shared Clipboard: HTTP transfer (handle %RU64) started ...\n", pSrvTx->hObj));
    292                 }
    293             }
    294 
    295             ShClTransferObjOpenParmsDestroy(&openParms);
    296         }
    297 
    298         shClHttpTransferUnlock(pSrvTx);
    299     }
    300     else
    301         rc = VERR_NOT_FOUND;
    302 
    303     if (RT_FAILURE(rc))
    304         LogRel(("Shared Clipboard: Error starting HTTP transfer for '%s', rc=%Rrc\n", pReq->pszUrl, rc));
    305 
    306     LogFlowFuncLeaveRC(rc);
    307     return rc;
    308 }
    309 
    310 /** @copydoc RTHTTPSERVERCALLBACKS::pfnRead */
    311 static DECLCALLBACK(int) shClTransferHttpRead(PRTHTTPCALLBACKDATA pData, void *pvHandle, void *pvBuf, size_t cbBuf, size_t *pcbRead)
    312 {
    313     PSHCLHTTPSERVER pThis = (PSHCLHTTPSERVER)pData->pvUser;
    314     Assert(pData->cbUser == sizeof(SHCLHTTPSERVER));
    315 
    316     RT_NOREF(pvBuf, cbBuf, pcbRead);
    317 
    318     int rc;
    319 
    320     PSHCLHTTPSERVERTRANSFER pSrvTx = shClTransferHttpGetTransferFromHandle(pThis, pvHandle);
    321     if (pSrvTx)
    322     {
    323         Assert(pSrvTx->hObj != SHCLOBJHANDLE_INVALID);
    324 
    325         uint32_t cbRead;
    326         rc = ShClTransferObjRead(pSrvTx->pTransfer, pSrvTx->hObj, pvBuf, cbBuf, 0 /* fFlags */, &cbRead);
    327         if (RT_SUCCESS(rc))
    328         {
    329             *pcbRead = (uint32_t)cbRead;
    330         }
    331 
    332         if (RT_FAILURE(rc))
    333             LogRel(("Shared Clipboard: Error reading HTTP transfer (handle %RU64), rc=%Rrc\n", pSrvTx->hObj, rc));
    334     }
    335     else
    336         rc = VERR_NOT_FOUND;
    337 
    338     LogFlowFuncLeaveRC(rc);
    339     return rc;
    340 }
    341 
    342 /** @copydoc RTHTTPSERVERCALLBACKS::pfnClose */
    343 static DECLCALLBACK(int) shClTransferHttpClose(PRTHTTPCALLBACKDATA pData, void *pvHandle)
    344 {
    345     PSHCLHTTPSERVER pThis = (PSHCLHTTPSERVER)pData->pvUser;
    346     Assert(pData->cbUser == sizeof(SHCLHTTPSERVER));
    347 
    348     int rc;
    349 
    350     PSHCLHTTPSERVERTRANSFER pSrvTx = shClTransferHttpGetTransferFromHandle(pThis, pvHandle);
    351     if (pSrvTx)
    352     {
    353         shClHttpTransferLock(pSrvTx);
    354 
    355         Assert(pSrvTx->hObj != SHCLOBJHANDLE_INVALID);
    356         rc = ShClTransferObjClose(pSrvTx->pTransfer, pSrvTx->hObj);
    357         if (RT_SUCCESS(rc))
    358         {
    359             pSrvTx->hObj = SHCLOBJHANDLE_INVALID;
    360             LogRel2(("Shared Clipboard: HTTP transfer %RU16 done\n", pSrvTx->pTransfer->State.uID));
    361         }
    362 
    363         if (RT_FAILURE(rc))
    364             LogRel(("Shared Clipboard: Error closing HTTP transfer (handle %RU64), rc=%Rrc\n", pSrvTx->hObj, rc));
    365 
    366         shClHttpTransferUnlock(pSrvTx);
    367     }
    368     else
    369         rc = VERR_NOT_FOUND;
    370 
    371     LogFlowFuncLeaveRC(rc);
    372     return rc;
    373 }
    374 
    375 /** @copydoc RTHTTPSERVERCALLBACKS::pfnQueryInfo */
    376 static DECLCALLBACK(int) shClTransferHttpQueryInfo(PRTHTTPCALLBACKDATA pData,
    377                                                    PRTHTTPSERVERREQ pReq, PRTFSOBJINFO pObjInfo, char **ppszMIMEHint)
    378 {
    379     PSHCLHTTPSERVER pThis = (PSHCLHTTPSERVER)pData->pvUser;
    380     Assert(pData->cbUser == sizeof(SHCLHTTPSERVER));
    381 
    382     int rc;
    383 
    384     PSHCLHTTPSERVERTRANSFER pSrvTx = shClTransferHttpGetTransferFromUrl(pThis, pReq->pszUrl);
    385     if (pSrvTx)
    386     {
    387         shClHttpTransferLock(pSrvTx);
    388 
    389         rc = shClTransferHttpGetTransferRoots(pThis, pSrvTx);
    390 
    391         shClHttpTransferUnlock(pSrvTx);
    392     }
    393     else
    394         rc = VERR_NOT_FOUND;
    395 
    396     RT_NOREF(pObjInfo, ppszMIMEHint);
    397 
    398     LogFlowFuncLeaveRC(rc);
    399     return rc;
    400 }
    401 
    402 /** @copydoc RTHTTPSERVERCALLBACKS::pfnDestroy */
    403 static DECLCALLBACK(int) shClTransferHttpDestroy(PRTHTTPCALLBACKDATA pData)
    404 {
    405     PSHCLHTTPSERVER pThis = (PSHCLHTTPSERVER)pData->pvUser;
    406     Assert(pData->cbUser == sizeof(SHCLHTTPSERVER));
    407 
    408     return shClTransferHttpServerDestroyInternal(pThis);
    409 }
    410 
    411 
    412 /*********************************************************************************************************************************
    413 *   Internal Shared Clipboard HTTP server functions                                                                              *
    414 *********************************************************************************************************************************/
    415 
    416 /**
    417  * Destroys a Shared Clipboard HTTP server instance, internal version.
    418  *
    419  * @returns VBox status code.
    420  * @param   pSrv                Shared Clipboard HTTP server instance to destroy.
    421  */
    422 static int shClTransferHttpServerDestroyInternal(PSHCLHTTPSERVER pSrv)
    423 {
    424     PSHCLHTTPSERVERTRANSFER pSrvTx, pSrvTxNext;
    425     RTListForEachSafe(&pSrv->lstTransfers, pSrvTx, pSrvTxNext, SHCLHTTPSERVERTRANSFER, Node)
    426     {
    427         RTListNodeRemove(&pSrvTx->Node);
    428 
    429         RTMemFree(pSrvTx);
    430         pSrvTx = NULL;
    431     }
    432 
    433     RTHttpServerResponseDestroy(&pSrv->Resp);
    434 
    435     pSrv->hHTTPServer = NIL_RTHTTPSERVER;
    436 
    437     int rc = VINF_SUCCESS;
    438 
    439     if (RTCritSectIsInitialized(&pSrv->CritSect))
    440         rc = RTCritSectDelete(&pSrv->CritSect);
    441 
    442     return rc;
    443 }
    444 
    445 /**
    446  * Locks the critical section of a Shared Clipboard HTTP server instance.
    447  *
    448  * @param   pSrv                Shared Clipboard HTTP server instance to lock.
    449  */
    450 DECLINLINE(void) shClTransferHttpServerLock(PSHCLHTTPSERVER pSrv)
    451 {
    452     int rc2 = RTCritSectEnter(&pSrv->CritSect);
    453     AssertRC(rc2);
    454 }
    455 
    456 /**
    457  * Unlocks the critical section of a Shared Clipboard HTTP server instance.
    458  *
    459  * @param   pSrv                Shared Clipboard HTTP server instance to unlock.
    460  */
    461 DECLINLINE(void) shClTransferHttpServerUnlock(PSHCLHTTPSERVER pSrv)
    462 {
    463     int rc2 = RTCritSectLeave(&pSrv->CritSect);
    464     AssertRC(rc2);
    465 }
    466 
    467 /**
    468  * Initializes a new Shared Clipboard HTTP server instance.
    469  *
    470  * @param   pSrv                HTTP server instance to initialize.
    471  */
    472 static void shClTransferHttpServerInitInternal(PSHCLHTTPSERVER pSrv)
    473 {
    474     pSrv->hHTTPServer = NIL_RTHTTPSERVER;
    475     pSrv->uPort       = 0;
    476     RTListInit(&pSrv->lstTransfers);
    477     pSrv->cTransfers  = 0;
    478     int rc2 = RTHttpServerResponseInit(&pSrv->Resp);
    479     AssertRC(rc2);
    480 }
    481 
    482 
    483 /*********************************************************************************************************************************
    484 *   Public Shared Clipboard HTTP server functions                                                                                *
    485 *********************************************************************************************************************************/
    486 
    487 /**
    488  * Initializes a new Shared Clipboard HTTP server instance.
    489  *
    490  * @param   pSrv                HTTP server instance to initialize.
    491  */
    492 void ShClTransferHttpServerInit(PSHCLHTTPSERVER pSrv)
    493 {
    494     AssertPtrReturnVoid(pSrv);
    495 
    496     shClTransferHttpServerInitInternal(pSrv);
    497 }
    498 
    499 /**
    500  * Creates a new Shared Clipboard HTTP server instance, extended version.
    501  *
    502  * @returns VBox status code.
    503  * @param   pSrv                HTTP server instance to create.
    504  * @param   uPort               TCP port number to use.
    505  */
    506 int ShClTransferHttpServerCreateEx(PSHCLHTTPSERVER pSrv, uint16_t uPort)
    507 {
    508     AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
    509 
    510     RTHTTPSERVERCALLBACKS Callbacks;
    511     RT_ZERO(Callbacks);
    512 
    513     Callbacks.pfnOpen          = shClTransferHttpOpen;
    514     Callbacks.pfnRead          = shClTransferHttpRead;
    515     Callbacks.pfnClose         = shClTransferHttpClose;
    516     Callbacks.pfnQueryInfo     = shClTransferHttpQueryInfo;
    517     Callbacks.pfnDestroy       = shClTransferHttpDestroy;
    518 
    519     /* Note: The server always and *only* runs against the localhost interface. */
    520     int rc = RTHttpServerCreate(&pSrv->hHTTPServer, "localhost", uPort, &Callbacks,
    521                                 pSrv, sizeof(SHCLHTTPSERVER));
    522     if (RT_SUCCESS(rc))
    523     {
    524         rc = RTCritSectInit(&pSrv->CritSect);
    525         AssertRCReturn(rc, rc);
    526 
    527         pSrv->uPort = uPort;
    528 
    529         LogRel2(("Shared Clipboard: HTTP server running at port %RU16\n", pSrv->uPort));
    530     }
    531     else
    532     {
    533         int rc2 = shClTransferHttpServerDestroyInternal(pSrv);
    534         AssertRC(rc2);
    535     }
    536 
    537     if (RT_FAILURE(rc))
    538         LogRel(("Shared Clipboard: HTTP server failed to run, rc=%Rrc\n", rc));
    539 
    540     return rc;
    541 }
    542 
    543 /**
    544  * Creates a new Shared Clipboard HTTP server instance.
    545  *
    546  * This does automatic probing of TCP ports if one already is being used.
    547  *
    548  * @returns VBox status code.
    549  * @param   pSrv                HTTP server instance to create.
    550  * @param   puPort              Where to return the TCP port number being used on success. Optional.
    551  */
    552 int ShClTransferHttpServerCreate(PSHCLHTTPSERVER pSrv, uint16_t *puPort)
    553 {
    554     AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
    555     /* puPort is optional. */
    556 
    557     /** @todo Try favorite ports first (e.g. 8080, 8000, ...)? */
    558 
    559     RTRAND hRand;
    560     int rc = RTRandAdvCreateSystemFaster(&hRand); /* Should be good enough for this task. */
    561     if (RT_SUCCESS(rc))
    562     {
    563         uint16_t uPort;
    564         for (int i = 0; i < 32; i++)
    565         {
    566 #ifdef DEBUG_andy
    567             uPort = 8080; /* Make the port predictable, but only for me, mwahaha! :-). */
    568 #else
    569             uPort = RTRandAdvU32Ex(hRand, 1024, UINT16_MAX);
    570 #endif
    571             rc = ShClTransferHttpServerCreateEx(pSrv, (uint32_t)uPort);
    572             if (RT_SUCCESS(rc))
    573             {
    574                 if (puPort)
    575                     *puPort = uPort;
    576                 break;
    577             }
    578         }
    579 
    580         RTRandAdvDestroy(hRand);
    581     }
    582 
    583     return rc;
    584 }
    585 
    586 /**
    587  * Destroys a Shared Clipboard HTTP server instance.
    588  *
    589  * @returns VBox status code.
    590  * @param   pSrv                HTTP server instance to destroy.
    591  */
    592 int ShClTransferHttpServerDestroy(PSHCLHTTPSERVER pSrv)
    593 {
    594     AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
    595 
    596     if (pSrv->hHTTPServer == NIL_RTHTTPSERVER)
    597         return VINF_SUCCESS;
    598 
    599     Assert(pSrv->cTransfers == 0); /* Sanity. */
    600 
    601     int rc = RTHttpServerDestroy(pSrv->hHTTPServer);
    602     if (RT_SUCCESS(rc))
    603         rc = shClTransferHttpServerDestroyInternal(pSrv);
    604 
    605     if (RT_SUCCESS(rc))
    606         LogRel2(("Shared Clipboard: HTTP server stopped\n"));
    607     else
    608         LogRel(("Shared Clipboard: HTTP server failed to stop, rc=%Rrc\n", rc));
    609 
    610     return rc;
    611 }
    612 
    613 /**
    614  * Registers a Shared Clipboard transfer to a HTTP server instance.
    615  *
    616  * @returns VBox status code.
    617  * @param   pSrv                HTTP server instance to register transfer for.
    618  * @param   pTransfer           Transfer to register.
    619  */
    620 int ShClTransferHttpServerRegisterTransfer(PSHCLHTTPSERVER pSrv, PSHCLTRANSFER pTransfer)
    621 {
    622     AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
    623     AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
    624 
    625     AssertReturn(pTransfer->State.uID, VERR_INVALID_PARAMETER); /* Paranoia. */
    626 
    627     shClTransferHttpServerLock(pSrv);
    628 
    629     PSHCLHTTPSERVERTRANSFER pSrvTx = (PSHCLHTTPSERVERTRANSFER)RTMemAllocZ(sizeof(SHCLHTTPSERVERTRANSFER));
    630     AssertPtrReturn(pSrvTx, VERR_NO_MEMORY);
    631 
    632     RTUUID Uuid;
    633     int rc = RTUuidCreate(&Uuid);
    634     if (RT_SUCCESS(rc))
    635     {
    636         char szUuid[64];
    637         rc = RTUuidToStr(&Uuid, szUuid, sizeof(szUuid));
    638         if (RT_SUCCESS(rc))
    639         {
    640             rc = RTCritSectInit(&pSrvTx->CritSect);
    641             AssertRC(rc);
    642 
    643             /* Create the virtual HTTP path for the transfer.
    644              * Every transfer has a dedicated HTTP path. */
    645 #ifdef DEBUG_andy
    646             ssize_t cch = RTStrPrintf2(pSrvTx->szPathVirtual, sizeof(pSrvTx->szPathVirtual), "/d1bbda60-80b7-45dc-a41c-ac4686c1d988/10664");
    647 #else
    648             ssize_t cch = RTStrPrintf2(pSrvTx->szPathVirtual, sizeof(pSrvTx->szPathVirtual), "%s/%RU16/", szUuid, pTransfer->State.uID);
    649 #endif
    650             AssertReturn(cch, VERR_BUFFER_OVERFLOW);
    651 
    652             pSrvTx->pTransfer = pTransfer;
    653             pSrvTx->pRootList = NULL;
    654             pSrvTx->hObj      = SHCLOBJHANDLE_INVALID;
    655 
    656             RTListAppend(&pSrv->lstTransfers, &pSrvTx->Node);
    657             pSrv->cTransfers++;
    658 
    659             LogFunc(("pTransfer=%p, idTransfer=%RU16, szPath=%s -> %RU32 transfers\n",
    660                      pSrvTx->pTransfer, pSrvTx->pTransfer->State.uID, pSrvTx->szPathVirtual, pSrv->cTransfers));
    661 
    662             LogRel2(("Shared Clipboard: Registered HTTP transfer %RU16, now %RU32 HTTP transfers total\n",
    663                      pTransfer->State.uID, pSrv->cTransfers));
    664         }
    665     }
    666 
    667     if (RT_FAILURE(rc))
    668         RTMemFree(pSrvTx);
    669 
    670     shClTransferHttpServerUnlock(pSrv);
    671 
    672     LogFlowFuncLeaveRC(rc);
    673     return rc;
    674 }
    675 
    676 /**
    677  * Unregisters a formerly registered Shared Clipboard transfer.
    678  *
    679  * @returns VBox status code.
    680  * @param   pSrv                HTTP server instance to unregister transfer from.
    681  * @param   pTransfer           Transfer to unregister.
    682  */
    683 int ShClTransferHttpServerUnregisterTransfer(PSHCLHTTPSERVER pSrv, PSHCLTRANSFER pTransfer)
    684 {
    685     AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
    686     AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
    687 
    688     shClTransferHttpServerLock(pSrv);
    689 
    690     AssertReturn(pSrv->cTransfers, VERR_WRONG_ORDER);
    691 
    692     int rc = VINF_SUCCESS;
    693 
    694     PSHCLHTTPSERVERTRANSFER pSrvTx;
    695     RTListForEach(&pSrv->lstTransfers, pSrvTx, SHCLHTTPSERVERTRANSFER, Node)
    696     {
    697         AssertPtr(pSrvTx->pTransfer);
    698         if (pSrvTx->pTransfer->State.uID == pTransfer->State.uID)
    699         {
    700             RTListNodeRemove(&pSrvTx->Node);
    701 
    702             Assert(pSrv->cTransfers);
    703             pSrv->cTransfers--;
    704 
    705             LogFunc(("pTransfer=%p, idTransfer=%RU16, szPath=%s -> %RU32 transfers\n",
    706                      pSrvTx->pTransfer, pSrvTx->pTransfer->State.uID, pSrvTx->szPathVirtual, pSrv->cTransfers));
    707 
    708             LogRel2(("Shared Clipboard: Unregistered HTTP transfer %RU16, now %RU32 HTTP transfers total\n",
    709                      pTransfer->State.uID, pSrv->cTransfers));
    710 
    711             rc = RTCritSectDelete(&pSrvTx->CritSect);
    712             AssertRC(rc);
    713 
    714             RTMemFree(pSrvTx);
    715             pSrvTx = NULL;
    716 
    717             rc = VINF_SUCCESS;
    718             break;
    719         }
    720     }
    721 
    722     shClTransferHttpServerUnlock(pSrv);
    723 
    724     LogFlowFuncLeaveRC(rc);
    725     return rc;
    726 }
    727 
    728 /**
    729  * Returns whether a specific transfer ID is registered with a HTTP server instance or not.
    730  *
    731  * @returns \c true if the transfer ID is registered, \c false if not.
    732  * @param   pSrv                HTTP server instance.
    733  * @param   idTransfer          Transfer ID to check for.
    734  */
    735 bool ShClTransferHttpServerHasTransfer(PSHCLHTTPSERVER pSrv, SHCLTRANSFERID idTransfer)
    736 {
    737     AssertPtrReturn(pSrv, false);
    738 
    739     shClTransferHttpServerLock(pSrv);
    740 
    741     const bool fRc = shClTransferHttpServerGetTransferById(pSrv, idTransfer) != NULL;
    742 
    743     shClTransferHttpServerUnlock(pSrv);
    744 
    745     return fRc;
    746 }
    747 
    748 /**
    749  * Returns the used TCP port number of a HTTP server instance.
    750  *
    751  * @returns TCP port number. 0 if not specified yet.
    752  * @param   pSrv                HTTP server instance to return port for.
    753  */
    754 uint16_t ShClTransferHttpServerGetPort(PSHCLHTTPSERVER pSrv)
    755 {
    756     AssertPtrReturn(pSrv, 0);
    757 
    758     shClTransferHttpServerLock(pSrv);
    759 
    760     const uint16_t uPort = pSrv->uPort;
    761 
    762     shClTransferHttpServerUnlock(pSrv);
    763 
    764     return uPort;
    765 }
    766 
    767 /**
    768  * Returns the number of registered HTTP server transfers of a HTTP server instance.
    769  *
    770  * @returns Number of registered transfers.
    771  * @param   pSrv                HTTP server instance to return registered transfers for.
    772  */
    773 uint32_t ShClTransferHttpServerGetTransferCount(PSHCLHTTPSERVER pSrv)
    774 {
    775     AssertPtrReturn(pSrv, 0);
    776 
    777     shClTransferHttpServerLock(pSrv);
    778 
    779     const uint32_t cTransfers = pSrv->cTransfers;
    780 
    781     shClTransferHttpServerUnlock(pSrv);
    782 
    783     return cTransfers;
    784 }
    785 
    786 /**
    787  * Returns the host name (scheme) of a HTTP server instance.
    788  *
    789  * @param   pSrv                HTTP server instance to return host name (scheme) for.
    790  *
    791  * @returns Host name (scheme).
    792  */
    793 static const char *shClTransferHttpServerGetHost(PSHCLHTTPSERVER pSrv)
    794 {
    795     RT_NOREF(pSrv);
    796     return "http://localhost"; /* Hardcoded for now. */
    797 }
    798 
    799 /**
    800  * Returns an allocated string with a HTTP server instance's address.
    801  *
    802  * @returns Allocated string with a HTTP server instance's address, or NULL on OOM.
    803  *          Needs to be free'd by the caller using RTStrFree().
    804  * @param   pSrv                HTTP server instance to return address for.
    805  */
    806 char *ShClTransferHttpServerGetAddressA(PSHCLHTTPSERVER pSrv)
    807 {
    808     AssertPtrReturn(pSrv, NULL);
    809 
    810     shClTransferHttpServerLock(pSrv);
    811 
    812     char *pszAddress = RTStrAPrintf2("%s:%RU16", shClTransferHttpServerGetHost(pSrv), pSrv->uPort);
    813     AssertPtr(pszAddress);
    814 
    815     shClTransferHttpServerUnlock(pSrv);
    816 
    817     return pszAddress;
    818 }
    819 
    820 /**
    821  * Returns an allocated string with the URL of a given Shared Clipboard transfer ID.
    822  *
    823  * @returns Allocated string with the URL of a given Shared Clipboard transfer ID, or NULL if not found.
    824  *          Needs to be free'd by the caller using RTStrFree().
    825  * @param   pSrv                HTTP server instance to return URL for.
    826  */
    827 char *ShClTransferHttpServerGetUrlA(PSHCLHTTPSERVER pSrv, SHCLTRANSFERID idTransfer)
    828 {
    829     AssertPtrReturn(pSrv, NULL);
    830     AssertReturn(idTransfer != NIL_SHCLTRANSFERID, NULL);
    831 
    832     shClTransferHttpServerLock(pSrv);
    833 
    834     PSHCLHTTPSERVERTRANSFER pSrvTx = shClTransferHttpServerGetTransferById(pSrv, idTransfer);
    835     if (!pSrvTx)
    836     {
    837         AssertFailed();
    838         shClTransferHttpServerUnlock(pSrv);
    839         return NULL;
    840     }
    841 
    842     AssertReturn(RTStrNLen(pSrvTx->szPathVirtual, RTPATH_MAX), NULL);
    843     char *pszUrl = RTStrAPrintf2("%s:%RU16/%s", shClTransferHttpServerGetHost(pSrv), pSrv->uPort, pSrvTx->szPathVirtual);
    844     AssertPtr(pszUrl);
    845 
    846     shClTransferHttpServerUnlock(pSrv);
    847 
    848     return pszUrl;
    849 }
    850 
    851 /**
    852  * Returns whether a given HTTP server instance is running or not.
    853  *
    854  * @returns \c true if running, or \c false if not.
    855  * @param   pSrv                HTTP server instance to check running state for.
    856  */
    857 bool ShClTransferHttpServerIsRunning(PSHCLHTTPSERVER pSrv)
    858 {
    859     AssertPtrReturn(pSrv, false);
    860 
    861     return (pSrv->hHTTPServer != NIL_RTHTTPSERVER); /* Seems enough for now. */
    862 }
  • trunk/src/VBox/GuestHost/SharedClipboard/clipboard-transfers.cpp

    r99405 r99937  
    4949static int shClConvertFileCreateFlags(uint32_t fShClFlags, uint64_t *pfOpen);
    5050static int shClTransferResolvePathAbs(PSHCLTRANSFER pTransfer, const char *pszPath, uint32_t fFlags, char **ppszResolved);
     51static int shClTransferValidatePath(const char *pcszPath, bool fMustExist);
    5152
    5253/** @todo Split this file up in different modules. */
     
    7677
    7778    for (uint32_t i = 0; i < pRootList->Hdr.cRoots; i++)
    78         ShClTransferListEntryInit(&pRootList->paEntries[i]);
     79        ShClTransferListEntryDestroy(&pRootList->paEntries[i]);
    7980
    8081    RTMemFree(pRootList);
     
    107108        return;
    108109
    109     pRootLstHdr->fRoots = 0;
    110110    pRootLstHdr->cRoots = 0;
    111111}
     
    193193    AssertPtrReturn(pInfo, VERR_INVALID_POINTER);
    194194
    195     pInfo->hList   = SHCLLISTHANDLE_INVALID;
     195    pInfo->hList   = NIL_SHCLLISTHANDLE;
    196196    pInfo->enmType = SHCLOBJTYPE_INVALID;
    197197
     
    556556
    557557/**
    558  * Initializes a clipboard list entry structure.
     558 * Returns whether a given list entry name is valid or not.
     559 *
     560 * @returns \c true if valid, or \c false if not.
     561 * @param   pszName             Name to check.
     562 * @param   cbName              Size (in bytes) of \a pszName to check.
     563 *                              Includes terminator.
     564 */
     565static bool shclTransferListEntryNameIsValid(const char *pszName, size_t cbName)
     566{
     567    if (!pszName)
     568        return false;
     569
     570    size_t const cchLen = strlen(pszName);
     571
     572    if (  !cbName
     573        || cchLen == 0
     574        || cchLen > cbName                 /* Includes zero termination */ - 1
     575        || cchLen > SHCLLISTENTRY_MAX_NAME /* Ditto */ - 1)
     576    {
     577        return false;
     578    }
     579
     580    int rc = shClTransferValidatePath(pszName, false /* fMustExist */);
     581    if (RT_FAILURE(rc))
     582        return false;
     583
     584    return true;
     585}
     586
     587/**
     588 * Initializes a clipboard list entry structure, extended version.
    559589 *
    560590 * @returns VBox status code.
    561591 * @param   pListEntry          Clipboard list entry structure to initialize.
    562  */
    563 int ShClTransferListEntryInit(PSHCLLISTENTRY pListEntry)
     592 * @param   pszName             Name (e.g. filename) to use. Can be NULL if not being used.
     593 *                              Up to SHCLLISTENTRY_MAX_NAME characters.
     594 */
     595int ShClTransferListEntryInitEx(PSHCLLISTENTRY pListEntry, const char *pszName)
    564596{
    565597    AssertPtrReturn(pListEntry, VERR_INVALID_POINTER);
     598    AssertReturn   (   pszName == NULL
     599                    || shclTransferListEntryNameIsValid(pszName, strlen(pszName) + 1), VERR_INVALID_PARAMETER);
    566600
    567601    RT_BZERO(pListEntry, sizeof(SHCLLISTENTRY));
    568602
    569     pListEntry->pszName = RTStrAlloc(SHCLLISTENTRY_MAX_NAME);
    570     if (!pListEntry->pszName)
    571         return VERR_NO_MEMORY;
    572 
    573     pListEntry->cbName = SHCLLISTENTRY_MAX_NAME;
     603    if (pszName)
     604    {
     605        pListEntry->pszName = RTStrDup(pszName);
     606        if (!pListEntry->pszName)
     607            return VERR_NO_MEMORY;
     608        pListEntry->cbName = strlen(pszName) + 1 /* Include terminator */;
     609    }
    574610
    575611    pListEntry->pvInfo = (PSHCLFSOBJINFO)RTMemAlloc(sizeof(SHCLFSOBJINFO));
     
    583619
    584620    return VERR_NO_MEMORY;
     621}
     622
     623/**
     624 * Initializes a clipboard list entry structure (as empty / invalid).
     625 *
     626 * @returns VBox status code.
     627 * @param   pListEntry          Clipboard list entry structure to initialize.
     628 */
     629int ShClTransferListEntryInit(PSHCLLISTENTRY pListEntry)
     630{
     631    return ShClTransferListEntryInitEx(pListEntry, NULL);
    585632}
    586633
     
    621668    AssertPtrReturn(pListEntry, false);
    622669
    623     if (   !pListEntry->pszName
    624         || !pListEntry->cbName
    625         || strlen(pListEntry->pszName) == 0
    626         || strlen(pListEntry->pszName) > pListEntry->cbName /* Includes zero termination */ - 1)
    627     {
     670    if (!shclTransferListEntryNameIsValid(pListEntry->pszName, pListEntry->cbName))
    628671        return false;
    629     }
    630672
    631673    if (pListEntry->cbInfo) /* cbInfo / pvInfo is optional. */
     
    650692    LogFlowFuncEnter();
    651693
    652     pObjCtx->uHandle  = SHCLOBJHANDLE_INVALID;
     694    pObjCtx->uHandle  = NIL_SHCLOBJHANDLE;
    653695
    654696    return VINF_SUCCESS;
     
    676718{
    677719    return (   pObjCtx
    678             && pObjCtx->uHandle != SHCLOBJHANDLE_INVALID);
     720            && pObjCtx->uHandle != NIL_SHCLOBJHANDLE);
    679721}
    680722
     
    689731    AssertPtrReturn(pInfo, VERR_INVALID_POINTER);
    690732
    691     pInfo->hObj    = SHCLOBJHANDLE_INVALID;
     733    pInfo->hObj    = NIL_SHCLOBJHANDLE;
    692734    pInfo->enmType = SHCLOBJTYPE_INVALID;
    693735
     
    12601302        return VINF_SUCCESS;
    12611303
     1304    AssertMsgReturn(pTransfer->cRefs == 0, ("Number of references > 0 (%RU32)\n", pTransfer->cRefs), VERR_WRONG_ORDER);
     1305
    12621306    LogFlowFuncEnter();
    12631307
     
    12841328int ShClTransferInit(PSHCLTRANSFER pTransfer, SHCLTRANSFERDIR enmDir, SHCLSOURCE enmSource)
    12851329{
     1330    pTransfer->cRefs = 0;
     1331
    12861332    pTransfer->State.enmDir    = enmDir;
    12871333    pTransfer->State.enmSource = enmSource;
     
    13051351    LogFlowFuncLeaveRC(rc);
    13061352    return rc;
     1353}
     1354
     1355/**
     1356 * Acquires a reference to this transfer.
     1357 *
     1358 * @returns New reference count.
     1359 * @param   pTransfer           Transfer to acquire reference for.
     1360 */
     1361uint32_t ShClTransferAcquire(PSHCLTRANSFER pTransfer)
     1362{
     1363    return ASMAtomicIncU32(&pTransfer->cRefs);
     1364}
     1365
     1366/**
     1367 * Releases a reference to this transfer.
     1368 *
     1369 * @returns New reference count.
     1370 * @param   pTransfer           Transfer to release reference for.
     1371 */
     1372uint32_t ShClTransferRelease(PSHCLTRANSFER pTransfer)
     1373{
     1374    return ASMAtomicDecU32(&pTransfer->cRefs);
    13071375}
    13081376
     
    15921660    AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
    15931661
    1594     if (hList == SHCLLISTHANDLE_INVALID)
     1662    if (hList == NIL_SHCLLISTHANDLE)
    15951663        return VINF_SUCCESS;
    15961664
     
    22412309    AssertPtrReturn(pRoot, VERR_INVALID_PARAMETER);
    22422310
     2311    const char *pcszSrcPathAbs = pRoot->pszPathAbs;
     2312
    22432313    /* Make sure that we only advertise relative source paths, not absolute ones. */
    2244     const char *pcszSrcPath = pRoot->pszPathAbs;
    2245 
    2246     char *pszFileName = RTPathFilename(pcszSrcPath);
     2314    char *pszFileName = RTPathFilename(pcszSrcPathAbs);
    22472315    if (pszFileName)
    22482316    {
    2249         Assert(pszFileName >= pcszSrcPath);
    2250         size_t cchDstBase = pszFileName - pcszSrcPath;
    2251         const char *pszDstPath = &pcszSrcPath[cchDstBase];
    2252 
    2253         LogFlowFunc(("pcszSrcPath=%s, pszDstPath=%s\n", pcszSrcPath, pszDstPath));
    2254 
    2255         rc = ShClTransferListEntryInit(pEntry);
     2317        Assert(pszFileName >= pcszSrcPathAbs);
     2318        size_t cchDstBase = pszFileName - pcszSrcPathAbs;
     2319        const char *pszDstPath = &pcszSrcPathAbs[cchDstBase];
     2320
     2321        LogFlowFunc(("pcszSrcPathAbs=%s, pszDstPath=%s\n", pcszSrcPathAbs, pszDstPath));
     2322
     2323        rc = ShClTransferListEntryInitEx(pEntry, pszFileName);
    22562324        if (RT_SUCCESS(rc))
    22572325        {
     
    22642332                {
    22652333                    RTFSOBJINFO fsObjInfo;
    2266                     rc = RTPathQueryInfo(pcszSrcPath, &fsObjInfo, RTFSOBJATTRADD_NOTHING);
     2334                    rc = RTPathQueryInfo(pcszSrcPathAbs, &fsObjInfo, RTFSOBJATTRADD_NOTHING);
    22672335                    if (RT_SUCCESS(rc))
    22682336                    {
     
    23352403        {
    23362404            pRootList->Hdr.cRoots = cRoots;
    2337             pRootList->Hdr.fRoots = 0; /** @todo Implement this. */
    23382405
    23392406            *ppRootList = pRootList;
     
    24702537
    24712538/**
     2539 * Sets a single file as a transfer root.
     2540 *
     2541 * @returns VBox status code.
     2542 * @param   pTransfer           Transfer to set transfer list entries for.
     2543 * @param   pszFile             File to use as transfer root.
     2544 *
     2545 * @note    Convenience function, uses ShClTransferRootsSet() internally.
     2546 */
     2547int ShClTransferRootsSetAsFile(PSHCLTRANSFER pTransfer, const char *pszFile)
     2548{
     2549    char *pszRoots = NULL;
     2550
     2551    int rc = RTStrAAppend(&pszRoots, pszFile);
     2552    AssertRCReturn(rc, rc);
     2553    rc = RTStrAAppend(&pszRoots, "\r\n");
     2554    AssertRCReturn(rc, rc);
     2555    rc =  ShClTransferRootsSet(pTransfer, pszRoots, strlen(pszRoots) + 1);
     2556    RTStrFree(pszRoots);
     2557    return rc;
     2558}
     2559
     2560/**
    24722561 * Returns the clipboard transfer's ID.
    24732562 *
     
    24942583    LogFlowFunc(("[Transfer %RU32] enmDir=%RU32\n", pTransfer->State.uID, pTransfer->State.enmDir));
    24952584    return pTransfer->State.enmDir;
     2585}
     2586
     2587/**
     2588 * Returns the absolute root path of a transfer.
     2589 *
     2590 * @returns VBox status code.
     2591 * @param   pTransfer           Clipboard transfer to return absolute root path for.
     2592 * @param   pszPath             Where to store the returned path.
     2593 * @param   cbPath              Size (in bytes) of  \a pszPath.
     2594 */
     2595int ShClTransferGetRootPathAbs(PSHCLTRANSFER pTransfer, char *pszPath, size_t cbPath)
     2596{
     2597    AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
     2598
     2599    return RTStrCopy(pszPath, cbPath, pTransfer->pszPathRootAbs);
    24962600}
    24972601
     
    26742778        RT_ZERO(pTransferCtx->bmTransferIds);
    26752779
    2676 #ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
    2677         ShClTransferHttpServerInit(&pTransferCtx->HttpServer);
    2678 #endif
    26792780        ShClTransferCtxReset(pTransferCtx);
    26802781    }
     
    28292930 *          is reached.
    28302931 * @param   pTransferCtx        Transfer context to register transfer to.
    2831  * @param   pTransfer           Transfer to register.
     2932 * @param   pTransfer           Transfer to register. The context takes ownership of the transfer on success.
    28322933 * @param   pidTransfer         Where to return the transfer ID on success. Optional.
    28332934 */
     
    29393040
    29403041/**
    2941  * Unregisters a transfer from an Transfer context.
     3042 * Unregisters a transfer from an transfer context.
    29423043 *
    29433044 * @retval  VINF_SUCCESS on success.
  • trunk/src/VBox/GuestHost/SharedClipboard/testcase/Makefile.kmk

    r98413 r99937  
    6969  endif
    7070
     71 endif
    7172
     73 ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
     74  ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
     75   PROGRAMS += tstClipboardHttpServer
     76   tstClipboardHttpServer_TEMPLATE = VBoxR3TstExe
     77   tstClipboardHttpServer_SOURCES  = \
     78        tstClipboardHttpServer.cpp \
     79        ../clipboard-common.cpp \
     80        ../clipboard-transfers.cpp \
     81        ../clipboard-transfers-http.cpp
     82   tstClipboardHttpServer_DEFS    += VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
     83  endif
    7284 endif
     85
    7386endif
    7487
  • trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-transfers.cpp

    r98103 r99937  
    18681868            RT_ZERO(creationCtx);
    18691869
    1870             if (enmDir == SHCLTRANSFERDIR_FROM_REMOTE)
     1870            if (enmDir == SHCLTRANSFERDIR_FROM_REMOTE) /* Guest -> Host. */
    18711871            {
    18721872                creationCtx.Interface.pfnRootsGet      = shClSvcTransferIfaceGetRoots;
     
    18811881                creationCtx.Interface.pfnObjRead       = shClSvcTransferIfaceObjRead;
    18821882            }
    1883             else if (enmDir == SHCLTRANSFERDIR_TO_REMOTE)
     1883            else if (enmDir == SHCLTRANSFERDIR_TO_REMOTE) /* Host -> Guest. */
    18841884            {
    18851885                creationCtx.Interface.pfnListHdrWrite   = shClSvcTransferIfaceListHdrWrite;
  • trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-x11.cpp

    r98103 r99937  
    505505    RT_NOREF(pBackend);
    506506#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
    507     return ShClHttpTransferRegister(&pClient->State.pCtx->X11.HttpCtx, pTransfer);
     507    return ShClHttpTransferRegisterAndMaybeStart(&pClient->State.pCtx->X11.HttpCtx, pTransfer);
    508508#else
    509509    RT_NOREF(pClient, pTransfer);
     
    516516    RT_NOREF(pBackend);
    517517#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
    518     return ShClHttpTransferUnregister(&pClient->State.pCtx->X11.HttpCtx, pTransfer);
     518    return ShClHttpTransferUnregisterAndMaybeStop(&pClient->State.pCtx->X11.HttpCtx, pTransfer);
    519519#else
    520520    RT_NOREF(pClient, pTransfer);
  • trunk/src/VBox/Runtime/r3/http-server.cpp

    r98103 r99937  
    794794            while (cbToRead)
    795795            {
    796                 RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnRead, pvHandle, pvBuf, RT_MIN(cbBuf, cbToRead), &cbRead);
     796                RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnRead, pReq, pvHandle, pvBuf, RT_MIN(cbBuf, cbToRead), &cbRead);
    797797                if (RT_FAILURE(rc))
    798798                    break;
     
    815815        int rc2 = rc; /* Save rc. */
    816816
    817         RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnClose, pvHandle);
     817        RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnClose, pReq, pvHandle);
    818818
    819819        if (RT_FAILURE(rc2)) /* Restore original rc on failure. */
     
    963963            while (cbToRead)
    964964            {
    965                 RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnRead, pvHandle, pvBuf, RT_MIN(cbBuf, cbToRead), &cbRead);
     965                RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnRead, pReq, pvHandle, pvBuf, RT_MIN(cbBuf, cbToRead), &cbRead);
    966966                if (RT_FAILURE(rc))
    967967                    break;
     
    985985        int rc2 = rc; /* Save rc. */
    986986
    987         RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnClose, pvHandle);
     987        RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnClose, pReq, pvHandle);
    988988
    989989        if (RT_FAILURE(rc2)) /* Restore original rc on failure. */
     
    10211021                       || RTFS_IS_FILE(objInfo.Attr.fMode);
    10221022
    1023             /* No symlinks and other stuff not allowed. */
     1023            /* No symlinks and other stuff allowed. */
    10241024        }
    10251025        else
     
    11421142        /*
    11431143         * Parse HTTP version to use.
    1144          * We're picky heree: Only HTTP 1.1 is supported by now.
     1144         * We're picky here: Only HTTP 1.1 is supported by now.
    11451145         */
    11461146        const char *pszVer = ppapszFirstLine[2];
     
    12091209        LogFlowFunc(("Request %s %s\n", RTHttpMethodToStr(pReq->enmMethod), pReq->pszUrl));
    12101210
     1211        RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnRequestBegin, pReq);
     1212
    12111213        unsigned i = 0;
    12121214        for (; i < RT_ELEMENTS(g_aMethodMap); i++)
     
    12251227        }
    12261228
     1229        RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnRequestEnd, pReq);
     1230
    12271231        if (i == RT_ELEMENTS(g_aMethodMap))
    12281232            enmSts = RTHTTPSTATUS_NOTIMPLEMENTED;
     
    12331237        enmSts = RTHTTPSTATUS_BADREQUEST;
    12341238
    1235     if (enmSts != RTHTTPSTATUS_INTERNAL_NOT_SET)
    1236     {
    1237         int rc2 = rtHttpServerSendResponseSimple(pClient, enmSts);
    1238         if (RT_SUCCESS(rc))
    1239             rc = rc2;
    1240     }
     1239    /* Make sure to return at least *something* to the client, to prevent hangs. */
     1240    if (enmSts == RTHTTPSTATUS_INTERNAL_NOT_SET)
     1241        enmSts = RTHTTPSTATUS_INTERNALSERVERERROR;
     1242
     1243    int rc2 = rtHttpServerSendResponseSimple(pClient, enmSts);
     1244    if (RT_SUCCESS(rc))
     1245        rc = rc2;
    12411246
    12421247    LogFlowFuncLeaveRC(rc);
     
    12691274        if (RT_FAILURE(rc))
    12701275        {
    1271             LogFlowFunc(("RTTcpSelectOne=%Rrc (cWaitMs=%RU64)\n", rc, cWaitMs));
     1276            Log2Func(("RTTcpSelectOne=%Rrc (cWaitMs=%RU64)\n", rc, cWaitMs));
    12721277            if (rc == VERR_TIMEOUT)
    12731278            {
     
    12771282                        tsLastReadMs = RTTimeMilliTS();
    12781283                    const uint64_t tsDeltaMs = pClient->State.msKeepAlive - (RTTimeMilliTS() - tsLastReadMs);
    1279                     LogFlowFunc(("tsLastReadMs=%RU64, tsDeltaMs=%RU64\n", tsLastReadMs, tsDeltaMs));
    1280                     Log3Func(("Keep alive active (%RU32ms): %RU64ms remaining\n", pClient->State.msKeepAlive, tsDeltaMs));
     1284                    Log2Func(("tsLastReadMs=%RU64, tsDeltaMs=%RU64\n", tsLastReadMs, tsDeltaMs));
     1285                    Log2Func(("Keep alive active (%RU32ms): %RU64ms remaining\n", pClient->State.msKeepAlive, tsDeltaMs));
    12811286                    if (   tsDeltaMs > cWaitMs
    12821287                        && tsDeltaMs < pClient->State.msKeepAlive)
     
    13321337        } while (cbToRead);
    13331338
     1339        Log2Func(("Read client request done (%zu bytes) -> rc=%Rrc\n", cbReadTotal, rc));
     1340
    13341341        if (   RT_SUCCESS(rc)
    13351342            && cbReadTotal)
    13361343        {
    1337             LogFlowFunc(("Received client request (%zu bytes)\n", cbReadTotal));
    1338 
    13391344            rtHttpServerLogProto(pClient, false /* fWrite */, szReq);
    13401345
  • trunk/src/VBox/Runtime/tools/RTHttpServer.cpp

    r98103 r99937  
    521521}
    522522
    523 static DECLCALLBACK(int) onRead(PRTHTTPCALLBACKDATA pData, void *pvHandle, void *pvBuf, size_t cbBuf, size_t *pcbRead)
    524 {
     523static DECLCALLBACK(int) onRead(PRTHTTPCALLBACKDATA pData,
     524                                PRTHTTPSERVERREQ pReq, void *pvHandle, void *pvBuf, size_t cbBuf, size_t *pcbRead)
     525{
     526    RT_NOREF(pReq);
     527
    525528    PHTTPSERVERDATA pThis = (PHTTPSERVERDATA)pData->pvUser;
    526529    Assert(pData->cbUser == sizeof(HTTPSERVERDATA));
     
    554557}
    555558
    556 static DECLCALLBACK(int) onClose(PRTHTTPCALLBACKDATA pData, void *pvHandle)
    557 {
     559static DECLCALLBACK(int) onClose(PRTHTTPCALLBACKDATA pData, PRTHTTPSERVERREQ pReq, void *pvHandle)
     560{
     561    RT_NOREF(pReq);
     562
    558563    PHTTPSERVERDATA pThis = (PHTTPSERVERDATA)pData->pvUser;
    559564    Assert(pData->cbUser == sizeof(HTTPSERVERDATA));
Note: See TracChangeset for help on using the changeset viewer.

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