VirtualBox

Ignore:
Timestamp:
Sep 4, 2015 2:19:44 AM (9 years ago)
Author:
vboxsync
Message:

IPRT,UINetworkReply.cpp: Added RTPathGlob, a set of RTCrStoreCertAddWantedDir/File/Store, a RTCrStoreCertAddWantedFromFishingExpedition, RTCrStoreCertCheckWanted, RTCrStoreCertCount, RTFsIsCaseSensitive and RTFileOpenTemp. Reworked some RTHttp bits and UINetworkReply stuff - this needs testing.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/common/misc/http.cpp

    r57577 r57613  
    3232#include "internal/iprt.h"
    3333
     34#include <iprt/asm.h>
    3435#include <iprt/assert.h>
     36#include <iprt/crypto/store.h>
    3537#include <iprt/env.h>
    3638#include <iprt/err.h>
     39#include <iprt/file.h>
    3740#include <iprt/mem.h>
     41#include <iprt/path.h>
     42#include <iprt/stream.h>
    3843#include <iprt/string.h>
    39 #include <iprt/file.h>
    40 #include <iprt/stream.h>
     44
     45#include "internal/magics.h"
    4146
    4247#include <curl/curl.h>
    43 #include "internal/magics.h"
    4448
    4549
     
    4751*   Structures and Typedefs                                                                                                      *
    4852*********************************************************************************************************************************/
     53/**
     54 * Internal HTTP client instance.
     55 */
    4956typedef struct RTHTTPINTERNAL
    5057{
     
    5259    uint32_t            u32Magic;
    5360    /** cURL handle. */
    54     CURL                *pCurl;
     61    CURL               *pCurl;
    5562    /** The last response code. */
    5663    long                lLastResp;
    57     /** custom headers */
    58     struct curl_slist   *pHeaders;
    59     /** CA certificate for HTTPS authentication check. */
    60     char                *pcszCAFile;
     64    /** Custom headers/ */
     65    struct curl_slist  *pHeaders;
     66    /** CA certificate file for HTTPS authentication. */
     67    char               *pszCaFile;
     68    /** Whether to delete the CA on destruction. */
     69    bool                fDeleteCaFile;
    6170    /** Abort the current HTTP request if true. */
    62     bool                fAbort;
     71    bool volatile       fAbort;
     72    /** Set if someone is preforming an HTTP operation. */
     73    bool volatile       fBusy;
    6374    /** The location field for 301 responses. */
    64     char                *pszRedirLocation;
     75    char               *pszRedirLocation;
     76
     77    /** Output callback data. */
     78    union
     79    {
     80        /** For file destination.  */
     81        RTFILE          hFile;
     82        /** For memory destination. */
     83        struct
     84        {
     85            /** The current size (sans terminator char). */
     86            size_t      cb;
     87            /** The currently allocated size. */
     88            size_t      cbAllocated;
     89            /** Pointer to the buffer. */
     90            uint8_t    *pb;
     91        } Mem;
     92    } Output;
     93    /** Output callback status. */
     94    int                 rcOutput;
     95    /** Download size hint set by the progress callback. */
     96    uint64_t            cbDownloadHint;
    6597} RTHTTPINTERNAL;
     98/** Pointer to an internal HTTP client instance. */
    6699typedef RTHTTPINTERNAL *PRTHTTPINTERNAL;
    67 
    68 typedef struct RTHTTPMEMCHUNK
    69 {
    70     uint8_t    *pu8Mem;
    71     size_t      cb;
    72 } RTHTTPMEMCHUNK;
    73 typedef RTHTTPMEMCHUNK *PRTHTTPMEMCHUNK;
    74100
    75101
     
    77103*   Defined Constants And Macros                                                                                                 *
    78104*********************************************************************************************************************************/
    79 #define CURL_FAILED(rcCurl) (RT_UNLIKELY(rcCurl != CURLE_OK))
     105/** @def RTHTTP_MAX_MEM_DOWNLOAD
     106 * The max size we are allowed to download to a memory buffer.
     107 *
     108 * @remarks The minus 1 is for the trailing zero terminator we always add.
     109 */
     110#if ARCH_BITS == 64
     111# define RTHTTP_MAX_MEM_DOWNLOAD_SIZE       (UINT32_C(64)*_1M - 1)
     112#else
     113# define RTHTTP_MAX_MEM_DOWNLOAD_SIZE       (UINT32_C(32)*_1M - 1)
     114#endif
     115
     116/** Checks whether a cURL return code indicates success. */
     117#define CURL_SUCCESS(rcCurl)    RT_LIKELY(rcCurl == CURLE_OK)
     118/** Checks whether a cURL return code indicates failure. */
     119#define CURL_FAILURE(rcCurl)    RT_UNLIKELY(rcCurl != CURLE_OK)
    80120
    81121/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
     
    97137
    98138
     139/*********************************************************************************************************************************
     140*   Internal Functions                                                                                                           *
     141*********************************************************************************************************************************/
     142static void rtHttpUnsetCaFile(PRTHTTPINTERNAL pThis);
     143
     144
    99145RTR3DECL(int) RTHttpCreate(PRTHTTP phHttp)
    100146{
    101147    AssertPtrReturn(phHttp, VERR_INVALID_PARAMETER);
    102148
     149    /** @todo r=bird: rainy day: curl_global_init is not thread safe, only a
     150     *        problem if multiple threads get here at the same time. */
     151    int rc = VERR_HTTP_INIT_FAILED;
    103152    CURLcode rcCurl = curl_global_init(CURL_GLOBAL_ALL);
    104     if (CURL_FAILED(rcCurl))
    105         return VERR_HTTP_INIT_FAILED;
    106 
    107     CURL *pCurl = curl_easy_init();
    108     if (!pCurl)
    109         return VERR_HTTP_INIT_FAILED;
    110 
    111     PRTHTTPINTERNAL pHttpInt = (PRTHTTPINTERNAL)RTMemAllocZ(sizeof(RTHTTPINTERNAL));
    112     if (!pHttpInt)
    113         return VERR_NO_MEMORY;
    114 
    115     pHttpInt->u32Magic = RTHTTP_MAGIC;
    116     pHttpInt->pCurl = pCurl;
    117 
    118     *phHttp = (RTHTTP)pHttpInt;
     153    if (!CURL_FAILURE(rcCurl))
     154    {
     155        CURL *pCurl = curl_easy_init();
     156        if (pCurl)
     157        {
     158            PRTHTTPINTERNAL pThis = (PRTHTTPINTERNAL)RTMemAllocZ(sizeof(RTHTTPINTERNAL));
     159            if (pThis)
     160            {
     161                pThis->u32Magic = RTHTTP_MAGIC;
     162                pThis->pCurl    = pCurl;
     163
     164                *phHttp = (RTHTTP)pThis;
     165
     166                return VINF_SUCCESS;
     167            }
     168            rc = VERR_NO_MEMORY;
     169        }
     170        else
     171            rc = VERR_HTTP_INIT_FAILED;
     172    }
     173    curl_global_cleanup();
     174    return rc;
     175}
     176
     177
     178RTR3DECL(void) RTHttpDestroy(RTHTTP hHttp)
     179{
     180    if (hHttp == NIL_RTHTTP)
     181        return;
     182
     183    PRTHTTPINTERNAL pThis = hHttp;
     184    RTHTTP_VALID_RETURN_VOID(pThis);
     185
     186    Assert(!pThis->fBusy);
     187
     188    pThis->u32Magic = RTHTTP_MAGIC_DEAD;
     189
     190    curl_easy_cleanup(pThis->pCurl);
     191    pThis->pCurl = NULL;
     192
     193    if (pThis->pHeaders)
     194        curl_slist_free_all(pThis->pHeaders);
     195
     196    rtHttpUnsetCaFile(pThis);
     197    Assert(!pThis->pszCaFile);
     198
     199    if (pThis->pszRedirLocation)
     200        RTStrFree(pThis->pszRedirLocation);
     201
     202    RTMemFree(pThis);
     203
     204    curl_global_cleanup();
     205}
     206
     207
     208RTR3DECL(int) RTHttpAbort(RTHTTP hHttp)
     209{
     210    PRTHTTPINTERNAL pThis = hHttp;
     211    RTHTTP_VALID_RETURN(pThis);
     212
     213    pThis->fAbort = true;
    119214
    120215    return VINF_SUCCESS;
     
    122217
    123218
    124 RTR3DECL(void) RTHttpDestroy(RTHTTP hHttp)
    125 {
    126     if (!hHttp)
    127         return;
    128 
    129     PRTHTTPINTERNAL pHttpInt = hHttp;
    130     RTHTTP_VALID_RETURN_VOID(pHttpInt);
    131 
    132     pHttpInt->u32Magic = RTHTTP_MAGIC_DEAD;
    133 
    134     curl_easy_cleanup(pHttpInt->pCurl);
    135 
    136     if (pHttpInt->pHeaders)
    137         curl_slist_free_all(pHttpInt->pHeaders);
    138 
    139     if (pHttpInt->pcszCAFile)
    140         RTStrFree(pHttpInt->pcszCAFile);
    141 
    142     if (pHttpInt->pszRedirLocation)
    143         RTStrFree(pHttpInt->pszRedirLocation);
    144 
    145     RTMemFree(pHttpInt);
    146 
    147     curl_global_cleanup();
    148 }
    149 
    150 
    151 static DECLCALLBACK(size_t) rtHttpWriteData(void *pvBuf, size_t cb, size_t n, void *pvUser)
    152 {
    153     PRTHTTPMEMCHUNK pMem = (PRTHTTPMEMCHUNK)pvUser;
    154     size_t cbAll = cb * n;
    155 
    156     pMem->pu8Mem = (uint8_t*)RTMemRealloc(pMem->pu8Mem, pMem->cb + cbAll + 1);
    157     if (pMem->pu8Mem)
    158     {
    159         memcpy(&pMem->pu8Mem[pMem->cb], pvBuf, cbAll);
    160         pMem->cb += cbAll;
    161         pMem->pu8Mem[pMem->cb] = '\0';
    162     }
    163     return cbAll;
    164 }
    165 
    166 
    167 static DECLCALLBACK(int) rtHttpProgress(void *pData, double DlTotal, double DlNow, double UlTotal, double UlNow)
    168 {
    169     PRTHTTPINTERNAL pHttpInt = (PRTHTTPINTERNAL)pData;
    170     AssertReturn(pHttpInt->u32Magic == RTHTTP_MAGIC, 1);
    171 
    172     return pHttpInt->fAbort ? 1 : 0;
    173 }
    174 
    175 
    176 RTR3DECL(int) RTHttpAbort(RTHTTP hHttp)
    177 {
    178     PRTHTTPINTERNAL pHttpInt = hHttp;
    179     RTHTTP_VALID_RETURN(pHttpInt);
    180 
    181     pHttpInt->fAbort = true;
    182 
    183     return VINF_SUCCESS;
    184 }
    185 
    186 
    187219RTR3DECL(int) RTHttpGetRedirLocation(RTHTTP hHttp, char **ppszRedirLocation)
    188220{
    189     PRTHTTPINTERNAL pHttpInt = hHttp;
    190     RTHTTP_VALID_RETURN(pHttpInt);
    191 
    192     if (!pHttpInt->pszRedirLocation)
     221    PRTHTTPINTERNAL pThis = hHttp;
     222    RTHTTP_VALID_RETURN(pThis);
     223    Assert(!pThis->fBusy);
     224
     225    if (!pThis->pszRedirLocation)
    193226        return VERR_HTTP_NOT_FOUND;
    194227
    195     *ppszRedirLocation = RTStrDup(pHttpInt->pszRedirLocation);
    196     return VINF_SUCCESS;
     228    return RTStrDupEx(ppszRedirLocation, pThis->pszRedirLocation);
    197229}
    198230
     
    200232RTR3DECL(int) RTHttpUseSystemProxySettings(RTHTTP hHttp)
    201233{
    202     PRTHTTPINTERNAL pHttpInt = hHttp;
    203     RTHTTP_VALID_RETURN(pHttpInt);
     234    PRTHTTPINTERNAL pThis = hHttp;
     235    RTHTTP_VALID_RETURN(pThis);
    204236
    205237    /*
     
    213245        if (!strncmp(szProxy, RT_STR_TUPLE("http://")))
    214246        {
    215             rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROXY, &szProxy[sizeof("http://") - 1]);
    216             if (CURL_FAILED(rcCurl))
     247            rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXY, &szProxy[sizeof("http://") - 1]);
     248            if (CURL_FAILURE(rcCurl))
    217249                return VERR_INVALID_PARAMETER;
    218             rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROXYPORT, 80);
    219             if (CURL_FAILED(rcCurl))
     250            rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPORT, 80);
     251            if (CURL_FAILURE(rcCurl))
    220252                return VERR_INVALID_PARAMETER;
    221253        }
    222254        else
    223255        {
    224             rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROXY, &szProxy[sizeof("http://") - 1]);
    225             if (CURL_FAILED(rcCurl))
     256            rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXY, &szProxy[sizeof("http://") - 1]);
     257            if (CURL_FAILURE(rcCurl))
    226258                return VERR_INVALID_PARAMETER;
    227259        }
     
    237269                             const char *pcszProxyUser, const char *pcszProxyPwd)
    238270{
    239     PRTHTTPINTERNAL pHttpInt = hHttp;
    240     RTHTTP_VALID_RETURN(pHttpInt);
     271    PRTHTTPINTERNAL pThis = hHttp;
     272    RTHTTP_VALID_RETURN(pThis);
    241273    AssertPtrReturn(pcszProxy, VERR_INVALID_PARAMETER);
    242274
    243     int rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROXY, pcszProxy);
    244     if (CURL_FAILED(rcCurl))
     275    int rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXY, pcszProxy);
     276    if (CURL_FAILURE(rcCurl))
    245277        return VERR_INVALID_PARAMETER;
    246278
    247279    if (uPort != 0)
    248280    {
    249         rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROXYPORT, (long)uPort);
    250         if (CURL_FAILED(rcCurl))
     281        rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPORT, (long)uPort);
     282        if (CURL_FAILURE(rcCurl))
    251283            return VERR_INVALID_PARAMETER;
    252284    }
     
    254286    if (pcszProxyUser && pcszProxyPwd)
    255287    {
    256         rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROXYUSERNAME, pcszProxyUser);
    257         if (CURL_FAILED(rcCurl))
     288        rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYUSERNAME, pcszProxyUser);
     289        if (CURL_FAILURE(rcCurl))
    258290            return VERR_INVALID_PARAMETER;
    259291
    260         rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROXYPASSWORD, pcszProxyPwd);
    261         if (CURL_FAILED(rcCurl))
     292        rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPASSWORD, pcszProxyPwd);
     293        if (CURL_FAILURE(rcCurl))
    262294            return VERR_INVALID_PARAMETER;
    263295    }
     
    269301RTR3DECL(int) RTHttpSetHeaders(RTHTTP hHttp, size_t cHeaders, const char * const *papszHeaders)
    270302{
    271     PRTHTTPINTERNAL pHttpInt = hHttp;
    272     RTHTTP_VALID_RETURN(pHttpInt);
     303    PRTHTTPINTERNAL pThis = hHttp;
     304    RTHTTP_VALID_RETURN(pThis);
    273305
    274306    if (!cHeaders)
    275307    {
    276         if (pHttpInt->pHeaders)
    277             curl_slist_free_all(pHttpInt->pHeaders);
    278         pHttpInt->pHeaders = 0;
     308        if (pThis->pHeaders)
     309            curl_slist_free_all(pThis->pHeaders);
     310        pThis->pHeaders = 0;
    279311        return VINF_SUCCESS;
    280312    }
     
    284316        pHeaders = curl_slist_append(pHeaders, papszHeaders[i]);
    285317
    286     pHttpInt->pHeaders = pHeaders;
    287     int rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_HTTPHEADER, pHeaders);
    288     if (CURL_FAILED(rcCurl))
     318    pThis->pHeaders = pHeaders;
     319    int rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HTTPHEADER, pHeaders);
     320    if (CURL_FAILURE(rcCurl))
    289321        return VERR_INVALID_PARAMETER;
    290322
     
    293325
    294326
    295 RTR3DECL(int) RTHttpSetCAFile(RTHTTP hHttp, const char *pcszCAFile)
    296 {
    297     PRTHTTPINTERNAL pHttpInt = hHttp;
    298     RTHTTP_VALID_RETURN(pHttpInt);
    299 
    300     if (pHttpInt->pcszCAFile)
    301         RTStrFree(pHttpInt->pcszCAFile);
    302     pHttpInt->pcszCAFile = RTStrDup(pcszCAFile);
    303     if (!pHttpInt->pcszCAFile)
    304         return VERR_NO_MEMORY;
    305 
     327/**
     328 * Set the CA file to NULL, deleting any temporary file if necessary.
     329 *
     330 * @param   pThis           The HTTP/HTTPS client instance.
     331 */
     332static void rtHttpUnsetCaFile(PRTHTTPINTERNAL pThis)
     333{
     334    if (pThis->pszCaFile)
     335    {
     336        if (pThis->fDeleteCaFile)
     337        {
     338            int rc2 = RTFileDelete(pThis->pszCaFile);
     339            AssertMsg(RT_SUCCESS(rc2) || !RTFileExists(pThis->pszCaFile), ("rc=%Rrc '%s'\n", rc2, pThis->pszCaFile));
     340        }
     341        RTStrFree(pThis->pszCaFile);
     342        pThis->pszCaFile = NULL;
     343    }
     344}
     345
     346
     347RTR3DECL(int) RTHttpSetCAFile(RTHTTP hHttp, const char *pszCaFile)
     348{
     349    PRTHTTPINTERNAL pThis = hHttp;
     350    RTHTTP_VALID_RETURN(pThis);
     351
     352    rtHttpUnsetCaFile(pThis);
     353
     354    pThis->fDeleteCaFile = false;
     355    if (pszCaFile)
     356        return RTStrDupEx(&pThis->pszCaFile, pszCaFile);
    306357    return VINF_SUCCESS;
    307358}
     359
     360
     361RTR3DECL(int) RTHttpUseTemporaryCaFile(RTHTTP hHttp, PRTERRINFO pErrInfo)
     362{
     363    PRTHTTPINTERNAL pThis = hHttp;
     364    RTHTTP_VALID_RETURN(pThis);
     365
     366    /*
     367     * Create a temporary file.
     368     */
     369    int rc = VERR_NO_STR_MEMORY;
     370    char *pszCaFile = RTStrAlloc(RTPATH_MAX);
     371    if (pszCaFile)
     372    {
     373        RTFILE hFile;
     374        rc = RTFileOpenTemp(&hFile, pszCaFile, RTPATH_MAX,
     375                            RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE | (0600 << RTFILE_O_CREATE_MODE_SHIFT));
     376        if (RT_SUCCESS(rc))
     377        {
     378            /*
     379             * Gather certificates into a temporary store and export them to the temporary file.
     380             */
     381            RTCRSTORE hStore;
     382            rc = RTCrStoreCreateInMem(&hStore, 256);
     383            if (RT_SUCCESS(rc))
     384            {
     385                rc = RTHttpGatherCaCertsInStore(hStore, 0 /*fFlags*/, pErrInfo);
     386                if (RT_SUCCESS(rc))
     387                    /** @todo Consider adding an API for exporting to a RTFILE... */
     388                    rc = RTCrStoreCertExportAsPem(hStore, 0 /*fFlags*/, pszCaFile);
     389                RTCrStoreRelease(hStore);
     390            }
     391            RTFileClose(hFile);
     392            if (RT_SUCCESS(rc))
     393            {
     394                /*
     395                 * Set the CA file for the instance.
     396                 */
     397                rtHttpUnsetCaFile(pThis);
     398
     399                pThis->fDeleteCaFile = true;
     400                pThis->pszCaFile = pszCaFile;
     401                return VINF_SUCCESS;
     402            }
     403
     404            int rc2 = RTFileDelete(pszCaFile);
     405            AssertRC(rc2);
     406        }
     407        else
     408            RTErrInfoAddF(pErrInfo, rc, "Error creating temorary file: %Rrc", rc);
     409
     410        RTStrFree(pszCaFile);
     411    }
     412    return rc;
     413}
     414
     415
     416RTR3DECL(int) RTHttpGatherCaCertsInStore(RTCRSTORE hStore, uint32_t fFlags, PRTERRINFO pErrInfo)
     417{
     418    uint32_t const cBefore = RTCrStoreCertCount(hStore);
     419    AssertReturn(cBefore != UINT32_MAX, VERR_INVALID_HANDLE);
     420
     421    /*
     422     * Add the user store, quitely ignoring any errors.
     423     */
     424    RTCRSTORE hSrcStore;
     425    int rcUser = RTCrStoreCreateSnapshotById(&hSrcStore, RTCRSTOREID_USER_TRUSTED_CAS_AND_CERTIFICATES, pErrInfo);
     426    if (RT_SUCCESS(rcUser))
     427    {
     428        rcUser = RTCrStoreCertAddFromStore(hStore, RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
     429                                           hSrcStore);
     430        RTCrStoreRelease(hSrcStore);
     431    }
     432
     433    /*
     434     * Ditto for the system store.
     435     */
     436    int rcSystem = RTCrStoreCreateSnapshotById(&hSrcStore, RTCRSTOREID_USER_TRUSTED_CAS_AND_CERTIFICATES, pErrInfo);
     437    if (RT_SUCCESS(rcSystem))
     438    {
     439        rcSystem = RTCrStoreCertAddFromStore(hStore, RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
     440                                             hSrcStore);
     441        RTCrStoreRelease(hSrcStore);
     442    }
     443
     444    /*
     445     * If the number of certificates increased, we consider it a success.
     446     */
     447    if (RTCrStoreCertCount(hStore) > cBefore)
     448    {
     449        if (RT_FAILURE(rcSystem))
     450            return -rcSystem;
     451        if (RT_FAILURE(rcUser))
     452            return -rcUser;
     453        return rcSystem != VINF_SUCCESS ? rcSystem : rcUser;
     454    }
     455
     456    if (RT_FAILURE(rcSystem))
     457        return rcSystem;
     458    if (RT_FAILURE(rcUser))
     459        return rcSystem;
     460    return VERR_NOT_FOUND;
     461}
     462
     463
     464RTR3DECL(int) RTHttpGatherCaCertsInFile(const char *pszCaFile, uint32_t fFlags, PRTERRINFO pErrInfo)
     465{
     466    RTCRSTORE hStore;
     467    int rc = RTCrStoreCreateInMem(&hStore, 256);
     468    if (RT_SUCCESS(rc))
     469    {
     470        rc = RTHttpGatherCaCertsInStore(hStore, fFlags, pErrInfo);
     471        if (RT_SUCCESS(rc))
     472            rc = RTCrStoreCertExportAsPem(hStore, 0 /*fFlags*/, pszCaFile);
     473        RTCrStoreRelease(hStore);
     474    }
     475    return rc;
     476}
     477
    308478
    309479
     
    312482 *
    313483 * @returns IPRT status code.
    314  * @param   pHttpInt            HTTP instance.
    315  * @param   rcCurl              What curl returned.
    316  */
    317 static int rtHttpGetCalcStatus(PRTHTTPINTERNAL pHttpInt, int rcCurl)
     484 * @param   pThis           The HTTP/HTTPS client instance.
     485 * @param   rcCurl          What curl returned.
     486 */
     487static int rtHttpGetCalcStatus(PRTHTTPINTERNAL pThis, int rcCurl)
    318488{
    319489    int rc = VERR_INTERNAL_ERROR;
    320490
    321     if (pHttpInt->pszRedirLocation)
    322     {
    323         RTStrFree(pHttpInt->pszRedirLocation);
    324         pHttpInt->pszRedirLocation = NULL;
     491    if (pThis->pszRedirLocation)
     492    {
     493        RTStrFree(pThis->pszRedirLocation);
     494        pThis->pszRedirLocation = NULL;
    325495    }
    326496    if (rcCurl == CURLE_OK)
    327497    {
    328         curl_easy_getinfo(pHttpInt->pCurl, CURLINFO_RESPONSE_CODE, &pHttpInt->lLastResp);
    329         switch (pHttpInt->lLastResp)
     498        curl_easy_getinfo(pThis->pCurl, CURLINFO_RESPONSE_CODE, &pThis->lLastResp);
     499        switch (pThis->lLastResp)
    330500        {
    331501            case 200:
     
    338508            {
    339509                const char *pszRedirect;
    340                 curl_easy_getinfo(pHttpInt->pCurl, CURLINFO_REDIRECT_URL, &pszRedirect);
     510                curl_easy_getinfo(pThis->pCurl, CURLINFO_REDIRECT_URL, &pszRedirect);
    341511                size_t cb = strlen(pszRedirect);
    342512                if (cb > 0 && cb < 2048)
    343                     pHttpInt->pszRedirLocation = RTStrDup(pszRedirect);
     513                    pThis->pszRedirLocation = RTStrDup(pszRedirect);
    344514                rc = VERR_HTTP_REDIRECTED;
    345515                break;
     
    399569
    400570/**
     571 * cURL callback for reporting progress, we use it for checking for abort.
     572 */
     573static int rtHttpProgress(void *pData, double rdTotalDownload, double rdDownloaded, double rdTotalUpload, double rdUploaded)
     574{
     575    PRTHTTPINTERNAL pThis = (PRTHTTPINTERNAL)pData;
     576    AssertReturn(pThis->u32Magic == RTHTTP_MAGIC, 1);
     577
     578    pThis->cbDownloadHint = (uint64_t)rdTotalDownload;
     579
     580    return pThis->fAbort ? 1 : 0;
     581}
     582
     583
     584/**
     585 * Whether we're likely to need SSL to handle the give URL.
     586 *
     587 * @returns true if we need, false if we probably don't.
     588 * @param   pszUrl              The URL.
     589 */
     590static bool rtHttpNeedSsl(const char *pszUrl)
     591{
     592    return RTStrNICmp(pszUrl, RT_STR_TUPLE("https:")) == 0;
     593}
     594
     595
     596/**
     597 * Applies recoded settings to the cURL instance before doing work.
     598 *
     599 * @returns IPRT status code.
     600 * @param   pThis           The HTTP/HTTPS client instance.
     601 * @param   pszUrl          The URL.
     602 */
     603static int rtHttpApplySettings(PRTHTTPINTERNAL pThis, const char *pszUrl)
     604{
     605    /*
     606     * The URL.
     607     */
     608    int rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_URL, pszUrl);
     609    if (CURL_FAILURE(rcCurl))
     610        return VERR_INVALID_PARAMETER;
     611
     612    /*
     613     * Setup SSL.  Can be a bit of work.
     614     */
     615    rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_SSLVERSION, (long)CURL_SSLVERSION_TLSv1);
     616    if (CURL_FAILURE(rcCurl))
     617        return VERR_INVALID_PARAMETER;
     618
     619    const char *pszCaFile = pThis->pszCaFile;
     620    if (   !pszCaFile
     621        && rtHttpNeedSsl(pszUrl))
     622    {
     623        int rc = RTHttpUseTemporaryCaFile(pThis, NULL);
     624        if (RT_SUCCESS(rc))
     625            pszCaFile = pThis->pszCaFile;
     626        else
     627            return rc; /* Non-portable alternative: pszCaFile = "/etc/ssl/certs/ca-certificates.crt"; */
     628    }
     629    if (pszCaFile)
     630    {
     631        rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_CAINFO, pszCaFile);
     632        if (CURL_FAILURE(rcCurl))
     633            return VERR_INTERNAL_ERROR;
     634    }
     635
     636    /*
     637     * Progress/abort.
     638     */
     639    rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROGRESSFUNCTION, &rtHttpProgress);
     640    if (CURL_FAILURE(rcCurl))
     641        return VERR_INTERNAL_ERROR;
     642    rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROGRESSDATA, (void *)pThis);
     643    if (CURL_FAILURE(rcCurl))
     644        return VERR_INTERNAL_ERROR;
     645    rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_NOPROGRESS, (long)0);
     646    if (CURL_FAILURE(rcCurl))
     647        return VERR_INTERNAL_ERROR;
     648
     649    return VINF_SUCCESS;
     650}
     651
     652
     653/**
     654 * cURL callback for writing data.
     655 */
     656static size_t rtHttpWriteData(void *pvBuf, size_t cbUnit, size_t cUnits, void *pvUser)
     657{
     658    PRTHTTPINTERNAL pThis = (PRTHTTPINTERNAL)pvUser;
     659
     660    /*
     661     * Do max size and overflow checks.
     662     */
     663    size_t const cbToAppend = cbUnit * cUnits;
     664    size_t const cbCurSize  = pThis->Output.Mem.cb;
     665    size_t const cbNewSize  = cbCurSize + cbToAppend;
     666    if (   cbToAppend < RTHTTP_MAX_MEM_DOWNLOAD_SIZE
     667        && cbNewSize  < RTHTTP_MAX_MEM_DOWNLOAD_SIZE)
     668    {
     669        if (cbNewSize + 1 <= pThis->Output.Mem.cbAllocated)
     670        {
     671            memcpy(&pThis->Output.Mem.pb[cbCurSize], pvBuf, cbToAppend);
     672            pThis->Output.Mem.cb = cbNewSize;
     673            pThis->Output.Mem.pb[cbNewSize] = '\0';
     674            return VINF_SUCCESS;
     675        }
     676
     677        /*
     678         * We need to reallocate the output buffer.
     679         */
     680        /** @todo this could do with a better strategy wrt growth. */
     681        size_t cbAlloc = RT_ALIGN_Z(cbNewSize + 1, 64);
     682        if (   cbAlloc <= pThis->cbDownloadHint
     683            && pThis->cbDownloadHint < RTHTTP_MAX_MEM_DOWNLOAD_SIZE)
     684            cbAlloc = RT_ALIGN_Z(pThis->cbDownloadHint + 1, 64);
     685
     686        uint8_t *pbNew = (uint8_t *)RTMemRealloc(pThis->Output.Mem.pb, cbAlloc);
     687        if (pbNew)
     688        {
     689            memcpy(&pbNew[cbCurSize], pvBuf, cbToAppend);
     690            pbNew[cbNewSize] = '\0';
     691
     692            pThis->Output.Mem.cbAllocated = cbAlloc;
     693            pThis->Output.Mem.pb = pbNew;
     694            pThis->Output.Mem.cb = cbNewSize;
     695            return VINF_SUCCESS;
     696        }
     697
     698        pThis->rcOutput = VERR_NO_MEMORY;
     699    }
     700    else
     701        pThis->rcOutput      = VERR_TOO_MUCH_DATA;
     702
     703    /*
     704     * Failure - abort.
     705     */
     706    RTMemFree(pThis->Output.Mem.pb);
     707    pThis->Output.Mem.pb = NULL;
     708    pThis->Output.Mem.cb = RTHTTP_MAX_MEM_DOWNLOAD_SIZE;
     709    pThis->fAbort        = true;
     710    return 0;
     711}
     712
     713
     714/**
    401715 * Internal worker that performs a HTTP GET.
    402716 *
    403717 * @returns IPRT status code.
    404  * @param   hHttp               The HTTP instance.
    405  * @param   pcszUrl             The URL.
     718 * @param   hHttp               The HTTP/HTTPS client instance.
     719 * @param   pszUrl              The URL.
    406720 * @param   ppvResponse         Where to return the pointer to the allocated
    407721 *                              response data (RTMemFree).  There will always be
     
    409723 *                              is not part of the size returned via @a pcb.
    410724 * @param   pcb                 The size of the response data.
    411  */
    412 RTR3DECL(int) rtHttpGet(RTHTTP hHttp, const char *pcszUrl, uint8_t **ppvResponse, size_t *pcb)
    413 {
    414     PRTHTTPINTERNAL pHttpInt = hHttp;
    415     RTHTTP_VALID_RETURN(pHttpInt);
    416 
    417     pHttpInt->fAbort = false;
    418 
    419     int rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_URL, pcszUrl);
    420     if (CURL_FAILED(rcCurl))
    421         return VERR_INVALID_PARAMETER;
    422 
    423 #if 0
    424     rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_VERBOSE, 1);
    425     if (CURL_FAILED(rcCurl))
    426         return VERR_INVALID_PARAMETER;
    427 #endif
    428 
    429     const char *pcszCAFile = "/etc/ssl/certs/ca-certificates.crt";
    430     if (pHttpInt->pcszCAFile)
    431         pcszCAFile = pHttpInt->pcszCAFile;
    432     if (RTFileExists(pcszCAFile))
    433     {
    434         rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_CAINFO, pcszCAFile);
    435         if (CURL_FAILED(rcCurl))
    436             return VERR_INTERNAL_ERROR;
    437     }
    438 
    439     RTHTTPMEMCHUNK Chunk = { NULL, 0 };
    440     rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_WRITEFUNCTION, &rtHttpWriteData);
    441     if (CURL_FAILED(rcCurl))
    442         return VERR_INTERNAL_ERROR;
    443     rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_WRITEDATA, (void *)&Chunk);
    444     if (CURL_FAILED(rcCurl))
    445         return VERR_INTERNAL_ERROR;
    446     rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROGRESSFUNCTION, &rtHttpProgress);
    447     if (CURL_FAILED(rcCurl))
    448         return VERR_INTERNAL_ERROR;
    449     rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROGRESSDATA, (void *)pHttpInt);
    450     if (CURL_FAILED(rcCurl))
    451         return VERR_INTERNAL_ERROR;
    452     rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_NOPROGRESS, (long)0);
    453     if (CURL_FAILED(rcCurl))
    454         return VERR_INTERNAL_ERROR;
    455     rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_SSLVERSION, (long)CURL_SSLVERSION_TLSv1);
    456     if (CURL_FAILED(rcCurl))
    457         return VERR_INVALID_PARAMETER;
    458 
    459     rcCurl = curl_easy_perform(pHttpInt->pCurl);
    460 
    461     int rc = rtHttpGetCalcStatus(pHttpInt, rcCurl);
     725 *
     726 * @remarks We ASSUME the API user doesn't do concurrent GETs in different
     727 *          threads, because that will probably blow up!
     728 */
     729static int rtHttpGetToMem(RTHTTP hHttp, const char *pszUrl, uint8_t **ppvResponse, size_t *pcb)
     730{
     731    PRTHTTPINTERNAL pThis = hHttp;
     732    RTHTTP_VALID_RETURN(pThis);
     733
     734    /*
     735     * Reset the return values in case of more "GUI programming" on the client
     736     * side (i.e. a programming style not bothering checking return codes).
     737     */
     738    *ppvResponse = NULL;
     739    *pcb         = 0;
     740
     741    /*
     742     * Set the busy flag (paranoia).
     743     */
     744    bool fBusy = ASMAtomicXchgBool(&pThis->fBusy, true);
     745    AssertReturn(fBusy, VERR_WRONG_ORDER);
     746
     747    /*
     748     * Reset the state and apply settings.
     749     */
     750    pThis->fAbort = false;
     751    pThis->rcOutput = VINF_SUCCESS;
     752    pThis->cbDownloadHint = 0;
     753
     754    int rc = rtHttpApplySettings(hHttp, pszUrl);
    462755    if (RT_SUCCESS(rc))
    463756    {
    464         *ppvResponse = Chunk.pu8Mem;
    465         *pcb         = Chunk.cb;
    466     }
    467     else
    468     {
    469         if (Chunk.pu8Mem)
    470             RTMemFree(Chunk.pu8Mem);
    471         *ppvResponse = NULL;
    472         *pcb         = 0;
    473     }
    474 
     757        RT_ZERO(pThis->Output.Mem);
     758        int rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_WRITEFUNCTION, &rtHttpWriteData);
     759        if (!CURL_FAILURE(rcCurl))
     760            rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_WRITEDATA, (void *)pThis);
     761        if (!CURL_FAILURE(rcCurl))
     762        {
     763            /*
     764             * Perform the HTTP operation.
     765             */
     766            rcCurl = curl_easy_perform(pThis->pCurl);
     767            rc = rtHttpGetCalcStatus(pThis, rcCurl);
     768            if (RT_SUCCESS(rc))
     769                rc = pThis->rcOutput;
     770            if (RT_SUCCESS(rc))
     771            {
     772                *ppvResponse = pThis->Output.Mem.pb;
     773                *pcb         = pThis->Output.Mem.cb;
     774            }
     775            else if (pThis->Output.Mem.pb)
     776                RTMemFree(pThis->Output.Mem.pb);
     777            RT_ZERO(pThis->Output.Mem);
     778        }
     779        else
     780            rc = VERR_INTERNAL_ERROR_3;
     781    }
     782
     783    ASMAtomicWriteBool(&pThis->fBusy, false);
    475784    return rc;
    476785}
    477786
    478787
    479 RTR3DECL(int) RTHttpGetText(RTHTTP hHttp, const char *pcszUrl, char **ppszNotUtf8)
     788RTR3DECL(int) RTHttpGetText(RTHTTP hHttp, const char *pszUrl, char **ppszNotUtf8)
    480789{
    481790    uint8_t *pv;
    482791    size_t   cb;
    483     int rc = rtHttpGet(hHttp, pcszUrl, &pv, &cb);
     792    int rc = rtHttpGetToMem(hHttp, pszUrl, &pv, &cb);
    484793    if (RT_SUCCESS(rc))
    485794    {
     
    501810
    502811
    503 RTR3DECL(int) RTHttpGetBinary(RTHTTP hHttp, const char *pcszUrl, void **ppvResponse, size_t *pcb)
    504 {
    505     return rtHttpGet(hHttp, pcszUrl, (uint8_t **)ppvResponse, pcb);
     812RTR3DECL(int) RTHttpGetBinary(RTHTTP hHttp, const char *pszUrl, void **ppvResponse, size_t *pcb)
     813{
     814    return rtHttpGetToMem(hHttp, pszUrl, (uint8_t **)ppvResponse, pcb);
    506815}
    507816
     
    513822
    514823
    515 static size_t rtHttpWriteDataToFile(void *pvBuf, size_t cb, size_t n, void *pvUser)
    516 {
    517     size_t cbAll = cb * n;
    518     RTFILE hFile = (RTFILE)(intptr_t)pvUser;
    519 
     824/**
     825 * cURL callback for writing data to a file.
     826 */
     827static size_t rtHttpWriteDataToFile(void *pvBuf, size_t cbUnit, size_t cUnits, void *pvUser)
     828{
     829    PRTHTTPINTERNAL pThis = (PRTHTTPINTERNAL)pvUser;
    520830    size_t cbWritten = 0;
    521     int rc = RTFileWrite(hFile, pvBuf, cbAll, &cbWritten);
     831    int rc = RTFileWrite(pThis->Output.hFile, pvBuf, cbUnit * cUnits, &cbWritten);
    522832    if (RT_SUCCESS(rc))
    523833        return cbWritten;
     834    pThis->rcOutput = rc;
    524835    return 0;
    525836}
     
    528839RTR3DECL(int) RTHttpGetFile(RTHTTP hHttp, const char *pszUrl, const char *pszDstFile)
    529840{
    530     PRTHTTPINTERNAL pHttpInt = hHttp;
    531     RTHTTP_VALID_RETURN(pHttpInt);
    532 
    533     /*
    534      * Set up the request.
    535      */
    536     pHttpInt->fAbort = false;
    537 
    538     int rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_URL, pszUrl);
    539     if (CURL_FAILED(rcCurl))
    540         return VERR_INVALID_PARAMETER;
    541 
    542 #if 0
    543     rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_VERBOSE, 1);
    544     if (CURL_FAILED(rcCurl))
    545         return VERR_INVALID_PARAMETER;
    546 #endif
    547 
    548     const char *pcszCAFile = "/etc/ssl/certs/ca-certificates.crt";
    549     if (pHttpInt->pcszCAFile)
    550         pcszCAFile = pHttpInt->pcszCAFile;
    551     if (RTFileExists(pcszCAFile))
    552     {
    553         rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_CAINFO, pcszCAFile);
    554         if (CURL_FAILED(rcCurl))
    555             return VERR_INTERNAL_ERROR;
    556     }
    557 
    558     rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_WRITEFUNCTION, &rtHttpWriteDataToFile);
    559     if (CURL_FAILED(rcCurl))
    560         return VERR_INTERNAL_ERROR;
    561     rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROGRESSFUNCTION, &rtHttpProgress);
    562     if (CURL_FAILED(rcCurl))
    563         return VERR_INTERNAL_ERROR;
    564     rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_PROGRESSDATA, (void *)pHttpInt);
    565     if (CURL_FAILED(rcCurl))
    566         return VERR_INTERNAL_ERROR;
    567     rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_NOPROGRESS, (long)0);
    568     if (CURL_FAILED(rcCurl))
    569         return VERR_INTERNAL_ERROR;
    570 
    571     /*
    572      * Open the output file.
    573      */
    574     RTFILE hFile;
    575     int rc = RTFileOpen(&hFile, pszDstFile, RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_READWRITE);
     841    PRTHTTPINTERNAL pThis = hHttp;
     842    RTHTTP_VALID_RETURN(pThis);
     843
     844    /*
     845     * Set the busy flag (paranoia).
     846     */
     847    bool fBusy = ASMAtomicXchgBool(&pThis->fBusy, true);
     848    AssertReturn(fBusy, VERR_WRONG_ORDER);
     849
     850    /*
     851     * Reset the state and apply settings.
     852     */
     853    pThis->fAbort = false;
     854    pThis->rcOutput = VINF_SUCCESS;
     855    pThis->cbDownloadHint = 0;
     856
     857    int rc = rtHttpApplySettings(hHttp, pszUrl);
    576858    if (RT_SUCCESS(rc))
    577859    {
    578         rcCurl = curl_easy_setopt(pHttpInt->pCurl, CURLOPT_WRITEDATA, (void *)(uintptr_t)hFile);
    579         if (!CURL_FAILED(rcCurl))
     860        pThis->Output.hFile = NIL_RTFILE;
     861        int rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_WRITEFUNCTION, &rtHttpWriteDataToFile);
     862        if (!CURL_FAILURE(rcCurl))
     863            rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_WRITEDATA, (void *)pThis);
     864        if (!CURL_FAILURE(rcCurl))
    580865        {
    581866            /*
    582              * Perform the request.
     867             * Open the output file.
    583868             */
    584             rcCurl = curl_easy_perform(pHttpInt->pCurl);
    585             rc = rtHttpGetCalcStatus(pHttpInt, rcCurl);
     869            rc = RTFileOpen(&pThis->Output.hFile, pszDstFile, RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_READWRITE);
     870            if (RT_SUCCESS(rc))
     871            {
     872                /*
     873                 * Perform the HTTP operation.
     874                 */
     875                rcCurl = curl_easy_perform(pThis->pCurl);
     876                rc = rtHttpGetCalcStatus(pThis, rcCurl);
     877                if (RT_SUCCESS(rc))
     878                     rc = pThis->rcOutput;
     879
     880                int rc2 = RTFileClose(pThis->Output.hFile);
     881                if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
     882                    rc = rc2;
     883            }
     884            pThis->Output.hFile = NIL_RTFILE;
    586885        }
    587886        else
    588             rc = VERR_INTERNAL_ERROR;
    589 
    590         int rc2 = RTFileClose(hFile);
    591         if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
    592             rc = rc2;
    593     }
    594 
     887            rc = VERR_INTERNAL_ERROR_3;
     888    }
     889
     890    ASMAtomicWriteBool(&pThis->fBusy, false);
    595891    return rc;
    596892}
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