VirtualBox

Changeset 79270 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Jun 21, 2019 10:28:42 AM (6 years ago)
Author:
vboxsync
Message:

Shared Clipboard/URI: Moved common URI code to an own file.

Location:
trunk/src/VBox
Files:
1 added
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Additions/WINNT/VBoxTray/Makefile.kmk

    r78974 r79270  
    6666        $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/ClipboardURIList.cpp \
    6767        $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/ClipboardURIObject.cpp \
    68         $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/clipboard-common.cpp
     68        $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/clipboard-common.cpp \
     69        $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/clipboard-uri.cpp
    6970 endif
    7071endif
  • trunk/src/VBox/GuestHost/SharedClipboard/clipboard-common.cpp

    r79267 r79270  
    2727#include <VBox/log.h>
    2828#include <VBox/GuestHost/clipboard-helper.h>
    29 #ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
    30 # include <VBox/GuestHost/SharedClipboard-uri.h>
    31 #endif
    32 
    33 
    34 #ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
    35 static int sharedClipboardURITransferThreadCreate(PSHAREDCLIPBOARDURITRANSFER pTransfer);
    36 static int sharedClipboardURITransferThreadDestroy(PSHAREDCLIPBOARDURITRANSFER pTransfer, RTMSINTERVAL uTimeoutMs);
    37 static int sharedClipboardURITransferReadThread(RTTHREAD hThread, void *pvUser);
    38 static int sharedClipboardURITransferWriteThread(RTTHREAD hThread, void *pvUser);
    39 static PSHAREDCLIPBOARDURITRANSFER sharedClipboardURICtxGetTransferInternal(PSHAREDCLIPBOARDURICTX pURI, uint32_t uIdx);
    40 
    41 static void sharedClipboardURITransferMetaDataDestroyInternal(PSHAREDCLIPBOARDURITRANSFER pTransfer);
    42 #endif
    4329
    4430
     
    423409#endif /* LOG_ENABLED */
    424410
    425 #ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
    426 int SharedClipboardURIDataHdrAlloc(PVBOXCLIPBOARDDATAHDR *ppDataHdr)
    427 {
    428     int rc;
    429 
    430     PVBOXCLIPBOARDDATAHDR pDataHdr = (PVBOXCLIPBOARDDATAHDR)RTMemAllocZ(sizeof(VBOXCLIPBOARDDATAHDR));
    431     if (pDataHdr)
    432     {
    433         PSHAREDCLIPBOARDMETADATAFMTDATA pMetaDataFmt
    434             = (PSHAREDCLIPBOARDMETADATAFMTDATA)RTMemAllocZ(sizeof(SHAREDCLIPBOARDMETADATAFMTDATA));
    435         if (pMetaDataFmt)
    436         {
    437             char *pszFmt = NULL;
    438             rc = RTStrAAppend(&pszFmt, "VBoxShClURIList");
    439             if (RT_SUCCESS(rc))
    440             {
    441                 pMetaDataFmt->uVer  = 1;
    442                 pMetaDataFmt->pvFmt = pszFmt;
    443                 pMetaDataFmt->cbFmt = (uint32_t)strlen(pszFmt) + 1 /* Include terminating zero */;
    444 
    445                 pDataHdr->pvMetaFmt = pMetaDataFmt;
    446                 pDataHdr->cbMetaFmt = sizeof(SHAREDCLIPBOARDMETADATAFMTDATA) + pMetaDataFmt->cbFmt;
    447 
    448                 *ppDataHdr = pDataHdr;
    449             }
    450         }
    451         else
    452             rc = VERR_NO_MEMORY;
    453 
    454         if (RT_FAILURE(rc))
    455         {
    456             RTMemFree(pDataHdr);
    457             pDataHdr = NULL;
    458         }
    459     }
    460     else
    461         rc = VERR_NO_MEMORY;
    462 
    463     LogFlowFuncLeaveRC(rc);
    464     return rc;
    465 }
    466 
    467 /**
    468  * Frees a VBOXCLIPBOARDDATAHDR structure.
    469  *
    470  * @param   pDataChunk          VBOXCLIPBOARDDATAHDR structure to free.
    471  */
    472 void SharedClipboardURIDataHdrFree(PVBOXCLIPBOARDDATAHDR pDataHdr)
    473 {
    474     if (!pDataHdr)
    475         return;
    476 
    477     LogFlowFuncEnter();
    478 
    479     SharedClipboardURIDataHdrDestroy(pDataHdr);
    480 
    481     RTMemFree(pDataHdr);
    482     pDataHdr = NULL;
    483 }
    484 
    485 /**
    486  * Duplicates (allocates) a VBOXCLIPBOARDDATAHDR structure.
    487  *
    488  * @returns Duplicated VBOXCLIPBOARDDATAHDR structure on success.
    489  * @param   pDataHdr            VBOXCLIPBOARDDATAHDR to duplicate.
    490  */
    491 PVBOXCLIPBOARDDATAHDR SharedClipboardURIDataHdrDup(PVBOXCLIPBOARDDATAHDR pDataHdr)
    492 {
    493     AssertPtrReturn(pDataHdr, NULL);
    494 
    495     PVBOXCLIPBOARDDATAHDR pDataHdrDup = (PVBOXCLIPBOARDDATAHDR)RTMemAlloc(sizeof(VBOXCLIPBOARDDATAHDR));
    496     if (pDataHdrDup)
    497     {
    498         *pDataHdrDup = *pDataHdr;
    499 
    500         if (pDataHdr->pvMetaFmt)
    501         {
    502             pDataHdrDup->pvMetaFmt = RTMemDup(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt);
    503             pDataHdrDup->cbMetaFmt = pDataHdr->cbMetaFmt;
    504         }
    505 
    506         if (pDataHdr->pvChecksum)
    507         {
    508             pDataHdrDup->pvChecksum = RTMemDup(pDataHdr->pvChecksum, pDataHdr->cbChecksum);
    509             pDataHdrDup->cbChecksum = pDataHdr->cbChecksum;
    510         }
    511     }
    512 
    513     return pDataHdrDup;
    514 }
    515 
    516 /**
    517  * Returns the size (in bytes) of the announced meta data.
    518  *
    519  * @returns Announced meta data size in bytes.
    520  * @param   pDataHdr            Data header struct to get announced meta data size for.
    521  */
    522 uint32_t SharedClipboardURIDataHdrGetMetaDataSize(PVBOXCLIPBOARDDATAHDR pDataHdr)
    523 {
    524     AssertPtrReturn(pDataHdr, 0);
    525 
    526     return pDataHdr->cbMeta;
    527 }
    528 
    529 /**
    530  * Initializes an URI data header struct.
    531  *
    532  * @returns VBox status code.
    533  * @param   pDataHdr            Data header struct to initialize.
    534  */
    535 int SharedClipboardURIDataHdrInit(PVBOXCLIPBOARDDATAHDR pDataHdr)
    536 {
    537     AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
    538 
    539     SharedClipboardURIDataHdrReset(pDataHdr);
    540 
    541     return VINF_SUCCESS;
    542 }
    543 
    544 /**
    545  * Destroys an URI data header struct.
    546  *
    547  * @param   pDataHdr            Data header struct to destroy.
    548  */
    549 void SharedClipboardURIDataHdrDestroy(PVBOXCLIPBOARDDATAHDR pDataHdr)
    550 {
    551     if (!pDataHdr)
    552         return;
    553 
    554     LogFlowFuncEnter();
    555 
    556     if (pDataHdr->pvMetaFmt)
    557     {
    558         Assert(pDataHdr->cbMetaFmt);
    559 
    560         RTMemFree(pDataHdr->pvMetaFmt);
    561         pDataHdr->pvMetaFmt = NULL;
    562         pDataHdr->cbMetaFmt = 0;
    563     }
    564 
    565     if (pDataHdr->pvChecksum)
    566     {
    567         Assert(pDataHdr->cbChecksum);
    568 
    569         RTMemFree(pDataHdr->pvChecksum);
    570         pDataHdr->pvChecksum = NULL;
    571         pDataHdr->cbChecksum = 0;
    572     }
    573 }
    574 
    575 /**
    576  * Resets a VBOXCLIPBOARDDATAHDR structture.
    577  *
    578  * @returns VBox status code.
    579  * @param   pDataHdr            VBOXCLIPBOARDDATAHDR structture to reset.
    580  */
    581 void SharedClipboardURIDataHdrReset(PVBOXCLIPBOARDDATAHDR pDataHdr)
    582 {
    583     AssertPtrReturnVoid(pDataHdr);
    584 
    585     LogFlowFuncEnter();
    586 
    587     RT_BZERO(pDataHdr, sizeof(VBOXCLIPBOARDDATAHDR));
    588 }
    589 
    590 /**
    591  * Returns whether a given clipboard data header is valid or not.
    592  *
    593  * @returns \c true if valid, \c false if not.
    594  * @param   pDataHdr            Clipboard data header to validate.
    595  */
    596 bool SharedClipboardURIDataHdrIsValid(PVBOXCLIPBOARDDATAHDR pDataHdr)
    597 {
    598     RT_NOREF(pDataHdr);
    599     return true; /** @todo Implement this. */
    600 }
    601 
    602 /**
    603  * Creates (allocates) and initializes a VBOXCLIPBOARDDATACHUNK structure.
    604  *
    605  * @param   ppDirData           Where to return the created VBOXCLIPBOARDDATACHUNK structure on success.
    606  */
    607 int SharedClipboardURIDataChunkAlloc(PVBOXCLIPBOARDDATACHUNK *ppDataChunk)
    608 {
    609     PVBOXCLIPBOARDDATACHUNK pDataChunk = (PVBOXCLIPBOARDDATACHUNK)RTMemAlloc(sizeof(VBOXCLIPBOARDDATACHUNK));
    610     if (!pDataChunk)
    611         return VERR_NO_MEMORY;
    612 
    613     int rc = SharedClipboardURIDataChunkInit(pDataChunk);
    614     if (RT_SUCCESS(rc))
    615         *ppDataChunk = pDataChunk;
    616 
    617     return rc;
    618 }
    619 
    620 /**
    621  * Frees a VBOXCLIPBOARDDATACHUNK structure.
    622  *
    623  * @param   pDataChunk         VBOXCLIPBOARDDATACHUNK structure to free.
    624  */
    625 void SharedClipboardURIDataChunkFree(PVBOXCLIPBOARDDATACHUNK pDataChunk)
    626 {
    627     if (!pDataChunk)
    628         return;
    629 
    630     SharedClipboardURIDataChunkDestroy(pDataChunk);
    631     RTMemFree(pDataChunk);
    632 }
    633 
    634 /**
    635  * Initializes a VBOXCLIPBOARDDATACHUNK structure.
    636  *
    637  * @param   pDataChunk          VBOXCLIPBOARDDATACHUNK structure to initialize.
    638  */
    639 int SharedClipboardURIDataChunkInit(PVBOXCLIPBOARDDATACHUNK pDataChunk)
    640 {
    641     RT_BZERO(pDataChunk, sizeof(VBOXCLIPBOARDDATACHUNK));
    642 
    643     return VINF_SUCCESS;
    644 }
    645 
    646 /**
    647  * Initializes a VBOXCLIPBOARDDATACHUNK structure.
    648  *
    649  * @param   pDataChunk          VBOXCLIPBOARDDATACHUNK structure to destroy.
    650  */
    651 void SharedClipboardURIDataChunkDestroy(PVBOXCLIPBOARDDATACHUNK pDataChunk)
    652 {
    653     if (pDataChunk->pvData)
    654     {
    655         RTMemFree(pDataChunk->pvData);
    656         pDataChunk->pvData = NULL;
    657         pDataChunk->cbData = 0;
    658     }
    659 
    660     if (pDataChunk->pvChecksum)
    661     {
    662         RTMemFree(pDataChunk->pvChecksum);
    663         pDataChunk->pvChecksum = NULL;
    664         pDataChunk->cbChecksum = 0;
    665     }
    666 }
    667 
    668 /**
    669  * Returns whether a given clipboard data chunk is valid or not.
    670  *
    671  * @returns \c true if valid, \c false if not.
    672  * @param   pDataChunk          Clipboard data chunk to validate.
    673  */
    674 bool SharedClipboardURIDataChunkIsValid(PVBOXCLIPBOARDDATACHUNK pDataChunk)
    675 {
    676     RT_NOREF(pDataChunk);
    677 
    678     /** @todo Verify checksum. */
    679 
    680     return true; /** @todo Implement this. */
    681 }
    682 
    683 /**
    684  * Creates (allocates) and initializes a VBOXCLIPBOARDDIRDATA structure.
    685  *
    686  * @param   ppDirData           Where to return the created VBOXCLIPBOARDDIRDATA structure on success.
    687  */
    688 int SharedClipboardURIDirDataAlloc(PVBOXCLIPBOARDDIRDATA *ppDirData)
    689 {
    690     PVBOXCLIPBOARDDIRDATA pDirData = (PVBOXCLIPBOARDDIRDATA)RTMemAlloc(sizeof(VBOXCLIPBOARDDIRDATA));
    691     if (!pDirData)
    692         return VERR_NO_MEMORY;
    693 
    694     int rc = SharedClipboardURIDirDataInit(pDirData);
    695     if (RT_SUCCESS(rc))
    696         *ppDirData = pDirData;
    697 
    698     return rc;
    699 }
    700 
    701 /**
    702  * Frees a VBOXCLIPBOARDDIRDATA structure.
    703  *
    704  * @param   pDirData           Where to return the created VBOXCLIPBOARDDIRDATA structure on success.
    705  */
    706 void SharedClipboardURIDirDataFree(PVBOXCLIPBOARDDIRDATA pDirData)
    707 {
    708     if (!pDirData)
    709         return;
    710 
    711     SharedClipboardURIDirDataDestroy(pDirData);
    712     RTMemFree(pDirData);
    713 }
    714 
    715 /**
    716  * Initializes a VBOXCLIPBOARDDIRDATA structure.
    717  *
    718  * @param   pDirData            VBOXCLIPBOARDDIRDATA structure to initialize.
    719  */
    720 int SharedClipboardURIDirDataInit(PVBOXCLIPBOARDDIRDATA pDirData)
    721 {
    722     RT_BZERO(pDirData, sizeof(VBOXCLIPBOARDDIRDATA));
    723 
    724     return VINF_SUCCESS;
    725 }
    726 
    727 /**
    728  * Destroys a VBOXCLIPBOARDDIRDATA structure.
    729  *
    730  * @param   pDirData            VBOXCLIPBOARDDIRDATA structure to destroy.
    731  */
    732 void SharedClipboardURIDirDataDestroy(PVBOXCLIPBOARDDIRDATA pDirData)
    733 {
    734     if (!pDirData)
    735         return;
    736 
    737     if (pDirData->pszPath)
    738     {
    739         Assert(pDirData->cbPath);
    740         RTStrFree(pDirData->pszPath);
    741         pDirData->pszPath = NULL;
    742     }
    743 }
    744 
    745 /**
    746  * Duplicates (allocates) a VBOXCLIPBOARDDIRDATA structure.
    747  *
    748  * @returns Duplicated VBOXCLIPBOARDDIRDATA structure on success.
    749  * @param   pDirData            VBOXCLIPBOARDDIRDATA to duplicate.
    750  */
    751 PVBOXCLIPBOARDDIRDATA SharedClipboardURIDirDataDup(PVBOXCLIPBOARDDIRDATA pDirData)
    752 {
    753     AssertPtrReturn(pDirData, NULL);
    754 
    755     PVBOXCLIPBOARDDIRDATA pDirDataDup = (PVBOXCLIPBOARDDIRDATA)RTMemAllocZ(sizeof(VBOXCLIPBOARDDIRDATA));
    756     if (pDirDataDup)
    757     {
    758         *pDirDataDup = *pDirData;
    759 
    760         if (pDirData->pszPath)
    761         {
    762             pDirDataDup->pszPath = RTStrDup(pDirData->pszPath);
    763             if (pDirDataDup->pszPath)
    764                 pDirDataDup->cbPath = pDirData->cbPath;
    765         }
    766     }
    767 
    768     return pDirDataDup;
    769 }
    770 
    771 /**
    772  * Returns whether given clipboard directory data is valid or not.
    773  *
    774  * @returns \c true if valid, \c false if not.
    775  * @param   pDirData            Clipboard directory data to validate.
    776  */
    777 bool SharedClipboardURIDirDataIsValid(PVBOXCLIPBOARDDIRDATA pDirData)
    778 {
    779     if (   !pDirData->cbPath
    780         || pDirData->cbPath > RTPATH_MAX)
    781         return false;
    782 
    783     if (!RTStrIsValidEncoding(pDirData->pszPath))
    784         return false;
    785 
    786     return true;
    787 }
    788 
    789 /**
    790  * Initializes a VBOXCLIPBOARDFILEHDR structure.
    791  *
    792  * @param   pDirData            VBOXCLIPBOARDFILEHDR structure to initialize.
    793  */
    794 int SharedClipboardURIFileHdrInit(PVBOXCLIPBOARDFILEHDR pFileHdr)
    795 {
    796     RT_BZERO(pFileHdr, sizeof(VBOXCLIPBOARDFILEHDR));
    797 
    798     return VINF_SUCCESS;
    799 }
    800 
    801 /**
    802  * Destroys a VBOXCLIPBOARDFILEHDR structure.
    803  *
    804  * @param   pFileHdr            VBOXCLIPBOARDFILEHDR structure to destroy.
    805  */
    806 void SharedClipboardURIFileHdrDestroy(PVBOXCLIPBOARDFILEHDR pFileHdr)
    807 {
    808     if (!pFileHdr)
    809         return;
    810 
    811     if (pFileHdr->pszFilePath)
    812     {
    813         Assert(pFileHdr->pszFilePath);
    814         RTStrFree(pFileHdr->pszFilePath);
    815         pFileHdr->pszFilePath = NULL;
    816     }
    817 }
    818 
    819 /**
    820  * Duplicates (allocates) a VBOXCLIPBOARDFILEHDR structure.
    821  *
    822  * @returns Duplicated VBOXCLIPBOARDFILEHDR structure on success.
    823  * @param   pFileHdr            VBOXCLIPBOARDFILEHDR to duplicate.
    824  */
    825 PVBOXCLIPBOARDFILEHDR SharedClipboardURIFileHdrDup(PVBOXCLIPBOARDFILEHDR pFileHdr)
    826 {
    827     AssertPtrReturn(pFileHdr, NULL);
    828 
    829     PVBOXCLIPBOARDFILEHDR pFileHdrDup = (PVBOXCLIPBOARDFILEHDR)RTMemAllocZ(sizeof(VBOXCLIPBOARDFILEHDR));
    830     if (pFileHdrDup)
    831     {
    832         *pFileHdrDup = *pFileHdr;
    833 
    834         if (pFileHdr->pszFilePath)
    835         {
    836             pFileHdrDup->pszFilePath = RTStrDup(pFileHdr->pszFilePath);
    837             if (pFileHdrDup->pszFilePath)
    838                 pFileHdrDup->cbFilePath = pFileHdrDup->cbFilePath;
    839         }
    840     }
    841 
    842     return pFileHdrDup;
    843 }
    844 
    845 /**
    846  * Returns whether a given clipboard file header is valid or not.
    847  *
    848  * @returns \c true if valid, \c false if not.
    849  * @param   pFileHdr            Clipboard file header to validate.
    850  * @param   pDataHdr            Data header to use for validation.
    851  */
    852 bool SharedClipboardURIFileHdrIsValid(PVBOXCLIPBOARDFILEHDR pFileHdr, PVBOXCLIPBOARDDATAHDR pDataHdr)
    853 {
    854     if (   !pFileHdr->cbFilePath
    855         || pFileHdr->cbFilePath > RTPATH_MAX)
    856         return false;
    857 
    858     if (!RTStrIsValidEncoding(pFileHdr->pszFilePath))
    859         return false;
    860 
    861     if (pFileHdr->cbSize > pDataHdr->cbTotal)
    862         return false;
    863 
    864     return true;
    865 }
    866 
    867 /**
    868  * Destroys a VBOXCLIPBOARDFILEDATA structure.
    869  *
    870  * @param   pFileData           VBOXCLIPBOARDFILEDATA structure to destroy.
    871  */
    872 void SharedClipboardURIFileDataDestroy(PVBOXCLIPBOARDFILEDATA pFileData)
    873 {
    874     if (!pFileData)
    875         return;
    876 
    877     if (pFileData->pvData)
    878     {
    879         Assert(pFileData->cbData);
    880         RTMemFree(pFileData->pvData);
    881         pFileData->pvData = NULL;
    882     }
    883 }
    884 
    885 /**
    886  * Duplicates (allocates) a VBOXCLIPBOARDFILEDATA structure.
    887  *
    888  * @returns Duplicated VBOXCLIPBOARDFILEDATA structure on success.
    889  * @param   pFileData           VBOXCLIPBOARDFILEDATA to duplicate.
    890  */
    891 PVBOXCLIPBOARDFILEDATA SharedClipboardURIFileDataDup(PVBOXCLIPBOARDFILEDATA pFileData)
    892 {
    893     AssertPtrReturn(pFileData, NULL);
    894 
    895     PVBOXCLIPBOARDFILEDATA pFileDataDup = (PVBOXCLIPBOARDFILEDATA)RTMemAllocZ(sizeof(VBOXCLIPBOARDFILEDATA));
    896     if (pFileDataDup)
    897     {
    898         *pFileDataDup = *pFileData;
    899 
    900         if (pFileData->pvData)
    901         {
    902             pFileDataDup->pvData = RTMemDup(pFileData->pvData, pFileData->cbData);
    903             if (pFileDataDup->pvData)
    904                 pFileDataDup->cbData = pFileDataDup->cbData;
    905         }
    906 
    907         if (pFileData->pvChecksum)
    908         {
    909             pFileDataDup->pvChecksum = RTMemDup(pFileData->pvChecksum, pFileData->cbChecksum);
    910             if (pFileDataDup->pvChecksum)
    911                 pFileDataDup->cbChecksum = pFileData->cbChecksum;
    912         }
    913     }
    914 
    915     return pFileDataDup;
    916 }
    917 
    918 /**
    919  * Returns whether given clipboard file data is valid or not.
    920  *
    921  * @returns \c true if valid, \c false if not.
    922  * @param   pFileData           Clipboard file data to validate.
    923  * @param   pDataHdr            Data header to use for validation.
    924  */
    925 bool SharedClipboardURIFileDataIsValid(PVBOXCLIPBOARDFILEDATA pFileData, PVBOXCLIPBOARDDATAHDR pDataHdr)
    926 {
    927     RT_NOREF(pFileData, pDataHdr);
    928     return true;
    929 }
    930 
    931 /**
    932  * Initializes an URI object context.
    933  *
    934  * @returns VBox status code.
    935  * @param   pObjCtx             URI object context to initialize.
    936  */
    937 int SharedClipboardURIObjCtxInit(PSHAREDCLIPBOARDCLIENTURIOBJCTX pObjCtx)
    938 {
    939     AssertPtrReturn(pObjCtx, VERR_INVALID_POINTER);
    940 
    941     LogFlowFuncEnter();
    942 
    943     pObjCtx->pObj = NULL;
    944 
    945     return VINF_SUCCESS;
    946 }
    947 
    948 /**
    949  * Destroys an URI object context.
    950  *
    951  * @param   pObjCtx             URI object context to destroy.
    952  */
    953 void SharedClipboardURIObjCtxDestroy(PSHAREDCLIPBOARDCLIENTURIOBJCTX pObjCtx)
    954 {
    955     AssertPtrReturnVoid(pObjCtx);
    956 
    957     LogFlowFuncEnter();
    958 
    959     if (pObjCtx->pObj)
    960     {
    961         pObjCtx->pObj->Close();
    962         /* Note: Do *not* delete pObj here -- the associated URI list will do this. */
    963     }
    964 
    965     pObjCtx->pObj = NULL;
    966 }
    967 
    968 /**
    969  * Returns the URI object context's URI object.
    970  *
    971  * @returns Pointer to the URI object context's URI object.
    972  * @param   pObjCtx             URI object context to return the URI object for.
    973  */
    974 SharedClipboardURIObject *SharedClipboardURIObjCtxGetObj(PSHAREDCLIPBOARDCLIENTURIOBJCTX pObjCtx)
    975 {
    976     AssertPtrReturn(pObjCtx, NULL);
    977     return pObjCtx->pObj;
    978 }
    979 
    980 /**
    981  * Returns if an URI object context is valid or not.
    982  *
    983  * @returns \c true if valid, \c false if not.
    984  * @param   pObjCtx             URI object context to check.
    985  */
    986 bool SharedClipboardURIObjCtxIsValid(PSHAREDCLIPBOARDCLIENTURIOBJCTX pObjCtx)
    987 {
    988     return (   pObjCtx
    989             && pObjCtx->pObj
    990             && pObjCtx->pObj->IsComplete() == false
    991             && pObjCtx->pObj->IsOpen());
    992 }
    993 
    994 /**
    995  * Initializes an URI clipboard transfer struct.
    996  *
    997  * @returns VBox status code.
    998  * @param   enmDir              Transfer direction.
    999  * @param   ppTransfer          Where to return the created URI transfer struct.
    1000  *                              Must be destroyed by SharedClipboardURITransferDestroy().
    1001  */
    1002 int SharedClipboardURITransferCreate(SHAREDCLIPBOARDURITRANSFERDIR enmDir, PSHAREDCLIPBOARDURITRANSFER *ppTransfer)
    1003 {
    1004     AssertPtrReturn(ppTransfer, VERR_INVALID_POINTER);
    1005 
    1006     LogFlowFuncEnter();
    1007 
    1008     PSHAREDCLIPBOARDURITRANSFER pTransfer = (PSHAREDCLIPBOARDURITRANSFER)RTMemAlloc(sizeof(SHAREDCLIPBOARDURITRANSFER));
    1009     if (!pTransfer)
    1010         return VERR_NO_MEMORY;
    1011 
    1012     int rc = VINF_SUCCESS;
    1013 
    1014     pTransfer->State.enmDir = enmDir;
    1015 
    1016     pTransfer->State.pHeader = NULL;
    1017     pTransfer->State.pMeta   = NULL;
    1018     pTransfer->pArea         = NULL; /* Will be created later if needed. */
    1019 
    1020     pTransfer->Thread.hThread    = NIL_RTTHREAD;
    1021     pTransfer->Thread.fCancelled = false;
    1022     pTransfer->Thread.fStarted   = false;
    1023 
    1024     pTransfer->pvUser = NULL;
    1025     pTransfer->cbUser = 0;
    1026 
    1027     pTransfer->pURIList = new SharedClipboardURIList();
    1028     if (!pTransfer->pURIList)
    1029     {
    1030         RTMemFree(pTransfer);
    1031         return VERR_NO_MEMORY;
    1032     }
    1033 
    1034     rc = SharedClipboardURIObjCtxInit(&pTransfer->ObjCtx);
    1035     if (RT_SUCCESS(rc))
    1036     {
    1037         *ppTransfer = pTransfer;
    1038     }
    1039 
    1040     LogFlowFuncLeaveRC(rc);
    1041     return rc;
    1042 }
    1043 
    1044 /**
    1045  * Destroys an URI clipboard transfer context struct.
    1046  *
    1047  * @returns VBox status code.
    1048  * @param   pURI                URI clipboard transfer to destroy.
    1049  */
    1050 int SharedClipboardURITransferDestroy(PSHAREDCLIPBOARDURITRANSFER pTransfer)
    1051 {
    1052     if (!pTransfer)
    1053         return VINF_SUCCESS;
    1054 
    1055     LogFlowFuncEnter();
    1056 
    1057     int rc = sharedClipboardURITransferThreadDestroy(pTransfer, 30 * 1000 /* Timeout in ms */);
    1058     if (RT_FAILURE(rc))
    1059         return rc;
    1060 
    1061     SharedClipboardURIDataHdrDestroy(pTransfer->State.pHeader);
    1062     SharedClipboardMetaDataDestroy(pTransfer->State.pMeta);
    1063 
    1064     if (pTransfer->pURIList)
    1065     {
    1066         delete pTransfer->pURIList;
    1067         pTransfer->pURIList = NULL;
    1068     }
    1069 
    1070     if (pTransfer->pProvider)
    1071     {
    1072         delete pTransfer->pProvider;
    1073         pTransfer->pProvider = NULL;
    1074     }
    1075 
    1076     SharedClipboardURIObjCtxDestroy(&pTransfer->ObjCtx);
    1077 
    1078     RTMemFree(pTransfer);
    1079     pTransfer = NULL;
    1080 
    1081     LogFlowFuncLeave();
    1082 
    1083     return VINF_SUCCESS;
    1084 }
    1085 
    1086 /**
    1087  * Prepares everything needed for a read / write transfer to begin.
    1088  *
    1089  * @returns VBox status code.
    1090  * @param   pTransfer           URI clipboard transfer to prepare.
    1091  */
    1092 int SharedClipboardURITransferPrepare(PSHAREDCLIPBOARDURITRANSFER pTransfer)
    1093 {
    1094     AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
    1095 
    1096     LogFlowFuncEnter();
    1097 
    1098     AssertPtrReturn(pTransfer->State.pMeta, VERR_WRONG_ORDER);
    1099     AssertPtrReturn(pTransfer->pURIList,    VERR_WRONG_ORDER);
    1100 
    1101     PSHAREDCLIPBOARDMETADATA pMeta = pTransfer->State.pMeta;
    1102     AssertPtrReturn(pMeta, VERR_WRONG_ORDER);
    1103 
    1104     int rc;
    1105 
    1106     LogFlowFunc(("enmDir=%RU32\n", pTransfer->State.enmDir));
    1107 
    1108     if (pTransfer->State.enmDir == SHAREDCLIPBOARDURITRANSFERDIR_READ)
    1109     {
    1110         rc = pTransfer->pURIList->SetFromURIData(SharedClipboardMetaDataRaw(pMeta),
    1111                                                  SharedClipboardMetaDataGetUsed(pMeta),
    1112                                                  SHAREDCLIPBOARDURILIST_FLAGS_NONE);
    1113         /** @todo Verify pvMetaFmt. */
    1114 
    1115         sharedClipboardURITransferMetaDataDestroyInternal(pTransfer);
    1116     }
    1117     else if (pTransfer->State.enmDir == SHAREDCLIPBOARDURITRANSFERDIR_WRITE)
    1118     {
    1119         rc = pTransfer->pURIList->AppendURIPathsFromList((char *)SharedClipboardMetaDataRaw(pMeta),
    1120                                                          SharedClipboardMetaDataGetUsed(pMeta),
    1121                                                          SHAREDCLIPBOARDURILIST_FLAGS_KEEP_OPEN);
    1122         if (RT_SUCCESS(rc))
    1123         {
    1124             PVBOXCLIPBOARDDATAHDR pHeader;
    1125             rc = SharedClipboardURIDataHdrAlloc(&pHeader);
    1126             if (RT_SUCCESS(rc))
    1127             {
    1128                 /* The total size also contains the size of the meta data. */
    1129                 uint64_t cbTotal  = pMeta->cbUsed;
    1130                          cbTotal += pTransfer->pURIList->GetTotalBytes();
    1131 
    1132                 pHeader->cbTotal  = cbTotal;
    1133                 pHeader->cbMeta   = (uint32_t)SharedClipboardMetaDataGetUsed(pMeta);
    1134                 pHeader->cObjects = pTransfer->pURIList->GetTotalCount();
    1135 
    1136                 SharedClipboardURIDataHdrDestroy(pTransfer->State.pHeader);
    1137 
    1138                 if (RT_SUCCESS(rc))
    1139                 {
    1140                     LogFlowFunc(("Writing cbTotal=%RU64, cbMeta=%RU32, cObj=%RU64\n",
    1141                                  pHeader->cbTotal, pHeader->cbMeta, pHeader->cObjects));
    1142 
    1143                     pTransfer->State.pHeader = pHeader;
    1144                 }
    1145                 else
    1146                     SharedClipboardURIDataHdrFree(pHeader);
    1147             }
    1148         }
    1149     }
    1150     else
    1151     {
    1152         rc = VERR_NOT_IMPLEMENTED;
    1153         AssertFailed();
    1154     }
    1155 
    1156     if (RT_SUCCESS(rc))
    1157     {
    1158         /** @todo Add checksum support. */
    1159     }
    1160 
    1161     LogFlowFuncLeaveRC(rc);
    1162     return rc;
    1163 }
    1164 
    1165 /**
    1166  * Creates an URI provider for a given transfer.
    1167  *
    1168  * @returns VBox status code.
    1169  * @param   pTransfer           Transfer to create URI provider for.
    1170  * @param   pProviderCtx        Provider creation context to use for provider creation.
    1171  */
    1172 int SharedClipboardURITransferProviderCreate(PSHAREDCLIPBOARDURITRANSFER pTransfer,
    1173                                              PSHAREDCLIPBOARDPROVIDERCREATIONCTX pProviderCtx)
    1174 {
    1175     AssertPtrReturn(pTransfer,    VERR_INVALID_POINTER);
    1176     AssertPtrReturn(pProviderCtx, VERR_INVALID_POINTER);
    1177 
    1178     LogFlowFuncEnter();
    1179 
    1180     int rc;
    1181 
    1182     pTransfer->pProvider = SharedClipboardProvider::Create(pProviderCtx);
    1183     if (pTransfer->pProvider)
    1184     {
    1185         rc = VINF_SUCCESS;
    1186     }
    1187     else
    1188         rc = VERR_NO_MEMORY;
    1189 
    1190     LogFlowFuncLeaveRC(rc);
    1191     return rc;
    1192 }
    1193 
    1194 /**
    1195  * Resets an clipboard URI transfer.
    1196  *
    1197  * @param   pTransfer           URI clipboard transfer to reset.
    1198  */
    1199 void SharedClipboardURITransferReset(PSHAREDCLIPBOARDURITRANSFER pTransfer)
    1200 {
    1201     AssertPtrReturnVoid(pTransfer);
    1202 
    1203     LogFlowFuncEnter();
    1204 
    1205     /** @todo Anything else to do here? */
    1206 
    1207     if (pTransfer->pProvider)
    1208         pTransfer->pProvider->Reset();
    1209 
    1210     if (pTransfer->pURIList)
    1211         pTransfer->pURIList->Clear();
    1212 
    1213     SharedClipboardURIObjCtxDestroy(&pTransfer->ObjCtx);
    1214 }
    1215 
    1216 /**
    1217  * Returns the clipboard area for a clipboard URI transfer.
    1218  *
    1219  * @returns Current clipboard area, or NULL if none.
    1220  * @param   pTransfer           URI clipboard transfer to return clipboard area for.
    1221  */
    1222 SharedClipboardArea *SharedClipboardURITransferGetArea(PSHAREDCLIPBOARDURITRANSFER pTransfer)
    1223 {
    1224     AssertPtrReturn(pTransfer, NULL);
    1225 
    1226     return pTransfer->pArea;
    1227 }
    1228 
    1229 /**
    1230  * Returns the current object context of a clipboard URI transfer.
    1231  *
    1232  * @returns Current object context, or NULL if none.
    1233  * @param   pTransfer           URI clipboard transfer to return object context for.
    1234  */
    1235 PSHAREDCLIPBOARDCLIENTURIOBJCTX SharedClipboardURITransferGetCurrentObjCtx(PSHAREDCLIPBOARDURITRANSFER pTransfer)
    1236 {
    1237     /* At the moment we only have one object context per transfer at a time. */
    1238     return &pTransfer->ObjCtx;
    1239 }
    1240 
    1241 /**
    1242  * Returns the current URI object for a clipboard URI transfer.
    1243  *
    1244  * @returns Current URI object, or NULL if none.
    1245  * @param   pTransfer           URI clipboard transfer to return current URI object for.
    1246  */
    1247 const SharedClipboardURIObject *SharedClipboardURITransferGetCurrentObject(PSHAREDCLIPBOARDURITRANSFER pTransfer)
    1248 {
    1249     AssertPtrReturn(pTransfer, NULL);
    1250 
    1251     if (pTransfer->pURIList)
    1252         return pTransfer->pURIList->First();
    1253 
    1254     return NULL;
    1255 }
    1256 
    1257 /**
    1258  * Returns the provider for a clipboard URI transfer.
    1259  *
    1260  * @returns Current provider, or NULL if none.
    1261  * @param   pTransfer           URI clipboard transfer to return provider for.
    1262  */
    1263 SharedClipboardProvider *SharedClipboardURITransferGetProvider(PSHAREDCLIPBOARDURITRANSFER pTransfer)
    1264 {
    1265     AssertPtrReturn(pTransfer, NULL);
    1266 
    1267     return pTransfer->pProvider;
    1268 }
    1269 
    1270 /**
    1271  * Returns the URI list for a clipboard URI transfer.
    1272  *
    1273  * @returns Pointer to URI list.
    1274  * @param   pTransfer           URI clipboard transfer to return URI list for.
    1275  */
    1276 SharedClipboardURIList *SharedClipboardURITransferGetList(PSHAREDCLIPBOARDURITRANSFER pTransfer)
    1277 {
    1278     AssertPtrReturn(pTransfer, NULL);
    1279 
    1280     return pTransfer->pURIList;
    1281 }
    1282 
    1283 /**
    1284  * Returns the current URI object for a clipboard URI transfer.
    1285  *
    1286  * @returns Pointer to URI object.
    1287  * @param   pTransfer           URI clipboard transfer to return URI object for.
    1288  */
    1289 SharedClipboardURIObject *SharedClipboardURITransferGetObject(PSHAREDCLIPBOARDURITRANSFER pTransfer, uint64_t uIdx)
    1290 {
    1291     AssertPtrReturn(pTransfer, NULL);
    1292 
    1293     if (!pTransfer->pURIList)
    1294         return NULL;
    1295 
    1296     return pTransfer->pURIList->At(uIdx);
    1297 }
    1298 
    1299 /**
    1300  * Runs (starts) an URI transfer, either in synchronous or asynchronous (threaded) mode.
    1301  *
    1302  * @returns VBox status code.
    1303  * @param   pTransfer           URI clipboard transfer to run.
    1304  * @param   fAsync              Whether to run the transfer synchronously or asynchronously.
    1305  */
    1306 int SharedClipboardURITransferRun(PSHAREDCLIPBOARDURITRANSFER pTransfer, bool fAsync)
    1307 {
    1308     AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
    1309 
    1310     int rc;
    1311 
    1312     LogFlowFunc(("fAsync=%RTbool\n", fAsync));
    1313 
    1314     if (fAsync)
    1315     {
    1316         rc = sharedClipboardURITransferThreadCreate(pTransfer);
    1317     }
    1318     else
    1319     {
    1320         if (pTransfer->State.enmDir == SHAREDCLIPBOARDURITRANSFERDIR_READ)
    1321             rc = SharedClipboardURITransferRead(pTransfer);
    1322         else if (pTransfer->State.enmDir == SHAREDCLIPBOARDURITRANSFERDIR_WRITE)
    1323             rc = SharedClipboardURITransferWrite(pTransfer);
    1324         else
    1325             rc = VERR_NOT_IMPLEMENTED;
    1326     }
    1327 
    1328     LogFlowFuncLeaveRC(rc);
    1329     return rc;
    1330 }
    1331 
    1332 /**
    1333  * Sets or unsets the callback table to be used for a clipboard URI transfer.
    1334  *
    1335  * @returns VBox status code.
    1336  * @param   pTransfer           URI clipboard transfer to set callbacks for.
    1337  * @param   pCallbacks          Pointer to callback table to set. Specify NULL to unset existing callbacks.
    1338  */
    1339 void SharedClipboardURITransferSetCallbacks(PSHAREDCLIPBOARDURITRANSFER pTransfer, PSHAREDCLIPBOARDURITRANSFERCALLBACKS pCallbacks)
    1340 {
    1341     AssertPtrReturnVoid(pTransfer);
    1342     /* pCallbacks might be NULL to unset callbacks. */
    1343 
    1344     LogFlowFunc(("pCallbacks=%p\n", pCallbacks));
    1345 
    1346     if (pCallbacks)
    1347     {
    1348         pTransfer->Callbacks = *pCallbacks;
    1349     }
    1350     else
    1351         RT_ZERO(pTransfer->Callbacks);
    1352 }
    1353 
    1354 /**
    1355  * Creates a thread for a clipboard URI transfer.
    1356  *
    1357  * @returns VBox status code.
    1358  * @param   pTransfer           URI clipboard transfer to create thread for.
    1359  */
    1360 static int sharedClipboardURITransferThreadCreate(PSHAREDCLIPBOARDURITRANSFER pTransfer)
    1361 {
    1362     AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
    1363 
    1364     PFNRTTHREAD pfnRTThread = NULL;
    1365 
    1366     if (pTransfer->State.enmDir == SHAREDCLIPBOARDURITRANSFERDIR_READ)
    1367         pfnRTThread = sharedClipboardURITransferReadThread;
    1368     else if (pTransfer->State.enmDir == SHAREDCLIPBOARDURITRANSFERDIR_WRITE)
    1369         pfnRTThread = sharedClipboardURITransferWriteThread;
    1370 
    1371     AssertPtrReturn(pfnRTThread, VERR_NOT_SUPPORTED);
    1372 
    1373     /* Spawn a worker thread, so that we don't block the window thread for too long. */
    1374     int rc = RTThreadCreate(&pTransfer->Thread.hThread, pfnRTThread,
    1375                             pTransfer /* pvUser */, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
    1376                             "shclp");
    1377     if (RT_SUCCESS(rc))
    1378     {
    1379         int rc2 = RTThreadUserWait(pTransfer->Thread.hThread, 30 * 1000 /* Timeout in ms */);
    1380         AssertRC(rc2);
    1381 
    1382         if (!pTransfer->Thread.fStarted) /* Did the thread fail to start? */
    1383             rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
    1384     }
    1385 
    1386     LogFlowFuncLeaveRC(rc);
    1387     return rc;
    1388 }
    1389 
    1390 /**
    1391  * Destroys a thread of a clipboard URI transfer.
    1392  *
    1393  * @returns VBox status code.
    1394  * @param   pTransfer           URI clipboard transfer to destroy thread for.
    1395  * @param   uTimeoutMs          Timeout (in ms) to wait for thread creation.
    1396  */
    1397 static int sharedClipboardURITransferThreadDestroy(PSHAREDCLIPBOARDURITRANSFER pTransfer, RTMSINTERVAL uTimeoutMs)
    1398 {
    1399     AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
    1400 
    1401     if (pTransfer->Thread.hThread == NIL_RTTHREAD)
    1402         return VINF_SUCCESS;
    1403 
    1404     int rcThread = VERR_WRONG_ORDER;
    1405     int rc = RTThreadWait(pTransfer->Thread.hThread, uTimeoutMs, &rcThread);
    1406 
    1407     LogFlowFunc(("Waiting for thread resulted in %Rrc (thread exited with %Rrc)\n", rc, rcThread));
    1408 
    1409     return rc;
    1410 }
    1411 
    1412 /**
    1413  * Reads all URI objects using the connected provider.
    1414  *
    1415  * @returns VBox status code.
    1416  * @param   pTransfer           Transfer to read objects for.
    1417  */
    1418 int SharedClipboardURITransferRead(PSHAREDCLIPBOARDURITRANSFER pTransfer)
    1419 {
    1420     AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
    1421 
    1422     LogFlowFuncEnter();
    1423 
    1424     int rc = SharedClipboardURITransferMetaDataRead(pTransfer, NULL /* pcbRead */);
    1425     if (RT_SUCCESS(rc))
    1426     {
    1427         rc = SharedClipboardURITransferReadObjects(pTransfer);
    1428         if (RT_SUCCESS(rc))
    1429         {
    1430             if (pTransfer->Callbacks.pfnTransferComplete)
    1431             {
    1432                 SHAREDCLIPBOARDURITRANSFERCALLBACKDATA callbackData = { pTransfer, pTransfer->Callbacks.pvUser };
    1433                 pTransfer->Callbacks.pfnTransferComplete(&callbackData, rc);
    1434             }
    1435         }
    1436     }
    1437 
    1438     if (RT_FAILURE(rc))
    1439     {
    1440         if (pTransfer->Callbacks.pfnTransferError)
    1441         {
    1442             SHAREDCLIPBOARDURITRANSFERCALLBACKDATA callbackData = { pTransfer, pTransfer->Callbacks.pvUser };
    1443             pTransfer->Callbacks.pfnTransferError(&callbackData, rc);
    1444         }
    1445     }
    1446 
    1447     LogFlowFuncLeaveRC(rc);
    1448     return rc;
    1449 }
    1450 
    1451 /**
    1452  * Reads all URI objects using the connected provider.
    1453  *
    1454  * @returns VBox status code.
    1455  * @param   pTransfer           Transfer to read objects for.
    1456  */
    1457 int SharedClipboardURITransferReadObjects(PSHAREDCLIPBOARDURITRANSFER pTransfer)
    1458 {
    1459     AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
    1460 
    1461     LogFlowFuncEnter();
    1462 
    1463     int rc = VERR_NOT_IMPLEMENTED;
    1464 
    1465     LogFlowFuncLeaveRC(rc);
    1466     return rc;
    1467 }
    1468 
    1469 /**
    1470  * Thread for transferring (reading) URI objects from source to the target.
    1471  * For target to source transfers we utilize our own IDataObject / IStream implementations.
    1472  *
    1473  * @returns VBox status code.
    1474  * @param   hThread             Thread handle.
    1475  * @param   pvUser              User arguments; is PSHAREDCLIPBOARDURITRANSFER.
    1476  */
    1477 static int sharedClipboardURITransferReadThread(RTTHREAD hThread, void *pvUser)
    1478 {
    1479     AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
    1480 
    1481     LogFlowFuncEnter();
    1482 
    1483     /* At the moment we only support one transfer at a time. */
    1484     PSHAREDCLIPBOARDURITRANSFER pTransfer = (PSHAREDCLIPBOARDURITRANSFER)pvUser;
    1485     AssertPtr(pTransfer->pProvider);
    1486 
    1487     int rc = VINF_SUCCESS;
    1488 
    1489     if (RT_SUCCESS(rc))
    1490         pTransfer->Thread.fStarted = true;
    1491 
    1492     int rc2 = RTThreadUserSignal(hThread);
    1493     const bool fSignalled = RT_SUCCESS(rc2);
    1494 
    1495     if (RT_SUCCESS(rc))
    1496         rc = SharedClipboardURITransferRead(pTransfer);
    1497 
    1498     if (!fSignalled)
    1499     {
    1500         rc2 = RTThreadUserSignal(hThread);
    1501         AssertRC(rc2);
    1502     }
    1503 
    1504     LogFlowFuncLeaveRC(rc);
    1505     return rc;
    1506 }
    1507 
    1508 /**
    1509  * Creates the internal meta data buffer of an URI clipboard transfer.
    1510  *
    1511  * @returns VBox status code.
    1512  * @param   pTransfer           URI clipboard transfer to create internal meta data for.
    1513  * @param   cbSize              Size (in bytes) of meta data buffer to create. An existing meta data buffer
    1514  *                              will be resized accordingly.
    1515  */
    1516 static int sharedClipboardURITransferMetaDataCreateInternal(PSHAREDCLIPBOARDURITRANSFER pTransfer, uint32_t cbSize)
    1517 {
    1518     int rc;
    1519 
    1520     LogFlowFuncEnter();
    1521 
    1522     if (pTransfer->State.pMeta == NULL)
    1523     {
    1524         pTransfer->State.pMeta = (PSHAREDCLIPBOARDMETADATA)RTMemAlloc(sizeof(SHAREDCLIPBOARDMETADATA));
    1525         if (pTransfer->State.pMeta)
    1526         {
    1527             /** @todo Make meta data format handling more flexible. */
    1528             rc = SharedClipboardMetaDataInit(pTransfer->State.pMeta, SHAREDCLIPBOARDMETADATAFMT_URI_LIST);
    1529         }
    1530         else
    1531             rc = VERR_NO_MEMORY;
    1532     }
    1533     else
    1534         rc = SharedClipboardMetaDataResize(pTransfer->State.pMeta, cbSize);
    1535 
    1536     LogFlowFuncLeaveRC(rc);
    1537     return rc;
    1538 }
    1539 
    1540 /**
    1541  * Destroys a clipboard URI transfer's internal meta data.
    1542  *
    1543  * @param   pTransfer           URI clipboard transfer to destroy internal meta data of.
    1544  */
    1545 static void sharedClipboardURITransferMetaDataDestroyInternal(PSHAREDCLIPBOARDURITRANSFER pTransfer)
    1546 {
    1547     if (!pTransfer->State.pMeta)
    1548         return;
    1549 
    1550     LogFlowFuncEnter();
    1551 
    1552     /* We're done processing the meta data, so just destroy it. */
    1553     SharedClipboardMetaDataDestroy(pTransfer->State.pMeta);
    1554 
    1555     RTMemFree(pTransfer->State.pMeta);
    1556     pTransfer->State.pMeta = NULL;
    1557 }
    1558 
    1559 /**
    1560  * Adds meta data for a clipboard URI transfer, internal version.
    1561  *
    1562  * @returns VBox status code.
    1563  * @param   pTransfer           URI clipboard transfer to set meta data for.
    1564  * @param   pvMeta              Pointer to meta data buffer.
    1565  * @param   cbMeta              Size (in bytes) of meta data buffer.
    1566  */
    1567 static int sharedClipboardURITransferMetaDataAddInternal(PSHAREDCLIPBOARDURITRANSFER pTransfer,
    1568                                                          const void *pvMeta, uint32_t cbMeta)
    1569 {
    1570     LogFlowFunc(("pvMeta=%p, cbMeta=%RU32\n", pvMeta, cbMeta));
    1571 
    1572     int rc = SharedClipboardMetaDataAdd(pTransfer->State.pMeta, pvMeta, cbMeta);
    1573 
    1574     LogFlowFuncLeaveRC(rc);
    1575     return rc;
    1576 }
    1577 
    1578 /**
    1579  * Adds meta data for a clipboard URI transfer.
    1580  *
    1581  * @returns VBox status code.
    1582  * @param   pTransfer           URI clipboard transfer to set meta data for.
    1583  * @param   pvMeta              Pointer to meta data buffer.
    1584  * @param   cbMeta              Size (in bytes) of meta data buffer.
    1585  */
    1586 int SharedClipboardURITransferMetaDataAdd(PSHAREDCLIPBOARDURITRANSFER pTransfer, const void *pvMeta, uint32_t cbMeta)
    1587 {
    1588     AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
    1589 
    1590     LogFlowFuncEnter();
    1591 
    1592     int rc = sharedClipboardURITransferMetaDataCreateInternal(pTransfer, cbMeta);
    1593     if (RT_SUCCESS(rc))
    1594         rc = sharedClipboardURITransferMetaDataAddInternal(pTransfer, pvMeta, cbMeta);
    1595 
    1596     LogFlowFuncLeaveRC(rc);
    1597     return rc;
    1598 }
    1599 
    1600 /**
    1601  * Returns whether the meta data is in a complete state (e.g. completetely read / written) or not.
    1602  *
    1603  * @returns \c true if meta data is complete, \c false if not.
    1604  * @param   pTransfer           URI clipboard transfer to get completion status of meta data for.
    1605  */
    1606 bool SharedClipboardURITransferMetaDataIsComplete(PSHAREDCLIPBOARDURITRANSFER pTransfer)
    1607 {
    1608     AssertPtrReturn(pTransfer->State.pHeader, false);
    1609     AssertPtrReturn(pTransfer->State.pMeta,   false);
    1610 
    1611     return SharedClipboardMetaDataGetUsed(pTransfer->State.pMeta) == pTransfer->State.pHeader->cbMeta;
    1612 }
    1613 
    1614 /**
    1615  * Reads meta for a clipboard URI transfer.
    1616  *
    1617  * @returns VBox status code.
    1618  * @param   pTransfer           URI clipboard transfer to read meta data for.
    1619  * @param   pcbRead             How much meta data (in bytes) was read on success.
    1620  */
    1621 int SharedClipboardURITransferMetaDataRead(PSHAREDCLIPBOARDURITRANSFER pTransfer, uint32_t *pcbRead)
    1622 {
    1623     AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
    1624 
    1625     LogFlowFuncEnter();
    1626 
    1627     /* Destroy any former meta data. */
    1628     SharedClipboardMetaDataDestroy(pTransfer->State.pMeta);
    1629 
    1630     uint32_t cbReadTotal = 0;
    1631 
    1632     int rc = pTransfer->pProvider->ReadDataHdr(&pTransfer->State.pHeader);
    1633     if (RT_SUCCESS(rc))
    1634     {
    1635         uint32_t cbMeta = _4K; /** @todo Improve. */
    1636         void    *pvMeta = RTMemAlloc(cbMeta);
    1637 
    1638         if (pvMeta)
    1639         {
    1640             uint32_t cbMetaToRead = pTransfer->State.pHeader->cbMeta;
    1641             while (cbMetaToRead)
    1642             {
    1643                 uint32_t cbMetaRead;
    1644                 rc = pTransfer->pProvider->ReadDataChunk(pTransfer->State.pHeader, pvMeta, cbMeta, 0 /* fFlags */, &cbMetaRead);
    1645                 if (RT_SUCCESS(rc))
    1646                     rc = sharedClipboardURITransferMetaDataAddInternal(pTransfer, pvMeta, cbMeta);
    1647 
    1648                 if (RT_FAILURE(rc))
    1649                     break;
    1650 
    1651                 Assert(cbMetaToRead >= cbMetaRead);
    1652                 cbMetaToRead -= cbMetaRead;
    1653 
    1654                 cbReadTotal += cbReadTotal;
    1655             }
    1656 
    1657             RTMemFree(pvMeta);
    1658 
    1659             if (RT_SUCCESS(rc))
    1660             {
    1661                 if (pcbRead)
    1662                     *pcbRead = cbReadTotal;
    1663             }
    1664         }
    1665         else
    1666             rc = VERR_NO_MEMORY;
    1667     }
    1668 
    1669     LogFlowFuncLeaveRC(rc);
    1670     return rc;
    1671 }
    1672 
    1673 /**
    1674  * Writes the actual meta data.
    1675  *
    1676  * @returns IPRT status code.
    1677  * @param   pTransfer           Transfer to write meta data for.
    1678  * @param   pcbWritten          How much bytes were written on success. Optional.
    1679  */
    1680 int SharedClipboardURITransferMetaDataWrite(PSHAREDCLIPBOARDURITRANSFER pTransfer, uint32_t *pcbWritten)
    1681 {
    1682     AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
    1683 
    1684     AssertPtr(pTransfer->pProvider);
    1685 
    1686     LogFlowFuncEnter();
    1687 
    1688     AssertPtrReturn(pTransfer->State.pHeader, VERR_WRONG_ORDER);
    1689     AssertPtrReturn(pTransfer->State.pMeta,   VERR_WRONG_ORDER);
    1690 
    1691     uint32_t cbWrittenTotal = 0;
    1692 
    1693     int rc = pTransfer->pProvider->WriteDataHdr(pTransfer->State.pHeader);
    1694     if (RT_SUCCESS(rc))
    1695     {
    1696         /* Sanity. */
    1697         Assert(pTransfer->State.pHeader->cbMeta == pTransfer->State.pMeta->cbUsed);
    1698 
    1699         uint32_t cbMetaToWrite = pTransfer->State.pHeader->cbMeta;
    1700         while (cbMetaToWrite)
    1701         {
    1702             uint32_t cbMetaWritten;
    1703             rc = pTransfer->pProvider->WriteDataChunk(pTransfer->State.pHeader, (uint8_t *)pTransfer->State.pMeta->pvMeta + cbWrittenTotal,
    1704                                                       cbMetaToWrite, 0 /* fFlags */, &cbMetaWritten);
    1705             if (RT_FAILURE(rc))
    1706                 break;
    1707 
    1708             Assert(cbMetaToWrite >= cbMetaWritten);
    1709             cbMetaToWrite -= cbMetaWritten;
    1710 
    1711             cbWrittenTotal += cbMetaWritten;
    1712             Assert(cbWrittenTotal <= pTransfer->State.pHeader->cbMeta);
    1713         }
    1714 
    1715         if (RT_SUCCESS(rc))
    1716         {
    1717             if (pcbWritten)
    1718                 *pcbWritten = cbWrittenTotal;
    1719         }
    1720     }
    1721 
    1722     LogFlowFuncLeaveRC(rc);
    1723     return rc;
    1724 }
    1725 
    1726 /**
    1727  * Writes all URI objects using the connected provider.
    1728  *
    1729  * @returns VBox status code.
    1730  * @param   pTransfer           Transfer to write objects for.
    1731  */
    1732 int SharedClipboardURITransferWriteObjects(PSHAREDCLIPBOARDURITRANSFER pTransfer)
    1733 {
    1734     AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
    1735 
    1736     LogFlowFuncEnter();
    1737 
    1738     int rc = VINF_SUCCESS;
    1739 
    1740     AssertPtrReturn(pTransfer->pURIList,  VERR_WRONG_ORDER);
    1741     AssertPtrReturn(pTransfer->pProvider, VERR_WRONG_ORDER);
    1742 
    1743     while (!pTransfer->pURIList->IsEmpty())
    1744     {
    1745         SharedClipboardURIObject *pObj = pTransfer->pURIList->First();
    1746         AssertPtrBreakStmt(pObj, rc = VERR_INVALID_POINTER);
    1747 
    1748         switch (pObj->GetType())
    1749         {
    1750             case SharedClipboardURIObject::Type_Directory:
    1751             {
    1752                 RTCString strPath = pObj->GetDestPathAbs();
    1753                 LogFlowFunc(("strDir=%s (%zu), fMode=0x%x\n",
    1754                              strPath.c_str(), strPath.length(), pObj->GetMode()));
    1755 
    1756                 VBOXCLIPBOARDDIRDATA dirData;
    1757                 SharedClipboardURIDirDataInit(&dirData);
    1758 
    1759                 dirData.pszPath = RTStrDup(strPath.c_str());
    1760                 dirData.cbPath  = (uint32_t)strlen(dirData.pszPath) + 1 /* Include termination */;
    1761 
    1762                 rc = pTransfer->pProvider->WriteDirectory(&dirData);
    1763 
    1764                 SharedClipboardURIDirDataDestroy(&dirData);
    1765                 break;
    1766             }
    1767 
    1768             case SharedClipboardURIObject::Type_File:
    1769             {
    1770                 AssertBreakStmt(pObj->IsOpen(), rc = VERR_INVALID_STATE);
    1771 
    1772                 RTCString strPath = pObj->GetDestPathAbs();
    1773 
    1774                 LogFlowFunc(("strFile=%s (%zu), cbSize=%RU64, fMode=0x%x\n", strPath.c_str(), strPath.length(),
    1775                              pObj->GetSize(), pObj->GetMode()));
    1776 
    1777                 VBOXCLIPBOARDFILEHDR fileHdr;
    1778                 SharedClipboardURIFileHdrInit(&fileHdr);
    1779 
    1780                 fileHdr.pszFilePath = RTStrDup(strPath.c_str());
    1781                 fileHdr.cbFilePath  = (uint32_t)strlen(fileHdr.pszFilePath) + 1 /* Include termination */;
    1782                 fileHdr.cbSize      = pObj->GetSize();
    1783                 fileHdr.fFlags      = 0;
    1784                 fileHdr.fMode       = pObj->GetMode();
    1785 
    1786                 rc = pTransfer->pProvider->WriteFileHdr(&fileHdr);
    1787                 SharedClipboardURIFileHdrDestroy(&fileHdr);
    1788 
    1789                 if (RT_FAILURE(rc))
    1790                     break;
    1791 
    1792                 uint32_t cbData = _64K; /** @todo Improve. */
    1793                 void    *pvData = RTMemAlloc(cbData);
    1794 
    1795                 AssertPtrBreakStmt(pvData, rc = VERR_NO_MEMORY);
    1796 
    1797                 while (!pObj->IsComplete())
    1798                 {
    1799                     uint32_t cbRead;
    1800                     rc = pObj->Read(pvData, cbData, &cbRead);
    1801                     if (RT_SUCCESS(rc))
    1802                     {
    1803                         rc = pTransfer->pProvider->WriteFileData(pvData, cbRead);
    1804                     }
    1805 
    1806                     if (RT_FAILURE(rc))
    1807                         break;
    1808                 }
    1809 
    1810                 RTMemFree(pvData);
    1811                 pvData = NULL;
    1812                 break;
    1813             }
    1814 
    1815             default:
    1816                 AssertFailed();
    1817                 break;
    1818         }
    1819 
    1820         if (RT_FAILURE(rc))
    1821             break;
    1822 
    1823         /* Only remove current object on success. */
    1824         pTransfer->pURIList->RemoveFirst();
    1825     }
    1826 
    1827     LogFlowFuncLeaveRC(rc);
    1828     return rc;
    1829 }
    1830 
    1831 /**
    1832  * Thread for transferring (writing) URI objects from source to the target.
    1833  * For target to source transfers we utilize our own IDataObject / IStream implementations.
    1834  *
    1835  * @returns VBox status code.
    1836  * @param   hThread             Thread handle.
    1837  * @param   pvUser              User arguments; is PSHAREDCLIPBOARDURITRANSFER.
    1838  */
    1839 static int sharedClipboardURITransferWriteThread(RTTHREAD hThread, void *pvUser)
    1840 {
    1841     AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
    1842 
    1843     LogFlowFuncEnter();
    1844 
    1845     /* At the moment we only support one transfer at a time. */
    1846     PSHAREDCLIPBOARDURITRANSFER pTransfer = (PSHAREDCLIPBOARDURITRANSFER)pvUser;
    1847     AssertPtr(pTransfer->pProvider);
    1848 
    1849     int rc = VINF_SUCCESS;
    1850 
    1851     if (RT_SUCCESS(rc))
    1852         pTransfer->Thread.fStarted = true;
    1853 
    1854     int rc2 = RTThreadUserSignal(hThread);
    1855     const bool fSignalled = RT_SUCCESS(rc2);
    1856 
    1857     if (RT_SUCCESS(rc))
    1858         rc = SharedClipboardURITransferWrite(pTransfer);
    1859 
    1860     if (!fSignalled)
    1861     {
    1862         rc2 = RTThreadUserSignal(hThread);
    1863         AssertRC(rc2);
    1864     }
    1865 
    1866     LogFlowFuncLeaveRC(rc);
    1867     return rc;
    1868 }
    1869 
    1870 /**
    1871  * Main function to write a clipboard URI transfer.
    1872  *
    1873  * @returns VBox status code.
    1874  * @param   pURI                URI clipboard context to write.
    1875  */
    1876 int SharedClipboardURITransferWrite(PSHAREDCLIPBOARDURITRANSFER pTransfer)
    1877 {
    1878     AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
    1879 
    1880     LogFlowFuncEnter();
    1881 
    1882     int rc = SharedClipboardURITransferMetaDataWrite(pTransfer, NULL /* pcbWritten */);
    1883     if (RT_SUCCESS(rc))
    1884     {
    1885         rc = SharedClipboardURITransferWriteObjects(pTransfer);
    1886         if (RT_SUCCESS(rc))
    1887         {
    1888             if (pTransfer->Callbacks.pfnTransferComplete)
    1889             {
    1890                 SHAREDCLIPBOARDURITRANSFERCALLBACKDATA callbackData = { pTransfer, pTransfer->Callbacks.pvUser };
    1891                 pTransfer->Callbacks.pfnTransferComplete(&callbackData, rc);
    1892             }
    1893         }
    1894     }
    1895 
    1896     if (RT_FAILURE(rc))
    1897     {
    1898         if (pTransfer->Callbacks.pfnTransferError)
    1899         {
    1900             SHAREDCLIPBOARDURITRANSFERCALLBACKDATA callbackData = { pTransfer, pTransfer->Callbacks.pvUser };
    1901             pTransfer->Callbacks.pfnTransferError(&callbackData, rc);
    1902         }
    1903     }
    1904 
    1905     LogFlowFuncLeaveRC(rc);
    1906     return rc;
    1907 }
    1908 
    1909 /**
    1910  * Initializes a clipboard URI transfer.
    1911  *
    1912  * @returns VBox status code.
    1913  * @param   pURI                URI clipboard context to initialize.
    1914  */
    1915 int SharedClipboardURICtxInit(PSHAREDCLIPBOARDURICTX pURI)
    1916 {
    1917     AssertPtrReturn(pURI, VERR_INVALID_POINTER);
    1918 
    1919     LogFlowFuncEnter();
    1920 
    1921     int rc = RTCritSectInit(&pURI->CritSect);
    1922     if (RT_SUCCESS(rc))
    1923     {
    1924         RTListInit(&pURI->List);
    1925 
    1926         pURI->cTransfers    = 0;
    1927         pURI->cMaxTransfers = 1; /* For now we only support one transfer per client at a time. */
    1928 
    1929 #ifdef DEBUG_andy
    1930         pURI->cMaxTransfers = UINT32_MAX;
    1931 #endif
    1932 
    1933         SharedClipboardURICtxReset(pURI);
    1934     }
    1935 
    1936     return VINF_SUCCESS;
    1937 }
    1938 
    1939 /**
    1940  * Destroys an URI clipboard information context struct.
    1941  *
    1942  * @param   pURI                URI clipboard context to destroy.
    1943  */
    1944 void SharedClipboardURICtxDestroy(PSHAREDCLIPBOARDURICTX pURI)
    1945 {
    1946     AssertPtrReturnVoid(pURI);
    1947 
    1948     RTCritSectDelete(&pURI->CritSect);
    1949 
    1950     PSHAREDCLIPBOARDURITRANSFER pTransfer, pTransferNext;
    1951     RTListForEachSafe(&pURI->List, pTransfer, pTransferNext, SHAREDCLIPBOARDURITRANSFER, Node)
    1952     {
    1953         SharedClipboardURITransferDestroy(pTransfer);
    1954         RTListNodeRemove(&pTransfer->Node);
    1955     }
    1956 
    1957     LogFlowFuncEnter();
    1958 }
    1959 
    1960 /**
    1961  * Resets an clipboard URI transfer.
    1962  *
    1963  * @param   pURI                URI clipboard context to reset.
    1964  */
    1965 void SharedClipboardURICtxReset(PSHAREDCLIPBOARDURICTX pURI)
    1966 {
    1967     AssertPtrReturnVoid(pURI);
    1968 
    1969     LogFlowFuncEnter();
    1970 
    1971     PSHAREDCLIPBOARDURITRANSFER pTransfer;
    1972     RTListForEach(&pURI->List, pTransfer, SHAREDCLIPBOARDURITRANSFER, Node)
    1973         SharedClipboardURITransferReset(pTransfer);
    1974 }
    1975 
    1976 /**
    1977  * Adds a new URI transfer to an clipboard URI transfer.
    1978  *
    1979  * @returns VBox status code.
    1980  * @param   pURI                URI clipboard context to add transfer to.
    1981  * @param   pTransfer           Pointer to URI clipboard transfer to add.
    1982  */
    1983 int SharedClipboardURICtxTransferAdd(PSHAREDCLIPBOARDURICTX pURI, PSHAREDCLIPBOARDURITRANSFER pTransfer)
    1984 {
    1985     AssertPtrReturn(pURI,      VERR_INVALID_POINTER);
    1986     AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
    1987 
    1988     LogFlowFuncEnter();
    1989 
    1990     if (pURI->cTransfers == pURI->cMaxTransfers)
    1991         return VERR_SHCLPB_MAX_TRANSFERS_REACHED;
    1992 
    1993     RTListAppend(&pURI->List, &pTransfer->Node);
    1994     pURI->cTransfers++;
    1995 
    1996     return VINF_SUCCESS;
    1997 }
    1998 
    1999 /**
    2000  * Removes an URI transfer from a clipboard URI transfer.
    2001  *
    2002  * @returns VBox status code.
    2003  * @param   pURI                URI clipboard context to remove transfer from.
    2004  * @param   pTransfer           Pointer to URI clipboard transfer to remove.
    2005  */
    2006 int SharedClipboardURICtxTransferRemove(PSHAREDCLIPBOARDURICTX pURI, PSHAREDCLIPBOARDURITRANSFER pTransfer)
    2007 {
    2008     AssertPtrReturn(pURI,      VERR_INVALID_POINTER);
    2009     AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
    2010 
    2011     LogFlowFuncEnter();
    2012 
    2013     /* Sanity. */
    2014     AssertReturn(pURI->cTransfers, VERR_WRONG_ORDER);
    2015 
    2016     int rc = SharedClipboardURITransferDestroy(pTransfer);
    2017     if (RT_SUCCESS(rc))
    2018     {
    2019 
    2020         RTListNodeRemove(&pTransfer->Node);
    2021         Assert(pURI->cTransfers);
    2022         pURI->cTransfers--;
    2023     }
    2024 
    2025     LogFlowFuncLeaveRC(rc);
    2026     return rc;
    2027 }
    2028 
    2029 /**
    2030  * Returns a specific URI transfer, internal version.
    2031  *
    2032  * @returns URI transfer, or NULL if not found.
    2033  * @param   pURI                URI clipboard context to return transfer for.
    2034  * @param   uIdx                Index of the transfer to return.
    2035  */
    2036 static PSHAREDCLIPBOARDURITRANSFER sharedClipboardURICtxGetTransferInternal(PSHAREDCLIPBOARDURICTX pURI, uint32_t uIdx)
    2037 {
    2038     AssertReturn(uIdx == 0, NULL); /* Only one transfer allowed at the moment. */
    2039     return RTListGetFirst(&pURI->List, SHAREDCLIPBOARDURITRANSFER, Node);
    2040 }
    2041 
    2042 /**
    2043  * Returns a specific URI transfer.
    2044  *
    2045  * @returns URI transfer, or NULL if not found.
    2046  * @param   pURI                URI clipboard context to return transfer for.
    2047  * @param   uIdx                Index of the transfer to return.
    2048  */
    2049 PSHAREDCLIPBOARDURITRANSFER SharedClipboardURICtxGetTransfer(PSHAREDCLIPBOARDURICTX pURI, uint32_t uIdx)
    2050 {
    2051     return sharedClipboardURICtxGetTransferInternal(pURI, uIdx);
    2052 }
    2053 
    2054 /**
    2055  * Returns the number of active URI transfers.
    2056  *
    2057  * @returns VBox status code.
    2058  * @param   pURI                URI clipboard context to return number for.
    2059  */
    2060 uint32_t SharedClipboardURICtxGetActiveTransfers(PSHAREDCLIPBOARDURICTX pURI)
    2061 {
    2062     AssertPtrReturn(pURI, 0);
    2063     return pURI->cTransfers;
    2064 }
    2065 
    2066 /**
    2067  * Returns whether the maximum of concurrent transfers of a specific URI context has been reached or not.
    2068  *
    2069  * @returns \c if maximum has been reached, \c false if not.
    2070  * @param   pURI                URI clipboard context to determine value for.
    2071  */
    2072 bool SharedClipboardURICtxMaximumTransfersReached(PSHAREDCLIPBOARDURICTX pURI)
    2073 {
    2074     AssertPtrReturn(pURI, true);
    2075 
    2076     LogFlowFunc(("cTransfers=%RU32\n", pURI->cTransfers));
    2077 
    2078     Assert(pURI->cTransfers <= pURI->cMaxTransfers);
    2079     return pURI->cTransfers == pURI->cMaxTransfers;
    2080 }
    2081 #endif /* VBOX_WITH_SHARED_CLIPBOARD_URI_LIST */
    2082 
  • trunk/src/VBox/HostServices/SharedClipboard/Makefile.kmk

    r79181 r79270  
    5454        $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/ClipboardProvider-HostService.cpp \
    5555        $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/ClipboardURIList.cpp \
    56         $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/ClipboardURIObject.cpp
     56        $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/ClipboardURIObject.cpp \
     57        $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/clipboard-uri.cpp
    5758 VBoxSharedClipboard_SOURCES.win += \
    5859        $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/ClipboardDataObjectImpl-win.cpp \
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