cl_http.c

Go to the documentation of this file.
00001 
00014 /*
00015 Copyright (C) 1997-2001 Id Software, Inc.
00016 
00017 This program is free software; you can redistribute it and/or
00018 modify it under the terms of the GNU General Public License
00019 as published by the Free Software Foundation; either version 2
00020 of the License, or (at your option) any later version.
00021 
00022 This program is distributed in the hope that it will be useful,
00023 but WITHOUT ANY WARRANTY; without even the implied warranty of
00024 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00025 
00026 See the GNU General Public License for more details.
00027 
00028 You should have received a copy of the GNU General Public License
00029 along with this program; if not, write to the Free Software
00030 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00031 
00032 */
00033 
00034 #include "client.h"
00035 #include "cl_http.h"
00036 #include "battlescape/cl_parse.h"
00037 
00038 static cvar_t *cl_http_downloads;
00039 static cvar_t *cl_http_filelists;
00040 static cvar_t *cl_http_max_connections;
00041 
00042 enum {
00043     HTTPDL_ABORT_NONE,
00044     HTTPDL_ABORT_SOFT,
00045     HTTPDL_ABORT_HARD
00046 };
00047 
00048 static CURLM    *multi = NULL;
00049 static int      handleCount = 0;
00050 static int      pendingCount = 0;
00051 static int      abortDownloads = HTTPDL_ABORT_NONE;
00052 static qboolean downloadingPK3 = qfalse;
00053 
00054 static void StripHighBits (char *string)
00055 {
00056     char *p = string;
00057 
00058     while (string[0]) {
00059         const unsigned char c = *(string++);
00060 
00061         if (c >= 32 && c <= 127)
00062             *p++ = c;
00063     }
00064 
00065     p[0] = '\0';
00066 }
00067 
00068 static inline qboolean isvalidchar (int c)
00069 {
00070     if (!isalnum(c) && c != '_' && c != '-')
00071         return qfalse;
00072     return qtrue;
00073 }
00074 
00079 static int CL_HTTP_Progress (void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
00080 {
00081     dlhandle_t *dl;
00082 
00083     dl = (dlhandle_t *)clientp;
00084 
00085     dl->position = (unsigned)dlnow;
00086 
00087     /* don't care which download shows as long as something does :) */
00088     if (!abortDownloads) {
00089         Q_strncpyz(cls.downloadName, dl->queueEntry->ufoPath, sizeof(cls.downloadName));
00090         cls.downloadPosition = dl->position;
00091 
00092         if (dltotal)
00093             cls.downloadPercent = (int)((dlnow / dltotal) * 100.0f);
00094         else
00095             cls.downloadPercent = 0;
00096     }
00097 
00098     return abortDownloads;
00099 }
00100 
00106 static void CL_EscapeHTTPPath (const char *filePath, char *escaped)
00107 {
00108     int     i;
00109     size_t  len;
00110     char    *p;
00111 
00112     p = escaped;
00113 
00114     len = strlen(filePath);
00115     for (i = 0; i < len; i++) {
00116         if (!isalnum(filePath[i]) && filePath[i] != ';' && filePath[i] != '/' &&
00117             filePath[i] != '?' && filePath[i] != ':' && filePath[i] != '@' && filePath[i] != '&' &&
00118             filePath[i] != '=' && filePath[i] != '+' && filePath[i] != '$' && filePath[i] != ',' &&
00119             filePath[i] != '[' && filePath[i] != ']' && filePath[i] != '-' && filePath[i] != '_' &&
00120             filePath[i] != '.' && filePath[i] != '!' && filePath[i] != '~' && filePath[i] != '*' &&
00121             filePath[i] != '\'' && filePath[i] != '(' && filePath[i] != ')') {
00122             sprintf(p, "%%%02x", filePath[i]);
00123             p += 3;
00124         } else {
00125             *p = filePath[i];
00126             p++;
00127         }
00128     }
00129     p[0] = 0;
00130 
00131     /* using ./ in a url is legal, but all browsers condense the path and some IDS / request */
00132     /* filtering systems act a bit funky if http requests come in with uncondensed paths. */
00133     len = strlen(escaped);
00134     p = escaped;
00135     while ((p = strstr (p, "./"))) {
00136         memmove(p, p + 2, len - (p - escaped) - 1);
00137         len -= 2;
00138     }
00139 }
00140 
00144 static void CL_StartHTTPDownload (dlqueue_t *entry, dlhandle_t *dl)
00145 {
00146     char tempFile[MAX_OSPATH];
00147     char escapedFilePath[MAX_QPATH * 4];
00148     const char *extension = Com_GetExtension(entry->ufoPath);
00149 
00150     /* yet another hack to accomodate filelists, how i wish i could push :(
00151      * NULL file handle indicates filelist. */
00152     if (extension != NULL && !strcmp(extension, "filelist")) {
00153         dl->file = NULL;
00154         CL_EscapeHTTPPath(entry->ufoPath, escapedFilePath);
00155     } else {
00157         Com_sprintf(dl->filePath, sizeof(dl->filePath), "%s/%s", FS_Gamedir(), entry->ufoPath);
00158 
00159         Com_sprintf(tempFile, sizeof(tempFile), BASEDIRNAME"/%s", entry->ufoPath);
00160         CL_EscapeHTTPPath(tempFile, escapedFilePath);
00161 
00162         strcat(dl->filePath, ".tmp");
00163 
00164         FS_CreatePath(dl->filePath);
00165 
00166         /* don't bother with http resume... too annoying if server doesn't support it. */
00167         dl->file = fopen(dl->filePath, "wb");
00168         if (!dl->file) {
00169             Com_Printf("CL_StartHTTPDownload: Couldn't open %s for writing.\n", dl->filePath);
00170             entry->state = DLQ_STATE_DONE;
00171             /* CL_RemoveHTTPDownload(entry->ufoPath); */
00172             return;
00173         }
00174     }
00175 
00176     dl->tempBuffer = NULL;
00177     dl->speed = 0;
00178     dl->fileSize = 0;
00179     dl->position = 0;
00180     dl->queueEntry = entry;
00181 
00182     if (!dl->curl)
00183         dl->curl = curl_easy_init();
00184 
00185     Com_sprintf(dl->URL, sizeof(dl->URL), "%s%s", cls.downloadServer, escapedFilePath);
00186 
00187     curl_easy_setopt(dl->curl, CURLOPT_ENCODING, "");
00188 #ifdef PARANOID
00189     curl_easy_setopt(dl->curl, CURLOPT_VERBOSE, 1);
00190 #endif
00191     curl_easy_setopt(dl->curl, CURLOPT_NOPROGRESS, 0);
00192     if (dl->file) {
00193         curl_easy_setopt(dl->curl, CURLOPT_WRITEDATA, dl->file);
00194         curl_easy_setopt(dl->curl, CURLOPT_WRITEFUNCTION, NULL);
00195     } else {
00196         curl_easy_setopt(dl->curl, CURLOPT_WRITEDATA, dl);
00197         curl_easy_setopt(dl->curl, CURLOPT_WRITEFUNCTION, HTTP_Recv);
00198     }
00199     curl_easy_setopt(dl->curl, CURLOPT_PROXY, http_proxy->string);
00200     curl_easy_setopt(dl->curl, CURLOPT_FOLLOWLOCATION, 1);
00201     curl_easy_setopt(dl->curl, CURLOPT_MAXREDIRS, 5);
00202     curl_easy_setopt(dl->curl, CURLOPT_WRITEHEADER, dl);
00203     curl_easy_setopt(dl->curl, CURLOPT_HEADERFUNCTION, HTTP_Header);
00204     curl_easy_setopt(dl->curl, CURLOPT_PROGRESSFUNCTION, CL_HTTP_Progress);
00205     curl_easy_setopt(dl->curl, CURLOPT_PROGRESSDATA, dl);
00206     curl_easy_setopt(dl->curl, CURLOPT_USERAGENT, Cvar_GetString("version"));
00207     curl_easy_setopt(dl->curl, CURLOPT_REFERER, cls.downloadReferer);
00208     curl_easy_setopt(dl->curl, CURLOPT_URL, dl->URL);
00209 
00210     if (curl_multi_add_handle(multi, dl->curl) != CURLM_OK) {
00211         Com_Printf("curl_multi_add_handle: error\n");
00212         dl->queueEntry->state = DLQ_STATE_DONE;
00213         return;
00214     }
00215 
00216     handleCount++;
00217     /*Com_Printf("started dl: hc = %d\n", handleCount); */
00218     Com_Printf("CL_StartHTTPDownload: Fetching %s...\n", dl->URL);
00219     dl->queueEntry->state = DLQ_STATE_RUNNING;
00220 }
00221 
00225 void CL_SetHTTPServer (const char *URL)
00226 {
00227     dlqueue_t *q, *last;
00228 
00229     CL_HTTP_Cleanup();
00230 
00231     q = &cls.downloadQueue;
00232 
00233     last = NULL;
00234 
00235     while (q->next) {
00236         q = q->next;
00237 
00238         if (last)
00239             Mem_Free(last);
00240 
00241         last = q;
00242     }
00243 
00244     if (last)
00245         Mem_Free(last);
00246 
00247     if (multi)
00248         Com_Error(ERR_DROP, "CL_SetHTTPServer: Still have old handle");
00249 
00250     multi = curl_multi_init();
00251 
00252     memset(&cls.downloadQueue, 0, sizeof(cls.downloadQueue));
00253 
00254     abortDownloads = HTTPDL_ABORT_NONE;
00255     handleCount = pendingCount = 0;
00256 
00257     Q_strncpyz(cls.downloadServer, URL, sizeof(cls.downloadServer));
00258 }
00259 
00260 void CL_ResetPrecacheCheck(void);
00261 
00265 void CL_CancelHTTPDownloads (qboolean permKill)
00266 {
00267     dlqueue_t   *q;
00268 
00269     if (permKill)
00270         abortDownloads = HTTPDL_ABORT_HARD;
00271     else
00272         abortDownloads = HTTPDL_ABORT_SOFT;
00273 
00274     q = &cls.downloadQueue;
00275 
00276     while (q->next) {
00277         q = q->next;
00278         if (q->state == DLQ_STATE_NOT_STARTED)
00279             q->state = DLQ_STATE_DONE;
00280     }
00281 
00282     if (!pendingCount && !handleCount && abortDownloads == HTTPDL_ABORT_HARD)
00283         cls.downloadServer[0] = 0;
00284 
00285     pendingCount = 0;
00286 }
00287 
00291 static dlhandle_t *CL_GetFreeDLHandle (void)
00292 {
00293     int i;
00294 
00295     for (i = 0; i < 4; i++) {
00296         dlhandle_t *dl = &cls.HTTPHandles[i];
00297         if (!dl->queueEntry || dl->queueEntry->state == DLQ_STATE_DONE)
00298             return dl;
00299     }
00300 
00301     return NULL;
00302 }
00303 
00308 qboolean CL_QueueHTTPDownload (const char *ufoPath)
00309 {
00310     dlqueue_t *q;
00311 
00312     /* no http server (or we got booted) */
00313     if (!cls.downloadServer[0] || abortDownloads || !cl_http_downloads->integer)
00314         return qfalse;
00315 
00316     q = &cls.downloadQueue;
00317 
00318     while (q->next) {
00319         q = q->next;
00320 
00321         /* avoid sending duplicate requests */
00322         if (!strcmp(ufoPath, q->ufoPath))
00323             return qtrue;
00324     }
00325 
00326     q->next = Mem_Alloc(sizeof(*q));
00327     q = q->next;
00328 
00329     q->next = NULL;
00330     q->state = DLQ_STATE_NOT_STARTED;
00331     Q_strncpyz(q->ufoPath, ufoPath, sizeof(q->ufoPath));
00332 
00333     /* special case for map file lists */
00334     if (cl_http_filelists->integer) {
00335         const char *extension = Com_GetExtension(ufoPath);
00336         if (extension != NULL && !Q_strcasecmp(extension, "bsp")) {
00337             char listPath[MAX_OSPATH];
00338             const size_t len = strlen(ufoPath);
00339             Com_sprintf(listPath, sizeof(listPath), BASEDIRNAME"/%.*s.filelist", (int)(len - 4), ufoPath);
00340             CL_QueueHTTPDownload(listPath);
00341         }
00342     }
00343 
00344     /* if a download entry has made it this far, CL_FinishHTTPDownload is guaranteed to be called. */
00345     pendingCount++;
00346 
00347     return qtrue;
00348 }
00349 
00356 qboolean CL_PendingHTTPDownloads (void)
00357 {
00358     if (cls.downloadServer[0] == '\0')
00359         return qfalse;
00360 
00361     return pendingCount + handleCount;
00362 }
00363 
00369 qboolean CL_CheckOrDownloadFile (const char *filename)
00370 {
00371     static char lastfilename[MAX_OSPATH] = "";
00372 
00373     if (!filename || filename[0] == '\0')
00374         return qtrue;
00375 
00376     /* r1: don't attempt same file many times */
00377     if (!strcmp(filename, lastfilename))
00378         return qtrue;
00379 
00380     Q_strncpyz(lastfilename, filename, sizeof(lastfilename));
00381 
00382     if (strstr(filename, "..")) {
00383         Com_Printf("Refusing to check a path with .. (%s)\n", filename);
00384         return qtrue;
00385     }
00386 
00387     if (strchr(filename, ' ')) {
00388         Com_Printf("Refusing to check a path containing spaces (%s)\n", filename);
00389         return qtrue;
00390     }
00391 
00392     if (strchr(filename, ':')) {
00393         Com_Printf("Refusing to check a path containing a colon (%s)\n", filename);
00394         return qtrue;
00395     }
00396 
00397     if (filename[0] == '/') {
00398         Com_Printf("Refusing to check a path starting with / (%s)\n", filename);
00399         return qtrue;
00400     }
00401 
00402     if (FS_LoadFile(filename, NULL) != -1) {
00403         /* it exists, no need to download */
00404         return qtrue;
00405     }
00406 
00407     if (CL_QueueHTTPDownload(filename))
00408         return qfalse;
00409 
00410     return qtrue;
00411 }
00412 
00419 static void CL_CheckAndQueueDownload (char *path)
00420 {
00421     size_t      length;
00422     const char  *ext;
00423     qboolean    pak;
00424     qboolean    gameLocal;
00425 
00426     StripHighBits(path);
00427 
00428     length = strlen(path);
00429 
00430     if (length >= MAX_QPATH)
00431         return;
00432 
00433     ext = Com_GetExtension(path);
00434     if (ext == NULL)
00435         return;
00436 
00437     if (!strcmp(ext, "pk3")) {
00438         Com_Printf("NOTICE: Filelist is requesting a .pk3 file (%s)\n", path);
00439         pak = qtrue;
00440     } else
00441         pak = qfalse;
00442 
00443     if (!pak && strcmp(ext, "bsp") && strcmp(ext, "wav") && strcmp(ext, "md2") && strcmp(ext, "ogg") &&
00444         strcmp(ext, "md3") && strcmp(ext, "tga") && strcmp(ext, "png") && strcmp(ext, "jpg") &&
00445         strcmp(ext, "dpm") && strcmp(ext, "obj") && strcmp(ext, "mat") && strcmp(ext, "ump")) {
00446         Com_Printf("WARNING: Illegal file type '%s' in filelist.\n", path);
00447         return;
00448     }
00449 
00450     if (path[0] == '@') {
00451         if (pak) {
00452             Com_Printf("WARNING: @ prefix used on a pk3 file (%s) in filelist.\n", path);
00453             return;
00454         }
00455         gameLocal = qtrue;
00456         path++;
00457         length--;
00458     } else
00459         gameLocal = qfalse;
00460 
00461     if (strstr(path, "..") || !isvalidchar(path[0]) || !isvalidchar(path[length - 1]) || strstr(path, "//") ||
00462         strchr(path, '\\') || (!pak && !strchr(path, '/')) || (pak && strchr(path, '/'))) {
00463         Com_Printf("WARNING: Illegal path '%s' in filelist.\n", path);
00464         return;
00465     }
00466 
00467     /* by definition pk3s are game-local */
00468     if (gameLocal || pak) {
00469         qboolean exists;
00470 
00471         /* search the user homedir to find the pk3 file */
00472         if (pak) {
00473             char gamePath[MAX_OSPATH];
00474             FILE *f;
00475             Com_sprintf(gamePath, sizeof(gamePath), "%s/%s", FS_Gamedir(), path);
00476             f = fopen(gamePath, "rb");
00477             if (!f)
00478                 exists = qfalse;
00479             else {
00480                 exists = qtrue;
00481                 fclose(f);
00482             }
00483         } else
00484             exists = FS_CheckFile("%s", path);
00485 
00486         if (!exists) {
00487             if (CL_QueueHTTPDownload(path)) {
00488                 /* pk3s get bumped to the top and HTTP switches to single downloading.
00489                  * this prevents someone on 28k dialup trying to do both the main .pk3
00490                  * and referenced configstrings data at once. */
00491                 if (pak) {
00492                     dlqueue_t *q, *last;
00493 
00494                     last = q = &cls.downloadQueue;
00495 
00496                     while (q->next) {
00497                         last = q;
00498                         q = q->next;
00499                     }
00500 
00501                     last->next = NULL;
00502                     q->next = cls.downloadQueue.next;
00503                     cls.downloadQueue.next = q;
00504                 }
00505             }
00506         }
00507     } else
00508         CL_CheckOrDownloadFile(path);
00509 }
00510 
00514 static void CL_ParseFileList (dlhandle_t *dl)
00515 {
00516     char *list;
00517 
00518     if (!cl_http_filelists->integer)
00519         return;
00520 
00521     list = dl->tempBuffer;
00522 
00523     for (;;) {
00524         char *p = strchr(list, '\n');
00525         if (p) {
00526             p[0] = 0;
00527             if (list[0])
00528                 CL_CheckAndQueueDownload(list);
00529             list = p + 1;
00530         } else {
00531             if (list[0])
00532                 CL_CheckAndQueueDownload(list);
00533             break;
00534         }
00535     }
00536 
00537     Mem_Free(dl->tempBuffer);
00538     dl->tempBuffer = NULL;
00539 }
00540 
00545 static void CL_ReVerifyHTTPQueue (void)
00546 {
00547     dlqueue_t *q = &cls.downloadQueue;
00548 
00549     pendingCount = 0;
00550 
00551     while (q->next) {
00552         q = q->next;
00553         if (q->state == DLQ_STATE_NOT_STARTED) {
00554             if (FS_LoadFile(q->ufoPath, NULL) != -1)
00555                 q->state = DLQ_STATE_DONE;
00556             else
00557                 pendingCount++;
00558         }
00559     }
00560 }
00561 
00565 void CL_HTTP_Cleanup (void)
00566 {
00567     int i;
00568 
00569     for (i = 0; i < 4; i++) {
00570         dlhandle_t *dl = &cls.HTTPHandles[i];
00571 
00572         if (dl->file) {
00573             fclose(dl->file);
00574             remove(dl->filePath);
00575             dl->file = NULL;
00576         }
00577 
00578         if (dl->tempBuffer) {
00579             Mem_Free(dl->tempBuffer);
00580             dl->tempBuffer = NULL;
00581         }
00582 
00583         if (dl->curl) {
00584             if (multi)
00585                 curl_multi_remove_handle(multi, dl->curl);
00586             curl_easy_cleanup(dl->curl);
00587             dl->curl = NULL;
00588         }
00589     }
00590 
00591     if (multi) {
00592         curl_multi_cleanup(multi);
00593         multi = NULL;
00594     }
00595 }
00596 
00601 static void CL_FinishHTTPDownload (void)
00602 {
00603     int messagesInQueue, i;
00604     CURLcode result;
00605     CURL *curl;
00606     long responseCode;
00607     double timeTaken, fileSize;
00608     char tempName[MAX_OSPATH];
00609     qboolean isFile;
00610 
00611     do {
00612         CURLMsg *msg = curl_multi_info_read(multi, &messagesInQueue);
00613         dlhandle_t *dl = NULL;
00614 
00615         if (!msg) {
00616             Com_Printf("CL_FinishHTTPDownload: Odd, no message for us...\n");
00617             return;
00618         }
00619 
00620         if (msg->msg != CURLMSG_DONE) {
00621             Com_Printf("CL_FinishHTTPDownload: Got some weird message...\n");
00622             continue;
00623         }
00624 
00625         curl = msg->easy_handle;
00626 
00627         /* curl doesn't provide reverse-lookup of the void * ptr, so search for it */
00628         for (i = 0; i < 4; i++) {
00629             if (cls.HTTPHandles[i].curl == curl) {
00630                 dl = &cls.HTTPHandles[i];
00631                 break;
00632             }
00633         }
00634 
00635         if (!dl)
00636             Com_Error(ERR_DROP, "CL_FinishHTTPDownload: Handle not found");
00637 
00638         /* we mark everything as done even if it errored to prevent multiple attempts. */
00639         dl->queueEntry->state = DLQ_STATE_DONE;
00640 
00641         /* filelist processing is done on read */
00642         if (dl->file)
00643             isFile = qtrue;
00644         else
00645             isFile = qfalse;
00646 
00647         if (isFile) {
00648             fclose(dl->file);
00649             dl->file = NULL;
00650         }
00651 
00652         /* might be aborted */
00653         if (pendingCount)
00654             pendingCount--;
00655         handleCount--;
00656         /* Com_Printf("finished dl: hc = %d\n", handleCount); */
00657         cls.downloadName[0] = 0;
00658         cls.downloadPosition = 0;
00659 
00660         result = msg->data.result;
00661 
00662         switch (result) {
00663         /* for some reason curl returns CURLE_OK for a 404... */
00664         case CURLE_HTTP_RETURNED_ERROR:
00665         case CURLE_OK:
00666             curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode);
00667             if (responseCode == 404) {
00668                 const char *extension = Com_GetExtension(dl->queueEntry->ufoPath);
00669                 if (extension != NULL && !strcmp(extension, "pk3"))
00670                     downloadingPK3 = qfalse;
00671 
00672                 if (isFile)
00673                     FS_RemoveFile(dl->filePath);
00674                 Com_Printf("HTTP(%s): 404 File Not Found [%d remaining files]\n", dl->queueEntry->ufoPath, pendingCount);
00675                 curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &fileSize);
00676                 if (fileSize > 512) {
00677                     /* ick */
00678                     isFile = qfalse;
00679                     result = CURLE_FILESIZE_EXCEEDED;
00680                     Com_Printf("Oversized 404 body received (%d bytes), aborting HTTP downloading.\n", (int)fileSize);
00681                 } else {
00682                     curl_multi_remove_handle(multi, dl->curl);
00683                     continue;
00684                 }
00685             } else if (responseCode == 200) {
00686                 if (!isFile && !abortDownloads)
00687                     CL_ParseFileList(dl);
00688                 break;
00689             }
00690 
00691             /* every other code is treated as fatal, fallthrough here */
00692 
00693         /* fatal error, disable http */
00694         case CURLE_COULDNT_RESOLVE_HOST:
00695         case CURLE_COULDNT_CONNECT:
00696         case CURLE_COULDNT_RESOLVE_PROXY:
00697             if (isFile)
00698                 FS_RemoveFile(dl->filePath);
00699             Com_Printf("Fatal HTTP error: %s\n", curl_easy_strerror(result));
00700             curl_multi_remove_handle(multi, dl->curl);
00701             if (abortDownloads)
00702                 continue;
00703             CL_CancelHTTPDownloads(qtrue);
00704             continue;
00705         default:
00706             i = strlen(dl->queueEntry->ufoPath);
00707             if (!strcmp(dl->queueEntry->ufoPath + i - 4, ".pk3"))
00708                 downloadingPK3 = qfalse;
00709             if (isFile)
00710                 FS_RemoveFile(dl->filePath);
00711             Com_Printf("HTTP download failed: %s\n", curl_easy_strerror(result));
00712             curl_multi_remove_handle(multi, dl->curl);
00713             continue;
00714         }
00715 
00716         if (isFile) {
00717             /* rename the temp file */
00718             Com_sprintf(tempName, sizeof(tempName), "%s/%s", FS_Gamedir(), dl->queueEntry->ufoPath);
00719 
00720             if (!FS_RenameFile(dl->filePath, tempName, qfalse))
00721                 Com_Printf("Failed to rename %s for some odd reason...", dl->filePath);
00722 
00723             /* a pk3 file is very special... */
00724             i = strlen(tempName);
00725             if (!strcmp(tempName + i - 4, ".pk3")) {
00726                 FS_RestartFilesystem();
00727                 CL_ReVerifyHTTPQueue();
00728                 downloadingPK3 = qfalse;
00729             }
00730         }
00731 
00732         /* show some stats */
00733         curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &timeTaken);
00734         curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &fileSize);
00735 
00741         curl_multi_remove_handle(multi, dl->curl);
00742 
00743         Com_Printf("HTTP(%s): %.f bytes, %.2fkB/sec [%d remaining files]\n",
00744             dl->queueEntry->ufoPath, fileSize, (fileSize / 1024.0) / timeTaken, pendingCount);
00745     } while (messagesInQueue > 0);
00746 
00747     if (handleCount == 0) {
00748         if (abortDownloads == HTTPDL_ABORT_SOFT)
00749             abortDownloads = HTTPDL_ABORT_NONE;
00750         else if (abortDownloads == HTTPDL_ABORT_HARD)
00751             cls.downloadServer[0] = 0;
00752     }
00753 
00754     /* done current batch, see if we have more to dl - maybe a .bsp needs downloaded */
00755     if (cls.state == ca_connected && !CL_PendingHTTPDownloads())
00756         CL_RequestNextDownload();
00757 }
00758 
00763 static void CL_StartNextHTTPDownload (void)
00764 {
00765     dlqueue_t *q = &cls.downloadQueue;
00766 
00767     while (q->next) {
00768         q = q->next;
00769         if (q->state == DLQ_STATE_NOT_STARTED) {
00770             size_t len;
00771             dlhandle_t *dl = CL_GetFreeDLHandle();
00772             if (!dl)
00773                 return;
00774 
00775             CL_StartHTTPDownload(q, dl);
00776 
00777             /* ugly hack for pk3 file single downloading */
00778             len = strlen(q->ufoPath);
00779             if (len > 4 && !Q_strcasecmp(q->ufoPath + len - 4, ".pk3"))
00780                 downloadingPK3 = qtrue;
00781 
00782             break;
00783         }
00784     }
00785 }
00786 
00793 void CL_RunHTTPDownloads (void)
00794 {
00795     CURLMcode ret;
00796 
00797     if (!cls.downloadServer[0])
00798         return;
00799 
00800     /* Com_Printf("handle %d, pending %d\n", handleCount, pendingCount); */
00801 
00802     /* not enough downloads running, queue some more! */
00803     if (pendingCount && abortDownloads == HTTPDL_ABORT_NONE &&
00804         !downloadingPK3 && handleCount < cl_http_max_connections->integer)
00805         CL_StartNextHTTPDownload();
00806 
00807     do {
00808         int newHandleCount;
00809         ret = curl_multi_perform(multi, &newHandleCount);
00810         if (newHandleCount < handleCount) {
00811             /* Com_Printf("runnd dl: hc = %d, nc = %d\n", handleCount, newHandleCount); */
00812             /* hmm, something either finished or errored out. */
00813             CL_FinishHTTPDownload();
00814             handleCount = newHandleCount;
00815         }
00816     } while (ret == CURLM_CALL_MULTI_PERFORM);
00817 
00818     if (ret != CURLM_OK) {
00819         Com_Printf("curl_multi_perform error. Aborting HTTP downloads.\n");
00820         CL_CancelHTTPDownloads(qtrue);
00821     }
00822 
00823     /* not enough downloads running, queue some more! */
00824     if (pendingCount && abortDownloads == HTTPDL_ABORT_NONE &&
00825         !downloadingPK3 && handleCount < cl_http_max_connections->integer)
00826         CL_StartNextHTTPDownload();
00827 }
00828 
00829 void HTTP_InitStartup (void)
00830 {
00831     cl_http_filelists = Cvar_Get("cl_http_filelists", "1", 0, NULL);
00832     cl_http_downloads = Cvar_Get("cl_http_downloads", "1", 0, "Try to download files via http");
00833     cl_http_max_connections = Cvar_Get("cl_http_max_connections", "1", 0, NULL);
00834 }

Generated by  doxygen 1.6.2