Changeset 57613 in vbox for trunk/src/VBox/Runtime/common/misc
- Timestamp:
- Sep 4, 2015 2:19:44 AM (9 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/common/misc/http.cpp
r57577 r57613 32 32 #include "internal/iprt.h" 33 33 34 #include <iprt/asm.h> 34 35 #include <iprt/assert.h> 36 #include <iprt/crypto/store.h> 35 37 #include <iprt/env.h> 36 38 #include <iprt/err.h> 39 #include <iprt/file.h> 37 40 #include <iprt/mem.h> 41 #include <iprt/path.h> 42 #include <iprt/stream.h> 38 43 #include <iprt/string.h> 39 #include <iprt/file.h> 40 #include <iprt/stream.h>44 45 #include "internal/magics.h" 41 46 42 47 #include <curl/curl.h> 43 #include "internal/magics.h"44 48 45 49 … … 47 51 * Structures and Typedefs * 48 52 *********************************************************************************************************************************/ 53 /** 54 * Internal HTTP client instance. 55 */ 49 56 typedef struct RTHTTPINTERNAL 50 57 { … … 52 59 uint32_t u32Magic; 53 60 /** cURL handle. */ 54 CURL 61 CURL *pCurl; 55 62 /** The last response code. */ 56 63 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; 61 70 /** 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; 63 74 /** 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; 65 97 } RTHTTPINTERNAL; 98 /** Pointer to an internal HTTP client instance. */ 66 99 typedef RTHTTPINTERNAL *PRTHTTPINTERNAL; 67 68 typedef struct RTHTTPMEMCHUNK69 {70 uint8_t *pu8Mem;71 size_t cb;72 } RTHTTPMEMCHUNK;73 typedef RTHTTPMEMCHUNK *PRTHTTPMEMCHUNK;74 100 75 101 … … 77 103 * Defined Constants And Macros * 78 104 *********************************************************************************************************************************/ 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) 80 120 81 121 /** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */ … … 97 137 98 138 139 /********************************************************************************************************************************* 140 * Internal Functions * 141 *********************************************************************************************************************************/ 142 static void rtHttpUnsetCaFile(PRTHTTPINTERNAL pThis); 143 144 99 145 RTR3DECL(int) RTHttpCreate(PRTHTTP phHttp) 100 146 { 101 147 AssertPtrReturn(phHttp, VERR_INVALID_PARAMETER); 102 148 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; 103 152 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 178 RTR3DECL(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 208 RTR3DECL(int) RTHttpAbort(RTHTTP hHttp) 209 { 210 PRTHTTPINTERNAL pThis = hHttp; 211 RTHTTP_VALID_RETURN(pThis); 212 213 pThis->fAbort = true; 119 214 120 215 return VINF_SUCCESS; … … 122 217 123 218 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 187 219 RTR3DECL(int) RTHttpGetRedirLocation(RTHTTP hHttp, char **ppszRedirLocation) 188 220 { 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) 193 226 return VERR_HTTP_NOT_FOUND; 194 227 195 *ppszRedirLocation = RTStrDup(pHttpInt->pszRedirLocation); 196 return VINF_SUCCESS; 228 return RTStrDupEx(ppszRedirLocation, pThis->pszRedirLocation); 197 229 } 198 230 … … 200 232 RTR3DECL(int) RTHttpUseSystemProxySettings(RTHTTP hHttp) 201 233 { 202 PRTHTTPINTERNAL p HttpInt= hHttp;203 RTHTTP_VALID_RETURN(p HttpInt);234 PRTHTTPINTERNAL pThis = hHttp; 235 RTHTTP_VALID_RETURN(pThis); 204 236 205 237 /* … … 213 245 if (!strncmp(szProxy, RT_STR_TUPLE("http://"))) 214 246 { 215 rcCurl = curl_easy_setopt(p HttpInt->pCurl, CURLOPT_PROXY, &szProxy[sizeof("http://") - 1]);216 if (CURL_FAIL ED(rcCurl))247 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXY, &szProxy[sizeof("http://") - 1]); 248 if (CURL_FAILURE(rcCurl)) 217 249 return VERR_INVALID_PARAMETER; 218 rcCurl = curl_easy_setopt(p HttpInt->pCurl, CURLOPT_PROXYPORT, 80);219 if (CURL_FAIL ED(rcCurl))250 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPORT, 80); 251 if (CURL_FAILURE(rcCurl)) 220 252 return VERR_INVALID_PARAMETER; 221 253 } 222 254 else 223 255 { 224 rcCurl = curl_easy_setopt(p HttpInt->pCurl, CURLOPT_PROXY, &szProxy[sizeof("http://") - 1]);225 if (CURL_FAIL ED(rcCurl))256 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXY, &szProxy[sizeof("http://") - 1]); 257 if (CURL_FAILURE(rcCurl)) 226 258 return VERR_INVALID_PARAMETER; 227 259 } … … 237 269 const char *pcszProxyUser, const char *pcszProxyPwd) 238 270 { 239 PRTHTTPINTERNAL p HttpInt= hHttp;240 RTHTTP_VALID_RETURN(p HttpInt);271 PRTHTTPINTERNAL pThis = hHttp; 272 RTHTTP_VALID_RETURN(pThis); 241 273 AssertPtrReturn(pcszProxy, VERR_INVALID_PARAMETER); 242 274 243 int rcCurl = curl_easy_setopt(p HttpInt->pCurl, CURLOPT_PROXY, pcszProxy);244 if (CURL_FAIL ED(rcCurl))275 int rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXY, pcszProxy); 276 if (CURL_FAILURE(rcCurl)) 245 277 return VERR_INVALID_PARAMETER; 246 278 247 279 if (uPort != 0) 248 280 { 249 rcCurl = curl_easy_setopt(p HttpInt->pCurl, CURLOPT_PROXYPORT, (long)uPort);250 if (CURL_FAIL ED(rcCurl))281 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPORT, (long)uPort); 282 if (CURL_FAILURE(rcCurl)) 251 283 return VERR_INVALID_PARAMETER; 252 284 } … … 254 286 if (pcszProxyUser && pcszProxyPwd) 255 287 { 256 rcCurl = curl_easy_setopt(p HttpInt->pCurl, CURLOPT_PROXYUSERNAME, pcszProxyUser);257 if (CURL_FAIL ED(rcCurl))288 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYUSERNAME, pcszProxyUser); 289 if (CURL_FAILURE(rcCurl)) 258 290 return VERR_INVALID_PARAMETER; 259 291 260 rcCurl = curl_easy_setopt(p HttpInt->pCurl, CURLOPT_PROXYPASSWORD, pcszProxyPwd);261 if (CURL_FAIL ED(rcCurl))292 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPASSWORD, pcszProxyPwd); 293 if (CURL_FAILURE(rcCurl)) 262 294 return VERR_INVALID_PARAMETER; 263 295 } … … 269 301 RTR3DECL(int) RTHttpSetHeaders(RTHTTP hHttp, size_t cHeaders, const char * const *papszHeaders) 270 302 { 271 PRTHTTPINTERNAL p HttpInt= hHttp;272 RTHTTP_VALID_RETURN(p HttpInt);303 PRTHTTPINTERNAL pThis = hHttp; 304 RTHTTP_VALID_RETURN(pThis); 273 305 274 306 if (!cHeaders) 275 307 { 276 if (p HttpInt->pHeaders)277 curl_slist_free_all(p HttpInt->pHeaders);278 p HttpInt->pHeaders = 0;308 if (pThis->pHeaders) 309 curl_slist_free_all(pThis->pHeaders); 310 pThis->pHeaders = 0; 279 311 return VINF_SUCCESS; 280 312 } … … 284 316 pHeaders = curl_slist_append(pHeaders, papszHeaders[i]); 285 317 286 p HttpInt->pHeaders = pHeaders;287 int rcCurl = curl_easy_setopt(p HttpInt->pCurl, CURLOPT_HTTPHEADER, pHeaders);288 if (CURL_FAIL ED(rcCurl))318 pThis->pHeaders = pHeaders; 319 int rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HTTPHEADER, pHeaders); 320 if (CURL_FAILURE(rcCurl)) 289 321 return VERR_INVALID_PARAMETER; 290 322 … … 293 325 294 326 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 */ 332 static 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 347 RTR3DECL(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); 306 357 return VINF_SUCCESS; 307 358 } 359 360 361 RTR3DECL(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 416 RTR3DECL(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 464 RTR3DECL(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 308 478 309 479 … … 312 482 * 313 483 * @returns IPRT status code. 314 * @param p HttpInt HTTPinstance.315 * @param rcCurl 316 */ 317 static int rtHttpGetCalcStatus(PRTHTTPINTERNAL p HttpInt, int rcCurl)484 * @param pThis The HTTP/HTTPS client instance. 485 * @param rcCurl What curl returned. 486 */ 487 static int rtHttpGetCalcStatus(PRTHTTPINTERNAL pThis, int rcCurl) 318 488 { 319 489 int rc = VERR_INTERNAL_ERROR; 320 490 321 if (p HttpInt->pszRedirLocation)322 { 323 RTStrFree(p HttpInt->pszRedirLocation);324 p HttpInt->pszRedirLocation = NULL;491 if (pThis->pszRedirLocation) 492 { 493 RTStrFree(pThis->pszRedirLocation); 494 pThis->pszRedirLocation = NULL; 325 495 } 326 496 if (rcCurl == CURLE_OK) 327 497 { 328 curl_easy_getinfo(p HttpInt->pCurl, CURLINFO_RESPONSE_CODE, &pHttpInt->lLastResp);329 switch (p HttpInt->lLastResp)498 curl_easy_getinfo(pThis->pCurl, CURLINFO_RESPONSE_CODE, &pThis->lLastResp); 499 switch (pThis->lLastResp) 330 500 { 331 501 case 200: … … 338 508 { 339 509 const char *pszRedirect; 340 curl_easy_getinfo(p HttpInt->pCurl, CURLINFO_REDIRECT_URL, &pszRedirect);510 curl_easy_getinfo(pThis->pCurl, CURLINFO_REDIRECT_URL, &pszRedirect); 341 511 size_t cb = strlen(pszRedirect); 342 512 if (cb > 0 && cb < 2048) 343 p HttpInt->pszRedirLocation = RTStrDup(pszRedirect);513 pThis->pszRedirLocation = RTStrDup(pszRedirect); 344 514 rc = VERR_HTTP_REDIRECTED; 345 515 break; … … 399 569 400 570 /** 571 * cURL callback for reporting progress, we use it for checking for abort. 572 */ 573 static 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 */ 590 static 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 */ 603 static 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 */ 656 static 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 /** 401 715 * Internal worker that performs a HTTP GET. 402 716 * 403 717 * @returns IPRT status code. 404 * @param hHttp The HTTP instance.405 * @param p cszUrlThe URL.718 * @param hHttp The HTTP/HTTPS client instance. 719 * @param pszUrl The URL. 406 720 * @param ppvResponse Where to return the pointer to the allocated 407 721 * response data (RTMemFree). There will always be … … 409 723 * is not part of the size returned via @a pcb. 410 724 * @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 */ 729 static 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); 462 755 if (RT_SUCCESS(rc)) 463 756 { 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); 475 784 return rc; 476 785 } 477 786 478 787 479 RTR3DECL(int) RTHttpGetText(RTHTTP hHttp, const char *p cszUrl, char **ppszNotUtf8)788 RTR3DECL(int) RTHttpGetText(RTHTTP hHttp, const char *pszUrl, char **ppszNotUtf8) 480 789 { 481 790 uint8_t *pv; 482 791 size_t cb; 483 int rc = rtHttpGet (hHttp, pcszUrl, &pv, &cb);792 int rc = rtHttpGetToMem(hHttp, pszUrl, &pv, &cb); 484 793 if (RT_SUCCESS(rc)) 485 794 { … … 501 810 502 811 503 RTR3DECL(int) RTHttpGetBinary(RTHTTP hHttp, const char *p cszUrl, void **ppvResponse, size_t *pcb)504 { 505 return rtHttpGet (hHttp, pcszUrl, (uint8_t **)ppvResponse, pcb);812 RTR3DECL(int) RTHttpGetBinary(RTHTTP hHttp, const char *pszUrl, void **ppvResponse, size_t *pcb) 813 { 814 return rtHttpGetToMem(hHttp, pszUrl, (uint8_t **)ppvResponse, pcb); 506 815 } 507 816 … … 513 822 514 823 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 */ 827 static size_t rtHttpWriteDataToFile(void *pvBuf, size_t cbUnit, size_t cUnits, void *pvUser) 828 { 829 PRTHTTPINTERNAL pThis = (PRTHTTPINTERNAL)pvUser; 520 830 size_t cbWritten = 0; 521 int rc = RTFileWrite( hFile, pvBuf, cbAll, &cbWritten);831 int rc = RTFileWrite(pThis->Output.hFile, pvBuf, cbUnit * cUnits, &cbWritten); 522 832 if (RT_SUCCESS(rc)) 523 833 return cbWritten; 834 pThis->rcOutput = rc; 524 835 return 0; 525 836 } … … 528 839 RTR3DECL(int) RTHttpGetFile(RTHTTP hHttp, const char *pszUrl, const char *pszDstFile) 529 840 { 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); 576 858 if (RT_SUCCESS(rc)) 577 859 { 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)) 580 865 { 581 866 /* 582 * Perform the request.867 * Open the output file. 583 868 */ 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; 586 885 } 587 886 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); 595 891 return rc; 596 892 }
Note:
See TracChangeset
for help on using the changeset viewer.