5 static cvar_t cl_curl_maxdownloads = {1, "cl_curl_maxdownloads","1", "maximum number of concurrent HTTP/FTP downloads"};
6 static cvar_t cl_curl_maxspeed = {1, "cl_curl_maxspeed","100", "maximum download speed (KiB/s)"};
7 static cvar_t sv_curl_defaulturl = {1, "sv_curl_defaulturl","", "default autodownload source URL"};
8 static cvar_t sv_curl_serverpackages = {1, "sv_curl_serverpackages","", "list of required files for the clients, separated by spaces"};
9 static cvar_t cl_curl_enabled = {1, "cl_curl_enabled","0", "whether client's download support is enabled"};
12 =================================================================
14 Minimal set of definitions from libcurl
16 WARNING: for a matter of simplicity, several pointer types are
17 casted to "void*", and most enumerated values are not included
19 =================================================================
22 typedef struct CURL_s CURL;
23 typedef struct CURLM_s CURLM;
31 CURLM_CALL_MULTI_PERFORM=-1, /* please call curl_multi_perform() soon */
35 #define CURL_GLOBAL_NOTHING 0
36 #define CURL_GLOBAL_SSL 1
37 #define CURL_GLOBAL_WIN32 2
38 #define CURLOPTTYPE_LONG 0
39 #define CURLOPTTYPE_OBJECTPOINT 10000
40 #define CURLOPTTYPE_FUNCTIONPOINT 20000
41 #define CURLOPTTYPE_OFF_T 30000
42 #define CINIT(name,type,number) CURLOPT_ ## name = CURLOPTTYPE_ ## type + number
45 CINIT(WRITEDATA, OBJECTPOINT, 1),
46 CINIT(URL, OBJECTPOINT, 2),
47 CINIT(ERRORBUFFER, OBJECTPOINT, 10),
48 CINIT(WRITEFUNCTION, FUNCTIONPOINT, 11),
49 CINIT(REFERER, OBJECTPOINT, 16),
50 CINIT(USERAGENT, OBJECTPOINT, 18),
51 CINIT(RESUME_FROM, LONG, 21),
52 CINIT(FOLLOWLOCATION, LONG, 52), /* use Location: Luke! */
53 CINIT(PRIVATE, OBJECTPOINT, 103),
54 CINIT(LOW_SPEED_LIMIT, LONG , 19),
55 CINIT(LOW_SPEED_TIME, LONG, 20),
61 CURLINFO_HEADER_IN, /* 1 */
62 CURLINFO_HEADER_OUT, /* 2 */
63 CURLINFO_DATA_IN, /* 3 */
64 CURLINFO_DATA_OUT, /* 4 */
65 CURLINFO_SSL_DATA_IN, /* 5 */
66 CURLINFO_SSL_DATA_OUT, /* 6 */
70 #define CURLINFO_STRING 0x100000
71 #define CURLINFO_LONG 0x200000
72 #define CURLINFO_DOUBLE 0x300000
73 #define CURLINFO_SLIST 0x400000
74 #define CURLINFO_MASK 0x0fffff
75 #define CURLINFO_TYPEMASK 0xf00000
78 CURLINFO_NONE, /* first, never use this */
79 CURLINFO_EFFECTIVE_URL = CURLINFO_STRING + 1,
80 CURLINFO_RESPONSE_CODE = CURLINFO_LONG + 2,
81 CURLINFO_TOTAL_TIME = CURLINFO_DOUBLE + 3,
82 CURLINFO_NAMELOOKUP_TIME = CURLINFO_DOUBLE + 4,
83 CURLINFO_CONNECT_TIME = CURLINFO_DOUBLE + 5,
84 CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE + 6,
85 CURLINFO_SIZE_UPLOAD = CURLINFO_DOUBLE + 7,
86 CURLINFO_SIZE_DOWNLOAD = CURLINFO_DOUBLE + 8,
87 CURLINFO_SPEED_DOWNLOAD = CURLINFO_DOUBLE + 9,
88 CURLINFO_SPEED_UPLOAD = CURLINFO_DOUBLE + 10,
89 CURLINFO_HEADER_SIZE = CURLINFO_LONG + 11,
90 CURLINFO_REQUEST_SIZE = CURLINFO_LONG + 12,
91 CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG + 13,
92 CURLINFO_FILETIME = CURLINFO_LONG + 14,
93 CURLINFO_CONTENT_LENGTH_DOWNLOAD = CURLINFO_DOUBLE + 15,
94 CURLINFO_CONTENT_LENGTH_UPLOAD = CURLINFO_DOUBLE + 16,
95 CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE + 17,
96 CURLINFO_CONTENT_TYPE = CURLINFO_STRING + 18,
97 CURLINFO_REDIRECT_TIME = CURLINFO_DOUBLE + 19,
98 CURLINFO_REDIRECT_COUNT = CURLINFO_LONG + 20,
99 CURLINFO_PRIVATE = CURLINFO_STRING + 21,
100 CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG + 22,
101 CURLINFO_HTTPAUTH_AVAIL = CURLINFO_LONG + 23,
102 CURLINFO_PROXYAUTH_AVAIL = CURLINFO_LONG + 24,
103 CURLINFO_OS_ERRNO = CURLINFO_LONG + 25,
104 CURLINFO_NUM_CONNECTS = CURLINFO_LONG + 26,
105 CURLINFO_SSL_ENGINES = CURLINFO_SLIST + 27,
111 CURLMSG_NONE, /* first, not used */
112 CURLMSG_DONE, /* This easy handle has completed. 'result' contains
113 the CURLcode of the transfer */
119 CURLMSG msg; /* what this message means */
120 CURL *easy_handle; /* the handle it concerns */
123 void *whatever; /* message-specific data */
124 CURLcode result; /* return code for transfer */
130 static void (*qcurl_global_init) (long flags);
131 static void (*qcurl_global_cleanup) ();
133 static CURL * (*qcurl_easy_init) ();
134 static void (*qcurl_easy_cleanup) (CURL *handle);
135 static CURLcode (*qcurl_easy_setopt) (CURL *handle, CURLoption option, ...);
136 static CURLcode (*qcurl_easy_getinfo) (CURL *handle, CURLINFO info, ...);
137 static const char * (*qcurl_easy_strerror) (CURLcode);
139 static CURLM * (*qcurl_multi_init) ();
140 static CURLMcode (*qcurl_multi_perform) (CURLM *multi_handle, int *running_handles);
141 static CURLMcode (*qcurl_multi_add_handle) (CURLM *multi_handle, CURL *easy_handle);
142 static CURLMcode (*qcurl_multi_remove_handle) (CURLM *multi_handle, CURL *easy_handle);
143 static CURLMsg * (*qcurl_multi_info_read) (CURLM *multi_handle, int *msgs_in_queue);
144 static void (*qcurl_multi_cleanup) (CURLM *);
145 static const char * (*qcurl_multi_strerror) (CURLcode);
147 static dllfunction_t curlfuncs[] =
149 {"curl_global_init", (void **) &qcurl_global_init},
150 {"curl_global_cleanup", (void **) &qcurl_global_cleanup},
151 {"curl_easy_init", (void **) &qcurl_easy_init},
152 {"curl_easy_cleanup", (void **) &qcurl_easy_cleanup},
153 {"curl_easy_setopt", (void **) &qcurl_easy_setopt},
154 {"curl_easy_strerror", (void **) &qcurl_easy_strerror},
155 {"curl_easy_getinfo", (void **) &qcurl_easy_getinfo},
156 {"curl_multi_init", (void **) &qcurl_multi_init},
157 {"curl_multi_perform", (void **) &qcurl_multi_perform},
158 {"curl_multi_add_handle", (void **) &qcurl_multi_add_handle},
159 {"curl_multi_remove_handle",(void **) &qcurl_multi_remove_handle},
160 {"curl_multi_info_read", (void **) &qcurl_multi_info_read},
161 {"curl_multi_cleanup", (void **) &qcurl_multi_cleanup},
162 {"curl_multi_strerror", (void **) &qcurl_multi_strerror},
166 // Handle for CURL DLL
167 static dllhandle_t curl_dll = NULL;
168 // will be checked at many places to find out if qcurl calls are allowed
170 typedef struct downloadinfo_s
172 char filename[MAX_QPATH];
176 fs_offset_t startpos;
180 unsigned long bytes_received;
181 struct downloadinfo_s *next, *prev;
185 static downloadinfo *downloads = NULL;
186 static int numdownloads = 0;
188 static int numdownloads_fail = 0;
189 static int numdownloads_success = 0;
190 static int numdownloads_added = 0;
191 static char command_when_done[256] = "";
192 static char command_when_error[256] = "";
198 Sets the command which is to be executed when the last download completes AND
199 all downloads since last server connect ended with a successful status.
200 Setting the command to NULL clears it.
203 void Curl_CommandWhenDone(const char *cmd)
208 strlcpy(command_when_done, cmd, sizeof(command_when_done));
210 *command_when_done = 0;
215 Do not use yet. Not complete.
216 Problem: what counts as an error?
219 void Curl_CommandWhenError(const char *cmd)
224 strlcpy(command_when_error, cmd, sizeof(command_when_error));
226 *command_when_error = 0;
231 Curl_Clear_forthismap
233 Clears the "will disconnect on failure" flags.
236 void Curl_Clear_forthismap()
239 for(di = downloads; di; di = di->next)
240 di->forthismap = false;
241 Curl_CommandWhenError(NULL);
242 Curl_CommandWhenDone(NULL);
243 numdownloads_fail = 0;
244 numdownloads_success = 0;
245 numdownloads_added = 0;
248 /* obsolete: numdownloads_added contains the same
249 static qboolean Curl_Have_forthismap()
252 for(di = downloads; di; di = di->next)
261 Curl_CheckCommandWhenDone
263 Checks if a "done command" is to be executed.
264 All downloads finished, at least one success since connect, no single failure
265 -> execute the command.
267 static void Curl_CheckCommandWhenDone()
271 if(numdownloads_added && (numdownloads_success == numdownloads_added) && *command_when_done)
273 Con_DPrintf("Map downloads occurred, executing %s\n", command_when_done);
275 Cbuf_AddText(command_when_done);
277 Curl_Clear_forthismap();
279 else if(numdownloads_added && numdownloads_fail && *command_when_error)
281 Con_DPrintf("Map downloads FAILED, executing %s\n", command_when_error);
283 Cbuf_AddText(command_when_error);
285 Curl_Clear_forthismap();
296 static qboolean CURL_OpenLibrary (void)
298 const char* dllnames [] =
304 #elif defined(MACOSX)
305 "libcurl.3.dylib", // Mac OS X Tiger
306 "libcurl.2.dylib", // Mac OS X Panther
318 if (! Sys_LoadLibrary (dllnames, &curl_dll, curlfuncs))
320 Con_Printf ("cURL support disabled\n");
324 Con_Printf ("cURL support enabled\n");
336 static void CURL_CloseLibrary (void)
338 Sys_UnloadLibrary (&curl_dll);
342 static CURLM *curlm = NULL;
343 static unsigned long bytes_received = 0; // used for bandwidth throttling
344 static double curltime = 0;
350 fwrite-compatible function that writes the data to a file. libcurl can call
354 static size_t CURL_fwrite(void *data, size_t size, size_t nmemb, void *vdi)
357 size_t bytes = size * nmemb;
358 downloadinfo *di = (downloadinfo *) vdi;
360 bytes_received += bytes;
361 di->bytes_received += bytes;
363 ret = FS_Write(di->stream, data, bytes);
365 return ret; // why not ret / nmemb?
370 CURL_DOWNLOAD_SUCCESS = 0,
371 CURL_DOWNLOAD_FAILED,
372 CURL_DOWNLOAD_ABORTED,
373 CURL_DOWNLOAD_SERVERERROR
381 stops a download. It receives a status (CURL_DOWNLOAD_SUCCESS,
382 CURL_DOWNLOAD_FAILED or CURL_DOWNLOAD_ABORTED) and in the second case the error
383 code from libcurl, or 0, if another error has occurred.
386 static void Curl_EndDownload(downloadinfo *di, CurlStatus status, CURLcode error)
393 case CURL_DOWNLOAD_SUCCESS:
394 Con_Printf("Download of %s: OK\n", di->filename);
397 case CURL_DOWNLOAD_FAILED:
398 Con_Printf("Download of %s: FAILED\n", di->filename);
400 Con_Printf("Reason given by libcurl: %s\n", qcurl_easy_strerror(error));
402 case CURL_DOWNLOAD_ABORTED:
403 Con_Printf("Download of %s: ABORTED\n", di->filename);
405 case CURL_DOWNLOAD_SERVERERROR:
406 Con_Printf("Download of %s: %d\n", di->filename, (int) error);
408 // reopen to enforce it to have zero bytes again
409 FS_Close(di->stream);
410 di->stream = FS_Open(di->filename, "w", false, false);
417 qcurl_multi_remove_handle(curlm, di->curle);
418 qcurl_easy_cleanup(di->curle);
421 if(ok && !di->bytes_received)
423 Con_Printf("ERROR: empty file\n");
428 FS_Close(di->stream);
431 ok = FS_AddPack(di->filename, NULL, true);
434 di->prev->next = di->next;
436 downloads = di->next;
438 di->next->prev = di->prev;
444 ++numdownloads_success;
450 Curl_CheckCommandWhenDone();
455 CheckPendingDownloads
457 checks if there are free download slots to start new downloads in.
458 To not start too many downloads at once, only one download is added at a time,
459 up to a maximum number of cl_curl_maxdownloads are running.
462 static void CheckPendingDownloads()
466 if(numdownloads < cl_curl_maxdownloads.integer)
469 for(di = downloads; di; di = di->next)
473 Con_Printf("Downloading %s -> %s", di->url, di->filename);
475 di->stream = FS_Open(di->filename, "ab", false, false);
478 Con_Printf("\nFAILED: Could not open output file %s\n", di->filename);
479 Curl_EndDownload(di, CURL_DOWNLOAD_FAILED, CURLE_OK);
483 FS_Seek(di->stream, 0, SEEK_END);
484 di->startpos = FS_Tell(di->stream);
486 Con_Printf(", resuming from position %ld", (long) di->startpos);
489 di->curle = qcurl_easy_init();
490 qcurl_easy_setopt(di->curle, CURLOPT_URL, di->url);
491 qcurl_easy_setopt(di->curle, CURLOPT_USERAGENT, engineversion);
492 qcurl_easy_setopt(di->curle, CURLOPT_REFERER, di->referer);
493 qcurl_easy_setopt(di->curle, CURLOPT_RESUME_FROM, (long) di->startpos);
494 qcurl_easy_setopt(di->curle, CURLOPT_FOLLOWLOCATION, 1);
495 qcurl_easy_setopt(di->curle, CURLOPT_WRITEFUNCTION, CURL_fwrite);
496 qcurl_easy_setopt(di->curle, CURLOPT_LOW_SPEED_LIMIT, (long) 256);
497 qcurl_easy_setopt(di->curle, CURLOPT_LOW_SPEED_TIME, (long) 45);
498 qcurl_easy_setopt(di->curle, CURLOPT_WRITEDATA, (void *) di);
499 qcurl_easy_setopt(di->curle, CURLOPT_PRIVATE, (void *) di);
500 qcurl_multi_add_handle(curlm, di->curle);
503 if(numdownloads >= cl_curl_maxdownloads.integer)
514 this function MUST be called before using anything else in this file.
515 On Win32, this must be called AFTER WSAStartup has been done!
523 qcurl_global_init(CURL_GLOBAL_NOTHING);
524 curlm = qcurl_multi_init();
531 Surprise... closes all the stuff. Please do this BEFORE shutting down LHNET.
534 void Curl_ClearRequirements();
539 Curl_ClearRequirements();
549 Finds the internal information block for a download given by file name.
552 static downloadinfo *Curl_Find(const char *filename)
557 for(di = downloads; di; di = di->next)
558 if(!strcasecmp(di->filename, filename))
567 Starts a download of a given URL to the file name portion of this URL (or name
568 if given) in the "dlcache/" folder.
571 void Curl_Begin(const char *URL, const char *name, qboolean ispak, qboolean forthismap)
582 // Note: This extraction of the file name portion is NOT entirely correct.
584 // It does the following:
586 // http://host/some/script.cgi/SomeFile.pk3?uid=ABCDE -> SomeFile.pk3
587 // http://host/some/script.php?uid=ABCDE&file=/SomeFile.pk3 -> SomeFile.pk3
588 // http://host/some/script.php?uid=ABCDE&file=SomeFile.pk3 -> script.php
590 // However, I'd like to keep this "buggy" behavior so that PHP script
591 // authors can write download scripts without having to enable
592 // AcceptPathInfo on Apache. They just have to ensure that their script
593 // can be called with such a "fake" path name like
594 // http://host/some/script.php?uid=ABCDE&file=/SomeFile.pk3
596 // By the way, such PHP scripts should either send the file or a
597 // "Location:" redirect; PHP code example:
599 // header("Location: http://www.example.com/");
601 // By the way, this will set User-Agent to something like
602 // "Nexuiz build 22:27:55 Mar 17 2006" (engineversion) and Referer to
603 // dp://serverhost:serverport/ so you can filter on this; an example
604 // httpd log file line might be:
606 // 141.2.16.3 - - [17/Mar/2006:22:32:43 +0100] "GET /maps/tznex07.pk3 HTTP/1.1" 200 1077455 "dp://141.2.16.7:26000/" "Nexuiz Linux 22:07:43 Mar 17 2006"
610 p = strrchr(name, '/');
611 p = p ? (p+1) : name;
613 length = q ? (size_t)(q - p) : strlen(p);
614 dpsnprintf(fn, sizeof(fn), "dlcache/%.*s", (int)length, p);
617 // already downloading the file?
619 downloadinfo *di = Curl_Find(fn);
622 Con_Printf("Can't download %s, already getting it from %s!\n", fn, di->url);
624 // however, if it was not for this map yet...
625 if(forthismap && !di->forthismap)
627 di->forthismap = true;
628 // this "fakes" a download attempt so the client will wait for
629 // the download to finish and then reconnect
630 ++numdownloads_added;
637 if(ispak && FS_FileExists(fn))
639 qboolean already_loaded;
640 if(FS_AddPack(fn, &already_loaded, true))
642 Con_DPrintf("%s already exists, not downloading!\n", fn);
644 Con_DPrintf("(pak was already loaded)\n");
649 ++numdownloads_added;
650 ++numdownloads_success;
657 qfile_t *f = FS_Open(fn, "rb", false, false);
661 FS_Read(f, buf, sizeof(buf)); // no "-1", I will use memcmp
663 if(memcmp(buf, "PK\x03\x04", 4) && memcmp(buf, "PACK", 4))
665 Con_DPrintf("Detected non-PAK %s, clearing and NOT resuming.\n", fn);
667 f = FS_Open(fn, "w", false, false);
681 ++numdownloads_added;
682 di = (downloadinfo *) Z_Malloc(sizeof(*di));
683 strlcpy(di->filename, fn, sizeof(di->filename));
684 strlcpy(di->url, URL, sizeof(di->url));
685 dpsnprintf(di->referer, sizeof(di->referer), "dp://%s/", cls.netcon ? cls.netcon->address : "notconnected.invalid");
686 di->forthismap = forthismap;
692 di->bytes_received = 0;
693 di->next = downloads;
706 call this regularily as this will always download as much as possible without
712 if(!cl_curl_enabled.integer)
721 if(realtime < curltime) // throttle
730 mc = qcurl_multi_perform(curlm, &remaining);
732 while(mc == CURLM_CALL_MULTI_PERFORM);
736 CURLMsg *msg = qcurl_multi_info_read(curlm, &remaining);
739 if(msg->msg == CURLMSG_DONE)
742 CurlStatus failed = CURL_DOWNLOAD_SUCCESS;
744 qcurl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &di);
745 result = msg->data.result;
748 failed = CURL_DOWNLOAD_FAILED;
753 qcurl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &code);
758 failed = CURL_DOWNLOAD_SERVERERROR;
764 Curl_EndDownload(di, failed, result);
769 CheckPendingDownloads();
771 // when will we curl the next time?
772 // we will wait a bit to ensure our download rate is kept.
773 // we now know that realtime >= curltime... so set up a new curltime
774 if(cl_curl_maxspeed.value > 0)
776 unsigned long bytes = bytes_received; // maybe smoothen a bit?
777 curltime = realtime + bytes / (cl_curl_maxspeed.value * 1024.0);
778 bytes_received -= bytes;
791 void Curl_CancelAll()
798 Curl_EndDownload(downloads, CURL_DOWNLOAD_ABORTED, CURLE_OK);
799 // INVARIANT: downloads will point to the next download after that!
807 returns true iff there is a download running.
810 qboolean Curl_Running()
815 return downloads != NULL;
820 Curl_GetDownloadAmount
822 returns a value from 0.0 to 1.0 which represents the downloaded amount of data
823 for the given download.
826 static double Curl_GetDownloadAmount(downloadinfo *di)
833 qcurl_easy_getinfo(di->curle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &length);
835 return di->bytes_received / length;
845 Curl_GetDownloadSpeed
847 returns the speed of the given download in bytes per second
850 static double Curl_GetDownloadSpeed(downloadinfo *di)
857 qcurl_easy_getinfo(di->curle, CURLINFO_SPEED_DOWNLOAD, &speed);
868 prints the download list
871 // TODO rewrite using Curl_GetDownloadInfo?
872 static void Curl_Info_f()
879 Con_Print("Currently running downloads:\n");
880 for(di = downloads; di; di = di->next)
882 double speed, percent;
883 Con_Printf(" %s -> %s ", di->url, di->filename);
884 percent = 100.0 * Curl_GetDownloadAmount(di);
885 speed = Curl_GetDownloadSpeed(di);
887 Con_Printf("(%.1f%% @ %.1f KiB/s)\n", percent, speed / 1024.0);
889 Con_Print("(queued)\n");
894 Con_Print("No downloads running.\n");
902 implements the "curl" console command
906 curl --cancel filename
911 curl [--pak] [--forthismap] [--for filename filename...] url
912 --pak: after downloading, load the package into the virtual file system
913 --for filename...: only download of at least one of the named files is missing
914 --forthismap: don't reconnect on failure
916 curl --clear_autodownload
917 clears the download success/failure counters
919 curl --finish_autodownload
920 if at least one download has been started, disconnect and drop to the menu
921 once the last download completes successfully, reconnect to the current server
924 void Curl_Curl_f(void)
928 qboolean pak = false;
929 qboolean forthismap = false;
931 const char *name = 0;
935 Con_Print("libcurl DLL not found, this command is inactive.\n");
939 if(!cl_curl_enabled.integer)
941 Con_Print("curl support not enabled. Set cl_curl_enabled to 1 to enable.\n");
945 for(i = 0; i != Cmd_Argc(); ++i)
946 Con_DPrintf("%s ", Cmd_Argv(i));
951 Con_Print("usage:\ncurl --info, curl --cancel [filename], curl url\n");
955 url = Cmd_Argv(Cmd_Argc() - 1);
958 for(i = 1; i != end; ++i)
960 const char *a = Cmd_Argv(i);
961 if(!strcmp(a, "--info"))
966 else if(!strcmp(a, "--cancel"))
968 if(i == end - 1) // last argument
972 downloadinfo *di = Curl_Find(url);
974 Curl_EndDownload(di, CURL_DOWNLOAD_ABORTED, CURLE_OK);
976 Con_Print("download not found\n");
980 else if(!strcmp(a, "--pak"))
984 else if(!strcmp(a, "--for"))
986 for(i = i + 1; i != end - 1; ++i)
988 if(!FS_FileExists(Cmd_Argv(i)))
989 goto needthefile; // why can't I have a "double break"?
991 // if we get here, we have all the files...
994 else if(!strcmp(a, "--forthismap"))
998 else if(!strcmp(a, "--as"))
1006 else if(!strcmp(a, "--clear_autodownload"))
1008 // mark all running downloads as "not for this map", so if they
1009 // fail, it does not matter
1010 Curl_Clear_forthismap();
1013 else if(!strcmp(a, "--finish_autodownload"))
1015 if(numdownloads_added)
1017 char donecommand[256];
1020 dpsnprintf(donecommand, sizeof(donecommand), "connect %s", cls.netcon->address);
1021 Curl_CommandWhenDone(donecommand);
1025 Curl_CheckCommandWhenDone();
1031 Con_Printf("invalid option %s\n", a);
1037 Curl_Begin(url, name, pak, forthismap);
1041 ====================
1044 loads the commands and cvars this library uses
1045 ====================
1047 void Curl_Init_Commands(void)
1049 Cvar_RegisterVariable (&cl_curl_enabled);
1050 Cvar_RegisterVariable (&cl_curl_maxdownloads);
1051 Cvar_RegisterVariable (&cl_curl_maxspeed);
1052 Cvar_RegisterVariable (&sv_curl_defaulturl);
1053 Cvar_RegisterVariable (&sv_curl_serverpackages);
1054 Cmd_AddCommand ("curl", Curl_Curl_f, "download data from an URL and add to search path");
1058 ====================
1059 Curl_GetDownloadInfo
1061 returns an array of Curl_downloadinfo_t structs for usage by GUIs.
1062 The number of elements in the array is returned in int *nDownloads.
1063 const char **additional_info may be set to a string of additional user
1064 information, or to NULL if no such display shall occur. The returned
1065 array must be freed later using Z_Free.
1066 ====================
1068 Curl_downloadinfo_t *Curl_GetDownloadInfo(int *nDownloads, const char **additional_info)
1072 Curl_downloadinfo_t *downinfo;
1073 static char addinfo[128];
1079 *additional_info = NULL;
1084 for(di = downloads; di; di = di->next)
1087 downinfo = (Curl_downloadinfo_t *) Z_Malloc(sizeof(*downinfo) * n);
1089 for(di = downloads; di; di = di->next)
1091 strlcpy(downinfo[i].filename, di->filename, sizeof(downinfo[i].filename));
1094 downinfo[i].progress = Curl_GetDownloadAmount(di);
1095 downinfo[i].speed = Curl_GetDownloadSpeed(di);
1096 downinfo[i].queued = false;
1100 downinfo[i].queued = true;
1107 // TODO: can I clear command_when_done as soon as the first download fails?
1108 if(*command_when_done && !numdownloads_fail && numdownloads_added)
1110 if(strncmp(command_when_done, "connect ", 8))
1111 dpsnprintf(addinfo, sizeof(addinfo), "(will do '%s' when done)", command_when_done);
1113 dpsnprintf(addinfo, sizeof(addinfo), "(will join %s when done)", command_when_done + 8);
1114 *additional_info = addinfo;
1117 *additional_info = NULL;
1126 ====================
1129 finds the URL where to find a given package.
1131 For this, it reads a file "curl_urls.txt" of the following format:
1134 revdm*.pk3 http://revdm/downloads/are/here/
1135 * http://any/other/stuff/is/here/
1137 The URLs should end in /. If not, downloads will still work, but the cached files
1138 can't be just put into the data directory with the same download configuration
1139 (you might want to do this if you want to tag downloaded files from your
1140 server, but you should not). "-" means "don't download".
1142 If no single pattern matched, the cvar sv_curl_defaulturl is used as download
1145 Note: pak1.pak and data*.pk3 are excluded from autodownload at another point in
1146 this file for obvious reasons.
1147 ====================
1149 static const char *Curl_FindPackURL(const char *filename)
1151 static char foundurl[256];
1152 fs_offset_t filesize;
1153 char *buf = (char *) FS_LoadFile("curl_urls.txt", tempmempool, true, &filesize);
1156 // read lines of format "pattern url"
1158 char *pattern = NULL, *patternend = NULL, *url = NULL, *urlend = NULL;
1159 qboolean eof = false;
1171 if(pattern && url && patternend)
1177 if(matchpattern(filename, pattern, true))
1179 strlcpy(foundurl, url, sizeof(foundurl));
1191 if(pattern && !patternend)
1193 else if(url && !urlend)
1199 else if(pattern && patternend && !url)
1208 return sv_curl_defaulturl.string;
1211 typedef struct requirement_s
1213 struct requirement_s *next;
1214 char filename[MAX_QPATH];
1217 static requirement *requirements = NULL;
1221 ====================
1224 Adds the given file to the list of requirements.
1225 ====================
1227 void Curl_RequireFile(const char *filename)
1229 requirement *req = (requirement *) Z_Malloc(sizeof(*requirements));
1230 req->next = requirements;
1231 strlcpy(req->filename, filename, sizeof(req->filename));
1236 ====================
1237 Curl_ClearRequirements
1239 Clears the list of required files for playing on the current map.
1240 This should be called at every map change.
1241 ====================
1243 void Curl_ClearRequirements()
1248 requirement *req = requirements;
1249 requirements = requirements->next;
1252 p = sv_curl_serverpackages.string;
1253 Con_DPrintf("Require all of: %s\n", p);
1254 while(COM_ParseTokenConsole(&p))
1256 Con_DPrintf("Require: %s\n", com_token);
1257 Curl_RequireFile(com_token);
1262 ====================
1263 Curl_SendRequirements
1265 Makes the current host_clients download all files he needs.
1266 This is done by sending him the following console commands:
1268 curl --clear_autodownload
1269 curl --pak --for maps/pushmoddm1.bsp --forthismap http://where/this/darn/map/is/pushmoddm1.pk3
1270 curl --finish_autodownload
1271 ====================
1273 void Curl_SendRequirements()
1275 // for each requirement, find the pack name
1276 char sendbuffer[4096] = "";
1278 qboolean foundone = false;
1280 for(req = requirements; req; req = req->next)
1283 const char *thispack = FS_WhichPack(req->filename);
1284 const char *packurl;
1289 p = strrchr(thispack, '/');
1293 packurl = Curl_FindPackURL(thispack);
1295 if(packurl && *packurl && strcmp(packurl, "-"))
1298 strlcat(sendbuffer, "curl --clear_autodownload\n", sizeof(sendbuffer));
1300 strlcat(sendbuffer, "curl --pak --forthismap --as ", sizeof(sendbuffer));
1301 strlcat(sendbuffer, thispack, sizeof(sendbuffer));
1302 strlcat(sendbuffer, " --for ", sizeof(sendbuffer));
1303 strlcat(sendbuffer, req->filename, sizeof(sendbuffer));
1304 strlcat(sendbuffer, " ", sizeof(sendbuffer));
1305 strlcat(sendbuffer, packurl, sizeof(sendbuffer));
1306 strlcat(sendbuffer, thispack, sizeof(sendbuffer));
1307 strlcat(sendbuffer, "\n", sizeof(sendbuffer));
1314 strlcat(sendbuffer, "curl --finish_autodownload\n", sizeof(sendbuffer));
1316 if(strlen(sendbuffer) + 1 < sizeof(sendbuffer))
1317 Host_ClientCommands("%s", sendbuffer);
1319 Con_Printf("Could not initiate autodownload due to URL buffer overflow\n");