/* =============== CL_FinishHTTPDownload A download finished, find out what it was, whether there were any errors and if so, how severe. If none, rename file and other such stuff. =============== */ static void CL_FinishHTTPDownload (void) { size_t i; int msgs_in_queue; CURLMsg *msg; CURLcode result; dlhandle_t *dl; CURL *curl; long responseCode; double timeTaken; double fileSize; char tempName[MAX_OSPATH]; qboolean isFile; do { msg = curl_multi_info_read (multi, &msgs_in_queue); if (!msg) { Com_Printf ("CL_FinishHTTPDownload: Odd, no message for us...\n"); return; } if (msg->msg != CURLMSG_DONE) { Com_Printf ("CL_FinishHTTPDownload: Got some weird message...\n"); continue; } curl = msg->easy_handle; // curl doesn't provide reverse-lookup of the void * ptr, so search for it for (i = 0; i < 4; i++) { if (cls.HTTPHandles[i].curl == curl) { dl = &cls.HTTPHandles[i]; break; } } if (i == 4) Com_Error (ERR_DROP, "CL_FinishHTTPDownload: Handle not found"); // we mark everything as done even if it errored to prevent multiple // attempts. dl->queueEntry->state = DLQ_STATE_DONE; //filelist processing is done on read if (dl->file) isFile = true; else isFile = false; if (isFile) { fclose (dl->file); dl->file = NULL; } //might be aborted if (pendingCount) pendingCount--; handleCount--; //Com_Printf ("finished dl: hc = %d\n", LOG_GENERAL, handleCount); cls.downloadname[0] = 0; cls.downloadposition = 0; result = msg->data.result; switch (result) { // for some reason curl returns CURLE_OK for a 404... case CURLE_HTTP_RETURNED_ERROR: case CURLE_OK: curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &responseCode); if (responseCode == 404) { i = strlen (dl->queueEntry->quakePath); if ( !strcmp (dl->queueEntry->quakePath + i - 4, ".pak") || !strcmp (dl->queueEntry->quakePath + i - 4, ".pk3") ) downloading_pak = false; if (isFile) remove (dl->filePath); Com_Printf ("HTTP(%s): 404 File Not Found [%d remaining files]\n", dl->queueEntry->quakePath, pendingCount); curl_easy_getinfo (curl, CURLINFO_SIZE_DOWNLOAD, &fileSize); if (fileSize > 512) { // ick isFile = false; result = CURLE_FILESIZE_EXCEEDED; Com_Printf ("Oversized 404 body received (%d bytes), aborting HTTP downloading.\n", (int)fileSize); } else { curl_multi_remove_handle (multi, dl->curl); // Knightmare- fall back to UDP download for this map if failure on .bsp if ( !strncmp(dl->queueEntry->quakePath, "maps/", 5) && !strcmp(dl->queueEntry->quakePath + i - 4, ".bsp") ) { Com_Printf ("HTTP: failed to download %s, falling back to UDP until next map.\n", dl->queueEntry->quakePath); thisMapAbort = true; CL_CancelHTTPDownloads (false); CL_ResetPrecacheCheck (); } // end Knightmare continue; } } else if (responseCode == 200) { if (!isFile && !abortDownloads) CL_ParseFileList (dl); break; } //every other code is treated as fatal, fallthrough here Com_Printf ("Bad HTTP response code %d for %s, aborting HTTP downloading.\n", responseCode, dl->queueEntry->quakePath); //fatal error, disable http case CURLE_COULDNT_RESOLVE_HOST: case CURLE_COULDNT_CONNECT: case CURLE_COULDNT_RESOLVE_PROXY: if (isFile) remove (dl->filePath); // Com_Printf ("Fatal HTTP error: %s\n", curl_easy_strerror (result)); Com_Printf ("Fatal HTTP error: %s\n", CURL_ERROR(result)); curl_multi_remove_handle (multi, dl->curl); if (abortDownloads) continue; CL_CancelHTTPDownloads (true); continue; default: i = strlen (dl->queueEntry->quakePath); if ( !strcmp (dl->queueEntry->quakePath + i - 4, ".pak") || !strcmp (dl->queueEntry->quakePath + i - 4, ".pk3") ) downloading_pak = false; if (isFile) remove (dl->filePath); // Com_Printf ("HTTP download failed: %s\n", curl_easy_strerror (result)); Com_Printf ("HTTP download failed: %s\n", CURL_ERROR(result)); curl_multi_remove_handle (multi, dl->curl); continue; } if (isFile) { //rename the temp file Com_sprintf (tempName, sizeof(tempName), "%s/%s", FS_Gamedir(), dl->queueEntry->quakePath); if (rename (dl->filePath, tempName)) Com_Printf ("Failed to rename %s for some odd reason...", dl->filePath); //a pak file is very special... i = strlen (tempName); if ( !strcmp (tempName + i - 4, ".pak") || !strcmp (tempName + i - 4, ".pk3") ) { // FS_FlushCache (); // FS_ReloadPAKs (); // Knightmare- just add the pk3/ pak file if (!strcmp (tempName + i - 4, ".pk3")) FS_AddPK3File (tempName); else FS_AddPAKFile (tempName); CL_ReVerifyHTTPQueue (); downloading_pak = false; } } //show some stats curl_easy_getinfo (curl, CURLINFO_TOTAL_TIME, &timeTaken); curl_easy_getinfo (curl, CURLINFO_SIZE_DOWNLOAD, &fileSize); //FIXME: //technically i shouldn't need to do this as curl will auto reuse the //existing handle when you change the URL. however, the handleCount goes //all weird when reusing a download slot in this way. if you can figure //out why, please let me know. curl_multi_remove_handle (multi, dl->curl); Com_Printf ("HTTP(%s): %.f bytes, %.2fkB/sec [%d remaining files]\n", dl->queueEntry->quakePath, fileSize, (fileSize / 1024.0) / timeTaken, pendingCount); } while (msgs_in_queue > 0); // FS_FlushCache (); if (handleCount == 0) { if (abortDownloads == HTTPDL_ABORT_SOFT) abortDownloads = HTTPDL_ABORT_NONE; else if (abortDownloads == HTTPDL_ABORT_HARD) { // FS: Added because Whale's Weapons HTTP server rejects you after a lot of 404s. Then you lose HTTP until a hard reconnect. Q_strncpyz(cls.downloadServerRetry, cls.downloadServer, sizeof(cls.downloadServerRetry)); cls.downloadServer[0] = 0; } } // done current batch, see if we have more to dl - maybe a .bsp needs downloaded if (cls.state == ca_connected && !CL_PendingHTTPDownloads()) CL_RequestNextDownload (); }
/* ===================== CL_ParseDownload A download message has been received from the server ===================== */ void CL_ParseDownload (void) { int32_t size, percent; char name[MAX_OSPATH]; int32_t r;//, i; // read the data size = MSG_ReadShort (&net_message); percent = MSG_ReadByte (&net_message); if (size == -1) { Com_Printf ("Server does not have this file.\n"); if (cls.downloadname) // Knightmare- save name of failed download CL_AddToFailedDownloadList (cls.downloadname); if (cls.download) { // if here, we tried to resume a file but the server said no fclose (cls.download); cls.download = NULL; } CL_RequestNextDownload (); return; } // open the file if not opened yet if (!cls.download) { CL_DownloadFileName(name, sizeof(name), cls.downloadtempname); FS_CreatePath (name); cls.download = fopen (name, "wb"); if (!cls.download) { net_message.readcount += size; Com_Printf ("Failed to open %s\n", cls.downloadtempname); CL_RequestNextDownload (); return; } } fwrite (net_message.data + net_message.readcount, 1, size, cls.download); net_message.readcount += size; if (percent != 100) { // request next block // change display routines by zoid #if 0 Com_Printf ("."); if (10*(percent/10) != cls.downloadpercent) { cls.downloadpercent = 10*(percent/10); Com_Printf ("%i%%", cls.downloadpercent); } #endif cls.downloadpercent = percent; MSG_WriteByte (&cls.netchan.message, clc_stringcmd); SZ_Print (&cls.netchan.message, "nextdl"); } else { char oldn[MAX_OSPATH]; char newn[MAX_OSPATH]; // Com_Printf ("100%%\n"); fclose (cls.download); // rename the temp file to it's final name CL_DownloadFileName(oldn, sizeof(oldn), cls.downloadtempname); CL_DownloadFileName(newn, sizeof(newn), cls.downloadname); r = rename (oldn, newn); if (r) Com_Printf ("failed to rename.\n"); cls.download = NULL; cls.downloadpercent = 0; // add new pk3s to search paths, hack by Jay Dolan if (strstr(newn, ".pk3")) FS_AddPK3File (newn); // get another file if needed CL_RequestNextDownload (); } }