5 static cvar_t cl_curl_maxdownloads = {CVAR_SAVE, "cl_curl_maxdownloads","1", "maximum number of concurrent HTTP/FTP downloads"};
6 static cvar_t cl_curl_maxspeed = {CVAR_SAVE, "cl_curl_maxspeed","100", "maximum download speed (KiB/s)"};
7 static cvar_t sv_curl_defaulturl = {CVAR_SAVE, "sv_curl_defaulturl","", "default autodownload source URL"};
8 static cvar_t sv_curl_serverpackages = {CVAR_SAVE, "sv_curl_serverpackages","", "list of required files for the clients, separated by spaces"};
9 static cvar_t cl_curl_enabled = {CVAR_SAVE, "cl_curl_enabled","1", "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 qboolean noclear = FALSE;
190 static int numdownloads_fail = 0;
191 static int numdownloads_success = 0;
192 static int numdownloads_added = 0;
193 static char command_when_done[256] = "";
194 static char command_when_error[256] = "";
200 Sets the command which is to be executed when the last download completes AND
201 all downloads since last server connect ended with a successful status.
202 Setting the command to NULL clears it.
205 void Curl_CommandWhenDone(const char *cmd)
210 strlcpy(command_when_done, cmd, sizeof(command_when_done));
212 *command_when_done = 0;
217 Do not use yet. Not complete.
218 Problem: what counts as an error?
221 void Curl_CommandWhenError(const char *cmd)
226 strlcpy(command_when_error, cmd, sizeof(command_when_error));
228 *command_when_error = 0;
233 Curl_Clear_forthismap
235 Clears the "will disconnect on failure" flags.
238 void Curl_Clear_forthismap()
243 for(di = downloads; di; di = di->next)
244 di->forthismap = false;
245 Curl_CommandWhenError(NULL);
246 Curl_CommandWhenDone(NULL);
247 numdownloads_fail = 0;
248 numdownloads_success = 0;
249 numdownloads_added = 0;
256 Returns true if a download needed for the current game is running.
259 qboolean Curl_Have_forthismap()
261 return numdownloads_added;
264 void Curl_Register_predownload()
266 Curl_CommandWhenDone("cl_begindownloads");
267 Curl_CommandWhenError("cl_begindownloads");
272 Curl_CheckCommandWhenDone
274 Checks if a "done command" is to be executed.
275 All downloads finished, at least one success since connect, no single failure
276 -> execute the command.
278 static void Curl_CheckCommandWhenDone()
282 if(numdownloads_added && (numdownloads_success == numdownloads_added) && *command_when_done)
284 Con_DPrintf("cURL downloads occurred, executing %s\n", command_when_done);
286 Cbuf_AddText(command_when_done);
288 Curl_Clear_forthismap();
290 else if(numdownloads_added && numdownloads_fail && *command_when_error)
292 Con_DPrintf("cURL downloads FAILED, executing %s\n", command_when_error);
294 Cbuf_AddText(command_when_error);
296 Curl_Clear_forthismap();
307 static qboolean CURL_OpenLibrary (void)
309 const char* dllnames [] =
316 #elif defined(MACOSX)
317 "libcurl.4.dylib", // Mac OS X Notyetreleased
318 "libcurl.3.dylib", // Mac OS X Tiger
319 "libcurl.2.dylib", // Mac OS X Panther
323 "libcurl.so", // FreeBSD
333 return Sys_LoadLibrary (dllnames, &curl_dll, curlfuncs);
344 static void CURL_CloseLibrary (void)
346 Sys_UnloadLibrary (&curl_dll);
350 static CURLM *curlm = NULL;
351 static unsigned long bytes_received = 0; // used for bandwidth throttling
352 static double curltime = 0;
358 fwrite-compatible function that writes the data to a file. libcurl can call
362 static size_t CURL_fwrite(void *data, size_t size, size_t nmemb, void *vdi)
365 size_t bytes = size * nmemb;
366 downloadinfo *di = (downloadinfo *) vdi;
368 bytes_received += bytes;
369 di->bytes_received += bytes;
371 ret = FS_Write(di->stream, data, bytes);
373 return ret; // why not ret / nmemb?
378 CURL_DOWNLOAD_SUCCESS = 0,
379 CURL_DOWNLOAD_FAILED,
380 CURL_DOWNLOAD_ABORTED,
381 CURL_DOWNLOAD_SERVERERROR
389 stops a download. It receives a status (CURL_DOWNLOAD_SUCCESS,
390 CURL_DOWNLOAD_FAILED or CURL_DOWNLOAD_ABORTED) and in the second case the error
391 code from libcurl, or 0, if another error has occurred.
394 static void Curl_EndDownload(downloadinfo *di, CurlStatus status, CURLcode error)
401 case CURL_DOWNLOAD_SUCCESS:
402 Con_Printf("Download of %s: OK\n", di->filename);
405 case CURL_DOWNLOAD_FAILED:
406 Con_Printf("Download of %s: FAILED\n", di->filename);
408 Con_Printf("Reason given by libcurl: %s\n", qcurl_easy_strerror(error));
410 case CURL_DOWNLOAD_ABORTED:
411 Con_Printf("Download of %s: ABORTED\n", di->filename);
413 case CURL_DOWNLOAD_SERVERERROR:
414 Con_Printf("Download of %s: %d\n", di->filename, (int) error);
416 // reopen to enforce it to have zero bytes again
417 FS_Close(di->stream);
418 di->stream = FS_Open(di->filename, "w", false, false);
425 qcurl_multi_remove_handle(curlm, di->curle);
426 qcurl_easy_cleanup(di->curle);
429 if(ok && !di->bytes_received)
431 Con_Printf("ERROR: empty file\n");
436 FS_Close(di->stream);
439 ok = FS_AddPack(di->filename, NULL, true);
442 di->prev->next = di->next;
444 downloads = di->next;
446 di->next->prev = di->prev;
452 ++numdownloads_success;
461 CheckPendingDownloads
463 checks if there are free download slots to start new downloads in.
464 To not start too many downloads at once, only one download is added at a time,
465 up to a maximum number of cl_curl_maxdownloads are running.
468 static void CheckPendingDownloads()
472 if(numdownloads < cl_curl_maxdownloads.integer)
475 for(di = downloads; di; di = di->next)
479 Con_Printf("Downloading %s -> %s", di->url, di->filename);
481 di->stream = FS_Open(di->filename, "ab", false, false);
484 Con_Printf("\nFAILED: Could not open output file %s\n", di->filename);
485 Curl_EndDownload(di, CURL_DOWNLOAD_FAILED, CURLE_OK);
489 FS_Seek(di->stream, 0, SEEK_END);
490 di->startpos = FS_Tell(di->stream);
492 Con_Printf(", resuming from position %ld", (long) di->startpos);
495 di->curle = qcurl_easy_init();
496 qcurl_easy_setopt(di->curle, CURLOPT_URL, di->url);
497 qcurl_easy_setopt(di->curle, CURLOPT_USERAGENT, engineversion);
498 qcurl_easy_setopt(di->curle, CURLOPT_REFERER, di->referer);
499 qcurl_easy_setopt(di->curle, CURLOPT_RESUME_FROM, (long) di->startpos);
500 qcurl_easy_setopt(di->curle, CURLOPT_FOLLOWLOCATION, 1);
501 qcurl_easy_setopt(di->curle, CURLOPT_WRITEFUNCTION, CURL_fwrite);
502 qcurl_easy_setopt(di->curle, CURLOPT_LOW_SPEED_LIMIT, (long) 256);
503 qcurl_easy_setopt(di->curle, CURLOPT_LOW_SPEED_TIME, (long) 45);
504 qcurl_easy_setopt(di->curle, CURLOPT_WRITEDATA, (void *) di);
505 qcurl_easy_setopt(di->curle, CURLOPT_PRIVATE, (void *) di);
506 qcurl_multi_add_handle(curlm, di->curle);
509 if(numdownloads >= cl_curl_maxdownloads.integer)
520 this function MUST be called before using anything else in this file.
521 On Win32, this must be called AFTER WSAStartup has been done!
529 qcurl_global_init(CURL_GLOBAL_NOTHING);
530 curlm = qcurl_multi_init();
537 Surprise... closes all the stuff. Please do this BEFORE shutting down LHNET.
540 void Curl_ClearRequirements();
545 Curl_ClearRequirements();
555 Finds the internal information block for a download given by file name.
558 static downloadinfo *Curl_Find(const char *filename)
563 for(di = downloads; di; di = di->next)
564 if(!strcasecmp(di->filename, filename))
573 Starts a download of a given URL to the file name portion of this URL (or name
574 if given) in the "dlcache/" folder.
577 void Curl_Begin(const char *URL, const char *name, qboolean ispak, qboolean forthismap)
588 // Note: This extraction of the file name portion is NOT entirely correct.
590 // It does the following:
592 // http://host/some/script.cgi/SomeFile.pk3?uid=ABCDE -> SomeFile.pk3
593 // http://host/some/script.php?uid=ABCDE&file=/SomeFile.pk3 -> SomeFile.pk3
594 // http://host/some/script.php?uid=ABCDE&file=SomeFile.pk3 -> script.php
596 // However, I'd like to keep this "buggy" behavior so that PHP script
597 // authors can write download scripts without having to enable
598 // AcceptPathInfo on Apache. They just have to ensure that their script
599 // can be called with such a "fake" path name like
600 // http://host/some/script.php?uid=ABCDE&file=/SomeFile.pk3
602 // By the way, such PHP scripts should either send the file or a
603 // "Location:" redirect; PHP code example:
605 // header("Location: http://www.example.com/");
607 // By the way, this will set User-Agent to something like
608 // "Nexuiz build 22:27:55 Mar 17 2006" (engineversion) and Referer to
609 // dp://serverhost:serverport/ so you can filter on this; an example
610 // httpd log file line might be:
612 // 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"
616 p = strrchr(name, '/');
617 p = p ? (p+1) : name;
619 length = q ? (size_t)(q - p) : strlen(p);
620 dpsnprintf(fn, sizeof(fn), "dlcache/%.*s", (int)length, p);
622 if(cls.demorecording)
624 void *demobuf; fs_offset_t demofilesize;
625 char msg[MAX_QPATH + 16];
628 sb.data = (void *) msg;
629 sb.maxsize = sizeof(msg);
631 MSG_WriteByte(&sb, svc_stufftext);
632 MSG_WriteString(&sb, va("\ncurl --pak \"%.*s\"\n", (int)length, p));
634 CL_CutDemo(&demobuf, &demofilesize);
635 CL_WriteDemoMessage(&sb);
636 CL_PasteDemo(&demobuf, &demofilesize);
639 // already downloading the file?
641 downloadinfo *di = Curl_Find(fn);
644 Con_Printf("Can't download %s, already getting it from %s!\n", fn, di->url);
646 // however, if it was not for this map yet...
647 if(forthismap && !di->forthismap)
649 di->forthismap = true;
650 // this "fakes" a download attempt so the client will wait for
651 // the download to finish and then reconnect
652 ++numdownloads_added;
659 if(ispak && FS_FileExists(fn))
661 qboolean already_loaded;
662 if(FS_AddPack(fn, &already_loaded, true))
664 Con_DPrintf("%s already exists, not downloading!\n", fn);
666 Con_DPrintf("(pak was already loaded)\n");
671 ++numdownloads_added;
672 ++numdownloads_success;
679 qfile_t *f = FS_Open(fn, "rb", false, false);
683 FS_Read(f, buf, sizeof(buf)); // no "-1", I will use memcmp
685 if(memcmp(buf, "PK\x03\x04", 4) && memcmp(buf, "PACK", 4))
687 Con_DPrintf("Detected non-PAK %s, clearing and NOT resuming.\n", fn);
689 f = FS_Open(fn, "w", false, false);
703 ++numdownloads_added;
704 di = (downloadinfo *) Z_Malloc(sizeof(*di));
705 strlcpy(di->filename, fn, sizeof(di->filename));
706 strlcpy(di->url, URL, sizeof(di->url));
707 dpsnprintf(di->referer, sizeof(di->referer), "dp://%s/", cls.netcon ? cls.netcon->address : "notconnected.invalid");
708 di->forthismap = forthismap;
714 di->bytes_received = 0;
715 di->next = downloads;
728 call this regularily as this will always download as much as possible without
736 if(!cl_curl_enabled.integer)
742 Curl_CheckCommandWhenDone();
747 if(realtime < curltime) // throttle
756 mc = qcurl_multi_perform(curlm, &remaining);
758 while(mc == CURLM_CALL_MULTI_PERFORM);
762 CURLMsg *msg = qcurl_multi_info_read(curlm, &remaining);
765 if(msg->msg == CURLMSG_DONE)
768 CurlStatus failed = CURL_DOWNLOAD_SUCCESS;
770 qcurl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &di);
771 result = msg->data.result;
774 failed = CURL_DOWNLOAD_FAILED;
779 qcurl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &code);
784 failed = CURL_DOWNLOAD_SERVERERROR;
790 Curl_EndDownload(di, failed, result);
795 CheckPendingDownloads();
797 // when will we curl the next time?
798 // we will wait a bit to ensure our download rate is kept.
799 // we now know that realtime >= curltime... so set up a new curltime
800 if(cl_curl_maxspeed.value > 0)
802 unsigned long bytes = bytes_received; // maybe smoothen a bit?
803 curltime = realtime + bytes / (cl_curl_maxspeed.value * 1024.0);
804 bytes_received -= bytes;
817 void Curl_CancelAll()
824 Curl_EndDownload(downloads, CURL_DOWNLOAD_ABORTED, CURLE_OK);
825 // INVARIANT: downloads will point to the next download after that!
833 returns true iff there is a download running.
836 qboolean Curl_Running()
841 return downloads != NULL;
846 Curl_GetDownloadAmount
848 returns a value from 0.0 to 1.0 which represents the downloaded amount of data
849 for the given download.
852 static double Curl_GetDownloadAmount(downloadinfo *di)
859 qcurl_easy_getinfo(di->curle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &length);
861 return di->bytes_received / length;
871 Curl_GetDownloadSpeed
873 returns the speed of the given download in bytes per second
876 static double Curl_GetDownloadSpeed(downloadinfo *di)
883 qcurl_easy_getinfo(di->curle, CURLINFO_SPEED_DOWNLOAD, &speed);
894 prints the download list
897 // TODO rewrite using Curl_GetDownloadInfo?
898 static void Curl_Info_f()
905 Con_Print("Currently running downloads:\n");
906 for(di = downloads; di; di = di->next)
908 double speed, percent;
909 Con_Printf(" %s -> %s ", di->url, di->filename);
910 percent = 100.0 * Curl_GetDownloadAmount(di);
911 speed = Curl_GetDownloadSpeed(di);
913 Con_Printf("(%.1f%% @ %.1f KiB/s)\n", percent, speed / 1024.0);
915 Con_Print("(queued)\n");
920 Con_Print("No downloads running.\n");
928 implements the "curl" console command
932 curl --cancel filename
937 curl [--pak] [--forthismap] [--for filename filename...] url
938 --pak: after downloading, load the package into the virtual file system
939 --for filename...: only download of at least one of the named files is missing
940 --forthismap: don't reconnect on failure
942 curl --clear_autodownload
943 clears the download success/failure counters
945 curl --finish_autodownload
946 if at least one download has been started, disconnect and drop to the menu
947 once the last download completes successfully, reconnect to the current server
950 void Curl_Curl_f(void)
954 qboolean pak = false;
955 qboolean forthismap = false;
957 const char *name = 0;
961 Con_Print("libcurl DLL not found, this command is inactive.\n");
965 if(!cl_curl_enabled.integer)
967 Con_Print("curl support not enabled. Set cl_curl_enabled to 1 to enable.\n");
971 for(i = 0; i != Cmd_Argc(); ++i)
972 Con_DPrintf("%s ", Cmd_Argv(i));
977 Con_Print("usage:\ncurl --info, curl --cancel [filename], curl url\n");
981 url = Cmd_Argv(Cmd_Argc() - 1);
984 for(i = 1; i != end; ++i)
986 const char *a = Cmd_Argv(i);
987 if(!strcmp(a, "--info"))
992 else if(!strcmp(a, "--cancel"))
994 if(i == end - 1) // last argument
998 downloadinfo *di = Curl_Find(url);
1000 Curl_EndDownload(di, CURL_DOWNLOAD_ABORTED, CURLE_OK);
1002 Con_Print("download not found\n");
1006 else if(!strcmp(a, "--pak"))
1010 else if(!strcmp(a, "--for"))
1012 for(i = i + 1; i != end - 1; ++i)
1014 if(!FS_FileExists(Cmd_Argv(i)))
1015 goto needthefile; // why can't I have a "double break"?
1017 // if we get here, we have all the files...
1020 else if(!strcmp(a, "--forthismap"))
1024 else if(!strcmp(a, "--as"))
1032 else if(!strcmp(a, "--clear_autodownload"))
1034 // mark all running downloads as "not for this map", so if they
1035 // fail, it does not matter
1036 Curl_Clear_forthismap();
1039 else if(!strcmp(a, "--finish_autodownload"))
1041 if(numdownloads_added)
1043 char donecommand[256];
1046 if(cl.loadbegun) // curling won't inhibit loading the map any more when at this stage, so bail out and force a reconnect
1048 dpsnprintf(donecommand, sizeof(donecommand), "connect %s", cls.netcon->address);
1049 Curl_CommandWhenDone(donecommand);
1053 Curl_CheckCommandWhenDone();
1056 Curl_Register_predownload();
1063 Con_Printf("invalid option %s\n", a);
1069 Curl_Begin(url, name, pak, forthismap);
1073 ====================
1076 loads the commands and cvars this library uses
1077 ====================
1079 void Curl_Init_Commands(void)
1081 Cvar_RegisterVariable (&cl_curl_enabled);
1082 Cvar_RegisterVariable (&cl_curl_maxdownloads);
1083 Cvar_RegisterVariable (&cl_curl_maxspeed);
1084 Cvar_RegisterVariable (&sv_curl_defaulturl);
1085 Cvar_RegisterVariable (&sv_curl_serverpackages);
1086 Cmd_AddCommand ("curl", Curl_Curl_f, "download data from an URL and add to search path");
1090 ====================
1091 Curl_GetDownloadInfo
1093 returns an array of Curl_downloadinfo_t structs for usage by GUIs.
1094 The number of elements in the array is returned in int *nDownloads.
1095 const char **additional_info may be set to a string of additional user
1096 information, or to NULL if no such display shall occur. The returned
1097 array must be freed later using Z_Free.
1098 ====================
1100 Curl_downloadinfo_t *Curl_GetDownloadInfo(int *nDownloads, const char **additional_info)
1104 Curl_downloadinfo_t *downinfo;
1105 static char addinfo[128];
1111 *additional_info = NULL;
1116 for(di = downloads; di; di = di->next)
1119 downinfo = (Curl_downloadinfo_t *) Z_Malloc(sizeof(*downinfo) * n);
1121 for(di = downloads; di; di = di->next)
1123 strlcpy(downinfo[i].filename, di->filename, sizeof(downinfo[i].filename));
1126 downinfo[i].progress = Curl_GetDownloadAmount(di);
1127 downinfo[i].speed = Curl_GetDownloadSpeed(di);
1128 downinfo[i].queued = false;
1132 downinfo[i].queued = true;
1139 // TODO: can I clear command_when_done as soon as the first download fails?
1140 if(*command_when_done && !numdownloads_fail && numdownloads_added)
1142 if(!strncmp(command_when_done, "connect ", 8))
1143 dpsnprintf(addinfo, sizeof(addinfo), "(will join %s when done)", command_when_done + 8);
1144 else if(!strcmp(command_when_done, "cl_begindownloads"))
1145 dpsnprintf(addinfo, sizeof(addinfo), "(will enter the game when done)");
1147 dpsnprintf(addinfo, sizeof(addinfo), "(will do '%s' when done)", command_when_done);
1148 *additional_info = addinfo;
1151 *additional_info = NULL;
1160 ====================
1163 finds the URL where to find a given package.
1165 For this, it reads a file "curl_urls.txt" of the following format:
1168 revdm*.pk3 http://revdm/downloads/are/here/
1169 * http://any/other/stuff/is/here/
1171 The URLs should end in /. If not, downloads will still work, but the cached files
1172 can't be just put into the data directory with the same download configuration
1173 (you might want to do this if you want to tag downloaded files from your
1174 server, but you should not). "-" means "don't download".
1176 If no single pattern matched, the cvar sv_curl_defaulturl is used as download
1179 Note: pak1.pak and data*.pk3 are excluded from autodownload at another point in
1180 this file for obvious reasons.
1181 ====================
1183 static const char *Curl_FindPackURL(const char *filename)
1185 static char foundurl[256];
1186 fs_offset_t filesize;
1187 char *buf = (char *) FS_LoadFile("curl_urls.txt", tempmempool, true, &filesize);
1190 // read lines of format "pattern url"
1192 char *pattern = NULL, *patternend = NULL, *url = NULL, *urlend = NULL;
1193 qboolean eof = false;
1205 if(pattern && url && patternend)
1211 if(matchpattern(filename, pattern, true))
1213 strlcpy(foundurl, url, sizeof(foundurl));
1225 if(pattern && !patternend)
1227 else if(url && !urlend)
1233 else if(pattern && patternend && !url)
1242 return sv_curl_defaulturl.string;
1245 typedef struct requirement_s
1247 struct requirement_s *next;
1248 char filename[MAX_QPATH];
1251 static requirement *requirements = NULL;
1255 ====================
1258 Adds the given file to the list of requirements.
1259 ====================
1261 void Curl_RequireFile(const char *filename)
1263 requirement *req = (requirement *) Z_Malloc(sizeof(*requirements));
1264 req->next = requirements;
1265 strlcpy(req->filename, filename, sizeof(req->filename));
1270 ====================
1271 Curl_ClearRequirements
1273 Clears the list of required files for playing on the current map.
1274 This should be called at every map change.
1275 ====================
1277 void Curl_ClearRequirements()
1282 requirement *req = requirements;
1283 requirements = requirements->next;
1286 p = sv_curl_serverpackages.string;
1287 Con_DPrintf("Require all of: %s\n", p);
1288 while(COM_ParseToken_Simple(&p, false, false))
1290 Con_DPrintf("Require: %s\n", com_token);
1291 Curl_RequireFile(com_token);
1296 ====================
1297 Curl_SendRequirements
1299 Makes the current host_clients download all files he needs.
1300 This is done by sending him the following console commands:
1302 curl --clear_autodownload
1303 curl --pak --for maps/pushmoddm1.bsp --forthismap http://where/this/darn/map/is/pushmoddm1.pk3
1304 curl --finish_autodownload
1305 ====================
1307 void Curl_SendRequirements()
1309 // for each requirement, find the pack name
1310 char sendbuffer[4096] = "";
1312 qboolean foundone = false;
1314 for(req = requirements; req; req = req->next)
1317 const char *thispack = FS_WhichPack(req->filename);
1318 const char *packurl;
1323 p = strrchr(thispack, '/');
1327 packurl = Curl_FindPackURL(thispack);
1329 if(packurl && *packurl && strcmp(packurl, "-"))
1332 strlcat(sendbuffer, "curl --clear_autodownload\n", sizeof(sendbuffer));
1334 strlcat(sendbuffer, "curl --pak --forthismap --as ", sizeof(sendbuffer));
1335 strlcat(sendbuffer, thispack, sizeof(sendbuffer));
1336 strlcat(sendbuffer, " --for ", sizeof(sendbuffer));
1337 strlcat(sendbuffer, req->filename, sizeof(sendbuffer));
1338 strlcat(sendbuffer, " ", sizeof(sendbuffer));
1339 strlcat(sendbuffer, packurl, sizeof(sendbuffer));
1340 strlcat(sendbuffer, thispack, sizeof(sendbuffer));
1341 strlcat(sendbuffer, "\n", sizeof(sendbuffer));
1348 strlcat(sendbuffer, "curl --finish_autodownload\n", sizeof(sendbuffer));
1350 if(strlen(sendbuffer) + 1 < sizeof(sendbuffer))
1351 Host_ClientCommands("%s", sendbuffer);
1353 Con_Printf("Could not initiate autodownload due to URL buffer overflow\n");