void wswcurl_init( void ) { if( wswcurl_mempool ) { return; } wswcurl_mempool = Mem_AllocPool( NULL, "Curl" ); // HTTP proxy settings http_proxy = Cvar_Get( "http_proxy", "", CVAR_ARCHIVE ); http_proxyuserpwd = Cvar_Get( "http_proxyuserpwd", "", CVAR_ARCHIVE ); wswcurl_loadlib(); if( curlLibrary ) { qcurl_global_init( CURL_GLOBAL_ALL ); curldummy = qcurl_easy_init(); curlmulti = qcurl_multi_init(); } curldummy_mutex = QMutex_Create(); http_requests_mutex = QMutex_Create(); #ifdef USE_OPENSSL if( cryptoLibrary ) { int mutex_num; crypto_num_mutexes = qCRYPTO_num_locks(); crypto_mutexes = WMALLOC( crypto_num_mutexes * sizeof( *crypto_mutexes ) ); for( mutex_num = 0; mutex_num < crypto_num_mutexes; mutex_num++ ) crypto_mutexes[mutex_num] = QMutex_Create(); qCRYPTO_set_locking_callback( wswcurl_crypto_lockcallback ); } #endif }
/* ==================== CheckPendingDownloads checks if there are free download slots to start new downloads in. To not start too many downloads at once, only one download is added at a time, up to a maximum number of cl_curl_maxdownloads are running. ==================== */ static void CheckPendingDownloads(void) { const char *h; if(!curl_dll) return; if(numdownloads < cl_curl_maxdownloads.integer) { downloadinfo *di; for(di = downloads; di; di = di->next) { if(!di->started) { if(!di->buffer) { Con_Printf("Downloading %s -> %s", CleanURL(di->url), di->filename); di->stream = FS_OpenRealFile(di->filename, "ab", false); if(!di->stream) { Con_Printf("\nFAILED: Could not open output file %s\n", di->filename); Curl_EndDownload(di, CURL_DOWNLOAD_FAILED, CURLE_OK); return; } FS_Seek(di->stream, 0, SEEK_END); di->startpos = FS_Tell(di->stream); if(di->startpos > 0) Con_Printf(", resuming from position %ld", (long) di->startpos); Con_Print("...\n"); } else { Con_DPrintf("Downloading %s -> memory\n", CleanURL(di->url)); di->startpos = 0; } di->curle = qcurl_easy_init(); di->slist = NULL; qcurl_easy_setopt(di->curle, CURLOPT_URL, di->url); qcurl_easy_setopt(di->curle, CURLOPT_USERAGENT, engineversion); qcurl_easy_setopt(di->curle, CURLOPT_REFERER, di->referer); qcurl_easy_setopt(di->curle, CURLOPT_RESUME_FROM, (long) di->startpos); qcurl_easy_setopt(di->curle, CURLOPT_FOLLOWLOCATION, 1); qcurl_easy_setopt(di->curle, CURLOPT_WRITEFUNCTION, CURL_fwrite); qcurl_easy_setopt(di->curle, CURLOPT_LOW_SPEED_LIMIT, (long) 256); qcurl_easy_setopt(di->curle, CURLOPT_LOW_SPEED_TIME, (long) 45); qcurl_easy_setopt(di->curle, CURLOPT_WRITEDATA, (void *) di); qcurl_easy_setopt(di->curle, CURLOPT_PRIVATE, (void *) di); qcurl_easy_setopt(di->curle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP); if(qcurl_easy_setopt(di->curle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP) != CURLE_OK) { Con_Printf("^1WARNING:^7 for security reasons, please upgrade to libcurl 7.19.4 or above. In a later version of DarkPlaces, HTTP redirect support will be disabled for this libcurl version.\n"); //qcurl_easy_setopt(di->curle, CURLOPT_FOLLOWLOCATION, 0); } if(di->post_content_type) { qcurl_easy_setopt(di->curle, CURLOPT_POST, 1); qcurl_easy_setopt(di->curle, CURLOPT_POSTFIELDS, di->postbuf); qcurl_easy_setopt(di->curle, CURLOPT_POSTFIELDSIZE, di->postbufsize); di->slist = qcurl_slist_append(di->slist, va("Content-Type: %s", di->post_content_type)); } // parse extra headers into slist // \n separated list! h = di->extraheaders; while(h) { const char *hh = strchr(h, '\n'); if(hh) { char *buf = (char *) Mem_Alloc(tempmempool, hh - h + 1); memcpy(buf, h, hh - h); buf[hh - h] = 0; di->slist = qcurl_slist_append(di->slist, buf); h = hh + 1; } else { di->slist = qcurl_slist_append(di->slist, h); h = NULL; } } qcurl_easy_setopt(di->curle, CURLOPT_HTTPHEADER, di->slist); qcurl_multi_add_handle(curlm, di->curle); di->started = true; ++numdownloads; if(numdownloads >= cl_curl_maxdownloads.integer) break; } } } }
void CL_cURL_BeginDownload( const char *localName, const char *remoteURL ) { clc.cURLUsed = qtrue; Com_Printf("URL: %s\n", remoteURL); Com_DPrintf("***** CL_cURL_BeginDownload *****\n" "Localname: %s\n" "RemoteURL: %s\n" "****************************\n", localName, remoteURL); CL_cURL_Cleanup(); Q_strncpyz(clc.downloadURL, remoteURL, sizeof(clc.downloadURL)); Q_strncpyz(clc.downloadName, localName, sizeof(clc.downloadName)); Com_sprintf(clc.downloadTempName, sizeof(clc.downloadTempName), "%s.tmp", localName); // Set so UI gets access to it Cvar_Set("cl_downloadName", localName); Cvar_Set("cl_downloadSize", "0"); Cvar_Set("cl_downloadCount", "0"); Cvar_SetValue("cl_downloadTime", cls.realtime); clc.downloadBlock = 0; // Starting new file clc.downloadCount = 0; clc.downloadCURL = qcurl_easy_init(); if(!clc.downloadCURL) { Com_Error(ERR_DROP, "CL_cURL_BeginDownload: qcurl_easy_init() " "failed\n"); return; } clc.download = FS_SV_FOpenFileWrite(clc.downloadTempName); if(!clc.download) { Com_Error(ERR_DROP, "CL_cURL_BeginDownload: failed to open " "%s for writing\n", clc.downloadTempName); return; } qcurl_easy_setopt(clc.downloadCURL, CURLOPT_WRITEDATA, clc.download); if(com_developer->integer) qcurl_easy_setopt(clc.downloadCURL, CURLOPT_VERBOSE, 1); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_URL, clc.downloadURL); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_TRANSFERTEXT, 0); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_REFERER, va("ioQ3://%s", NET_AdrToString(clc.serverAddress))); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_USERAGENT, va("%s %s", Q3_VERSION, qcurl_version())); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_WRITEFUNCTION, CL_cURL_CallbackWrite); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_WRITEDATA, &clc.download); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_NOPROGRESS, 0); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_PROGRESSFUNCTION, CL_cURL_CallbackProgress); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_PROGRESSDATA, NULL); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_FAILONERROR, 1); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_FOLLOWLOCATION, 1); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_MAXREDIRS, 5); clc.downloadCURLM = qcurl_multi_init(); if(!clc.downloadCURLM) { qcurl_easy_cleanup(clc.downloadCURL); clc.downloadCURL = NULL; Com_Error(ERR_DROP, "CL_cURL_BeginDownload: qcurl_multi_init() " "failed\n"); return; } qcurl_multi_add_handle(clc.downloadCURLM, clc.downloadCURL); if(!(clc.sv_allowDownload & DLF_NO_DISCONNECT) && !clc.cURLDisconnected) { CL_AddReliableCommand("disconnect"); CL_WritePacket(); CL_WritePacket(); CL_WritePacket(); clc.cURLDisconnected = qtrue; } }
wswcurl_req *wswcurl_create( const char *iface, const char *furl, ... ) { wswcurl_req *retreq; CURL *curl; CURLcode res; char url[4 * 1024]; // 4kb url buffer? va_list arg; const char *proxy = http_proxy->string; const char *proxy_userpwd = http_proxyuserpwd->string; if( !curlLibrary ) { Com_Printf( "!!! WARNING: external library is missing (libcurl).\n" ); return NULL; } // Prepare url formatting with variable arguments va_start( arg, furl ); Q_vsnprintfz( url, sizeof( url ), furl, arg ); va_end( arg ); // Initialize structure if( !( curl = qcurl_easy_init() ) ) { return NULL; } // allocate, copy retreq = ( wswcurl_req * )WMALLOC( sizeof( wswcurl_req ) ); memset( retreq, 0, sizeof( *retreq ) ); retreq->curl = curl; retreq->url = ( char* )WMALLOC( strlen( url ) + 1 ); memcpy( retreq->url, url, strlen( url ) + 1 ); CURLSETOPT( curl, res, CURLOPT_URL, retreq->url ); CURLSETOPT( curl, res, CURLOPT_WRITEFUNCTION, wswcurl_write ); CURLSETOPT( curl, res, CURLOPT_NOPROGRESS, 1 ); CURLSETOPT( curl, res, CURLOPT_FOLLOWLOCATION, 1 ); CURLSETOPT( curl, res, CURLOPT_HEADERFUNCTION, wswcurl_readheader ); CURLSETOPT( curl, res, CURLOPT_CONNECTTIMEOUT, WCONNECTTIMEOUT ); #if defined( APPLICATION ) && defined( APP_VERSION_STR ) && defined( OSNAME ) && defined( CPUSTRING ) CURLSETOPT( curl, res, CURLOPT_USERAGENT, APPLICATION "/"APP_VERSION_STR " (compatible; N; "OSNAME "; "CPUSTRING ")" ); #endif CURLSETOPT( curl, res, CURLOPT_WRITEDATA, ( void * )retreq ); CURLSETOPT( curl, res, CURLOPT_WRITEHEADER, ( void * )retreq ); CURLSETOPT( curl, res, CURLOPT_PRIVATE, ( void * )retreq ); if( iface && *iface ) { CURLSETOPT( curl, res, CURLOPT_INTERFACE, ( void * )iface ); } CURLSETOPT( curl, res, CURLOPT_NOSIGNAL, 1 ); if( developer->integer ) { CURLSETOPT( curl, res, CURLOPT_DEBUGFUNCTION, &wswcurl_debug_callback ); CURLSETOPT( curl, res, CURLOPT_DEBUGDATA, ( void * )retreq ); CURLSETOPT( curl, res, CURLOPT_VERBOSE, 1 ); } // HTTP proxy settings if( proxy && *proxy ) { CURLSETOPT( curl, res, CURLOPT_PROXYTYPE, CURLPROXY_HTTP ); CURLSETOPT( curl, res, CURLOPT_PROXY, proxy ); if( proxy_userpwd && *proxy_userpwd ) { CURLSETOPT( curl, res, CURLOPT_PROXYUSERPWD, proxy_userpwd ); } } wswcurl_set_timeout( retreq, WTIMEOUT ); // link QMutex_Lock( http_requests_mutex ); retreq->prev = NULL; retreq->next = http_requests; if( retreq->next ) { retreq->next->prev = retreq; } else { http_requests_hnode = retreq; } http_requests = retreq; CURLDBG( ( va( " CURL CREATE %s\n", url ) ) ); QMutex_Unlock( http_requests_mutex ); return retreq; }