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 cl_curl_enabled = {1, "cl_curl_enabled","0", "whether client's download support is enabled"};
11 =================================================================
13 Minimal set of definitions from libcurl
15 WARNING: for a matter of simplicity, several pointer types are
16 casted to "void*", and most enumerated values are not included
18 =================================================================
21 typedef struct CURL_s CURL;
22 typedef struct CURLM_s CURLM;
30 CURLM_CALL_MULTI_PERFORM=-1, /* please call curl_multi_perform() soon */
34 #define CURL_GLOBAL_NOTHING 0
35 #define CURL_GLOBAL_SSL 1
36 #define CURL_GLOBAL_WIN32 2
37 #define CURLOPTTYPE_LONG 0
38 #define CURLOPTTYPE_OBJECTPOINT 10000
39 #define CURLOPTTYPE_FUNCTIONPOINT 20000
40 #define CURLOPTTYPE_OFF_T 30000
41 #define CINIT(name,type,number) CURLOPT_ ## name = CURLOPTTYPE_ ## type + number
44 CINIT(WRITEDATA, OBJECTPOINT, 1),
45 CINIT(URL, OBJECTPOINT, 2),
46 CINIT(ERRORBUFFER, OBJECTPOINT, 10),
47 CINIT(WRITEFUNCTION, FUNCTIONPOINT, 11),
48 CINIT(REFERER, OBJECTPOINT, 16),
49 CINIT(USERAGENT, OBJECTPOINT, 18),
50 CINIT(RESUME_FROM, LONG, 21),
51 CINIT(FOLLOWLOCATION, LONG, 52), /* use Location: Luke! */
52 CINIT(PRIVATE, OBJECTPOINT, 103),
58 CURLINFO_HEADER_IN, /* 1 */
59 CURLINFO_HEADER_OUT, /* 2 */
60 CURLINFO_DATA_IN, /* 3 */
61 CURLINFO_DATA_OUT, /* 4 */
62 CURLINFO_SSL_DATA_IN, /* 5 */
63 CURLINFO_SSL_DATA_OUT, /* 6 */
67 #define CURLINFO_STRING 0x100000
68 #define CURLINFO_LONG 0x200000
69 #define CURLINFO_DOUBLE 0x300000
70 #define CURLINFO_SLIST 0x400000
71 #define CURLINFO_MASK 0x0fffff
72 #define CURLINFO_TYPEMASK 0xf00000
75 CURLINFO_NONE, /* first, never use this */
76 CURLINFO_EFFECTIVE_URL = CURLINFO_STRING + 1,
77 CURLINFO_RESPONSE_CODE = CURLINFO_LONG + 2,
78 CURLINFO_TOTAL_TIME = CURLINFO_DOUBLE + 3,
79 CURLINFO_NAMELOOKUP_TIME = CURLINFO_DOUBLE + 4,
80 CURLINFO_CONNECT_TIME = CURLINFO_DOUBLE + 5,
81 CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE + 6,
82 CURLINFO_SIZE_UPLOAD = CURLINFO_DOUBLE + 7,
83 CURLINFO_SIZE_DOWNLOAD = CURLINFO_DOUBLE + 8,
84 CURLINFO_SPEED_DOWNLOAD = CURLINFO_DOUBLE + 9,
85 CURLINFO_SPEED_UPLOAD = CURLINFO_DOUBLE + 10,
86 CURLINFO_HEADER_SIZE = CURLINFO_LONG + 11,
87 CURLINFO_REQUEST_SIZE = CURLINFO_LONG + 12,
88 CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG + 13,
89 CURLINFO_FILETIME = CURLINFO_LONG + 14,
90 CURLINFO_CONTENT_LENGTH_DOWNLOAD = CURLINFO_DOUBLE + 15,
91 CURLINFO_CONTENT_LENGTH_UPLOAD = CURLINFO_DOUBLE + 16,
92 CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE + 17,
93 CURLINFO_CONTENT_TYPE = CURLINFO_STRING + 18,
94 CURLINFO_REDIRECT_TIME = CURLINFO_DOUBLE + 19,
95 CURLINFO_REDIRECT_COUNT = CURLINFO_LONG + 20,
96 CURLINFO_PRIVATE = CURLINFO_STRING + 21,
97 CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG + 22,
98 CURLINFO_HTTPAUTH_AVAIL = CURLINFO_LONG + 23,
99 CURLINFO_PROXYAUTH_AVAIL = CURLINFO_LONG + 24,
100 CURLINFO_OS_ERRNO = CURLINFO_LONG + 25,
101 CURLINFO_NUM_CONNECTS = CURLINFO_LONG + 26,
102 CURLINFO_SSL_ENGINES = CURLINFO_SLIST + 27,
108 CURLMSG_NONE, /* first, not used */
109 CURLMSG_DONE, /* This easy handle has completed. 'result' contains
110 the CURLcode of the transfer */
116 CURLMSG msg; /* what this message means */
117 CURL *easy_handle; /* the handle it concerns */
120 void *whatever; /* message-specific data */
121 CURLcode result; /* return code for transfer */
127 static void (*qcurl_global_init) (long flags);
128 static void (*qcurl_global_cleanup) ();
130 static CURL * (*qcurl_easy_init) ();
131 static void (*qcurl_easy_cleanup) (CURL *handle);
132 static CURLcode (*qcurl_easy_setopt) (CURL *handle, CURLoption option, ...);
133 static CURLcode (*qcurl_easy_getinfo) (CURL *handle, CURLINFO info, ...);
134 static const char * (*qcurl_easy_strerror) (CURLcode);
136 static CURLM * (*qcurl_multi_init) ();
137 static CURLMcode (*qcurl_multi_perform) (CURLM *multi_handle, int *running_handles);
138 static CURLMcode (*qcurl_multi_add_handle) (CURLM *multi_handle, CURL *easy_handle);
139 static CURLMcode (*qcurl_multi_remove_handle) (CURLM *multi_handle, CURL *easy_handle);
140 static CURLMsg * (*qcurl_multi_info_read) (CURLM *multi_handle, int *msgs_in_queue);
141 static void (*qcurl_multi_cleanup) (CURLM *);
142 static const char * (*qcurl_multi_strerror) (CURLcode);
144 static dllfunction_t curlfuncs[] =
146 {"curl_global_init", (void **) &qcurl_global_init},
147 {"curl_global_cleanup", (void **) &qcurl_global_cleanup},
148 {"curl_easy_init", (void **) &qcurl_easy_init},
149 {"curl_easy_cleanup", (void **) &qcurl_easy_cleanup},
150 {"curl_easy_setopt", (void **) &qcurl_easy_setopt},
151 {"curl_easy_strerror", (void **) &qcurl_easy_strerror},
152 {"curl_easy_getinfo", (void **) &qcurl_easy_getinfo},
153 {"curl_multi_init", (void **) &qcurl_multi_init},
154 {"curl_multi_perform", (void **) &qcurl_multi_perform},
155 {"curl_multi_add_handle", (void **) &qcurl_multi_add_handle},
156 {"curl_multi_remove_handle",(void **) &qcurl_multi_remove_handle},
157 {"curl_multi_info_read", (void **) &qcurl_multi_info_read},
158 {"curl_multi_cleanup", (void **) &qcurl_multi_cleanup},
159 {"curl_multi_strerror", (void **) &qcurl_multi_strerror},
163 // Handle for CURL DLL
164 static dllhandle_t curl_dll = NULL;
165 // will be checked at many places to find out if qcurl calls are allowed
167 typedef struct downloadinfo_s
169 char filename[MAX_QPATH];
173 fs_offset_t startpos;
177 unsigned long bytes_received;
178 struct downloadinfo_s *next, *prev;
182 static downloadinfo *downloads = NULL;
183 static int numdownloads = 0;
192 static qboolean CURL_OpenLibrary (void)
194 const char* dllnames [] =
200 #elif defined(MACOSX)
201 "libcurl.3.dylib", // Mac OS X Tiger
202 "libcurl.2.dylib", // Mac OS X Panther
214 if (! Sys_LoadLibrary (dllnames, &curl_dll, curlfuncs))
216 Con_Printf ("cURL support disabled\n");
220 Con_Printf ("cURL support enabled\n");
232 static void CURL_CloseLibrary (void)
234 Sys_UnloadLibrary (&curl_dll);
238 static CURLM *curlm = NULL;
239 static unsigned long bytes_received = 0; // used for bandwidth throttling
240 static double curltime = 0;
246 fwrite-compatible function that writes the data to a file. libcurl can call
250 static size_t CURL_fwrite(void *data, size_t size, size_t nmemb, void *vdi)
253 size_t bytes = size * nmemb;
254 downloadinfo *di = (downloadinfo *) vdi;
256 bytes_received += bytes;
257 di->bytes_received += bytes;
259 ret = FS_Write(di->stream, data, bytes);
261 return ret; // why not ret / nmemb?
266 CURL_DOWNLOAD_SUCCESS = 0,
267 CURL_DOWNLOAD_FAILED,
268 CURL_DOWNLOAD_ABORTED,
269 CURL_DOWNLOAD_SERVERERROR
275 Curl_Clear_forthismap
277 Clears the "will disconnect on failure" flags.
280 void Curl_Clear_forthismap()
283 for(di = downloads; di; di = di->next)
284 di->forthismap = false;
287 static qboolean Curl_Have_forthismap()
290 for(di = downloads; di; di = di->next)
300 stops a download. It receives a status (CURL_DOWNLOAD_SUCCESS,
301 CURL_DOWNLOAD_FAILED or CURL_DOWNLOAD_ABORTED) and in the second case the error
302 code from libcurl, or 0, if another error has occurred.
305 static void Curl_EndDownload(downloadinfo *di, CurlStatus status, CURLcode error)
312 case CURL_DOWNLOAD_SUCCESS:
313 Con_Printf("Download of %s: OK\n", di->filename);
316 case CURL_DOWNLOAD_FAILED:
317 Con_Printf("Download of %s: FAILED\n", di->filename);
319 Con_Printf("Reason given by libcurl: %s\n", qcurl_easy_strerror(error));
321 case CURL_DOWNLOAD_ABORTED:
322 Con_Printf("Download of %s: ABORTED\n", di->filename);
324 case CURL_DOWNLOAD_SERVERERROR:
325 Con_Printf("Download of %s: %d\n", di->filename, (int) error);
327 // reopen to enforce it to have zero bytes again
328 FS_Close(di->stream);
329 di->stream = FS_Open(di->filename, "w", false, false);
336 qcurl_multi_remove_handle(curlm, di->curle);
337 qcurl_easy_cleanup(di->curle);
340 if(ok && !di->bytes_received)
342 Con_Printf("ERROR: empty file\n");
347 FS_Close(di->stream);
351 ok = FS_AddPack(di->filename, NULL, true);
352 if(ok && di->forthismap)
356 if(!ok && di->forthismap)
358 // BAD. Something went totally wrong.
359 // The best we can do is clean up the forthismap flags...
360 Curl_Clear_forthismap();
366 di->prev->next = di->next;
368 downloads = di->next;
370 di->next->prev = di->prev;
378 CheckPendingDownloads
380 checks if there are free download slots to start new downloads in.
381 To not start too many downloads at once, only one download is added at a time,
382 up to a maximum number of cl_curl_maxdownloads are running.
385 static void CheckPendingDownloads()
389 if(numdownloads < cl_curl_maxdownloads.integer)
392 for(di = downloads; di; di = di->next)
396 Con_Printf("Downloading %s -> %s", di->url, di->filename);
398 di->stream = FS_Open(di->filename, "ab", false, false);
401 Con_Printf("\nFAILED: Could not open output file %s\n", di->filename);
402 Curl_EndDownload(di, CURL_DOWNLOAD_FAILED, CURLE_OK);
406 FS_Seek(di->stream, 0, SEEK_END);
407 di->startpos = FS_Tell(di->stream);
409 Con_Printf(", resuming from position %ld", (long) di->startpos);
412 di->curle = qcurl_easy_init();
413 qcurl_easy_setopt(di->curle, CURLOPT_URL, di->url);
414 qcurl_easy_setopt(di->curle, CURLOPT_USERAGENT, engineversion);
415 qcurl_easy_setopt(di->curle, CURLOPT_REFERER, di->referer);
416 qcurl_easy_setopt(di->curle, CURLOPT_RESUME_FROM, (long) di->startpos);
417 qcurl_easy_setopt(di->curle, CURLOPT_FOLLOWLOCATION, 1);
418 qcurl_easy_setopt(di->curle, CURLOPT_WRITEFUNCTION, CURL_fwrite);
419 qcurl_easy_setopt(di->curle, CURLOPT_WRITEDATA, (void *) di);
420 qcurl_easy_setopt(di->curle, CURLOPT_PRIVATE, (void *) di);
421 qcurl_multi_add_handle(curlm, di->curle);
424 if(numdownloads >= cl_curl_maxdownloads.integer)
435 this function MUST be called before using anything else in this file.
436 On Win32, this must be called AFTER WSAStartup has been done!
444 qcurl_global_init(CURL_GLOBAL_NOTHING);
445 curlm = qcurl_multi_init();
452 Surprise... closes all the stuff. Please do this BEFORE shutting down LHNET.
455 void Curl_ClearRequirements();
460 Curl_ClearRequirements();
470 Finds the internal information block for a download given by file name.
473 static downloadinfo *Curl_Find(const char *filename)
478 for(di = downloads; di; di = di->next)
479 if(!strcasecmp(di->filename, filename))
488 Starts a download of a given URL to the file name portion of this URL (or name
489 if given) in the "dlcache/" folder.
492 void Curl_Begin(const char *URL, const char *name, qboolean ispak, qboolean forthismap)
503 // Note: This extraction of the file name portion is NOT entirely correct.
505 // It does the following:
507 // http://host/some/script.cgi/SomeFile.pk3?uid=ABCDE -> SomeFile.pk3
508 // http://host/some/script.php?uid=ABCDE&file=/SomeFile.pk3 -> SomeFile.pk3
509 // http://host/some/script.php?uid=ABCDE&file=SomeFile.pk3 -> script.php
511 // However, I'd like to keep this "buggy" behavior so that PHP script
512 // authors can write download scripts without having to enable
513 // AcceptPathInfo on Apache. They just have to ensure that their script
514 // can be called with such a "fake" path name like
515 // http://host/some/script.php?uid=ABCDE&file=/SomeFile.pk3
517 // By the way, such PHP scripts should either send the file or a
518 // "Location:" redirect; PHP code example:
520 // header("Location: http://www.example.com/");
522 // By the way, this will set User-Agent to something like
523 // "Nexuiz build 22:27:55 Mar 17 2006" (engineversion) and Referer to
524 // dp://serverhost:serverport/ so you can filter on this; an example
525 // httpd log file line might be:
527 // 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"
531 p = strrchr(name, '/');
532 p = p ? (p+1) : name;
534 length = q ? (size_t)(q - p) : strlen(p);
535 dpsnprintf(fn, sizeof(fn), "dlcache/%.*s", (int)length, p);
538 // already downloading the file?
540 downloadinfo *di = Curl_Find(fn);
543 Con_Printf("Can't download %s, already getting it from %s!\n", fn, di->url);
545 // however, if it was not for this map yet...
547 di->forthismap = true;
553 if(ispak && FS_FileExists(fn))
555 qboolean already_loaded;
556 if(FS_AddPack(fn, &already_loaded, true))
558 Con_DPrintf("%s already exists, not downloading!\n", fn);
560 Con_DPrintf("(pak was already loaded)\n");
568 qfile_t *f = FS_Open(fn, "rb", false, false);
572 FS_Read(f, buf, sizeof(buf)); // no "-1", I will use memcmp
574 if(memcmp(buf, "PK\x03\x04", 4) && memcmp(buf, "PACK", 4))
576 Con_DPrintf("Detected non-PAK %s, clearing and NOT resuming.\n", fn);
578 f = FS_Open(fn, "w", false, false);
591 di = (downloadinfo *) Z_Malloc(sizeof(*di));
592 strlcpy(di->filename, fn, sizeof(di->filename));
593 strlcpy(di->url, URL, sizeof(di->url));
594 dpsnprintf(di->referer, sizeof(di->referer), "dp://%s/", cls.netcon ? cls.netcon->address : "notconnected.invalid");
595 di->forthismap = forthismap;
601 di->bytes_received = 0;
602 di->next = downloads;
615 call this regularily as this will always download as much as possible without
621 if(!cl_curl_enabled.integer)
630 if(realtime < curltime) // throttle
639 mc = qcurl_multi_perform(curlm, &remaining);
641 while(mc == CURLM_CALL_MULTI_PERFORM);
645 CURLMsg *msg = qcurl_multi_info_read(curlm, &remaining);
648 if(msg->msg == CURLMSG_DONE)
651 CurlStatus failed = CURL_DOWNLOAD_SUCCESS;
654 qcurl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &di);
655 result = msg->data.result;
658 failed = CURL_DOWNLOAD_FAILED;
663 qcurl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &code);
668 failed = CURL_DOWNLOAD_SERVERERROR;
674 Curl_EndDownload(di, failed, result);
679 CheckPendingDownloads();
681 // when will we curl the next time?
682 // we will wait a bit to ensure our download rate is kept.
683 // we now know that realtime >= curltime... so set up a new curltime
684 if(cl_curl_maxspeed.value > 0)
686 unsigned long bytes = bytes_received; // maybe smoothen a bit?
687 curltime = realtime + bytes / (cl_curl_maxspeed.value * 1024.0);
688 bytes_received -= bytes;
701 void Curl_CancelAll()
708 Curl_EndDownload(downloads, CURL_DOWNLOAD_ABORTED, CURLE_OK);
709 // INVARIANT: downloads will point to the next download after that!
717 returns true iff there is a download running.
720 qboolean Curl_Running()
725 return downloads != NULL;
730 Curl_GetDownloadAmount
732 returns a value from 0.0 to 1.0 which represents the downloaded amount of data
733 for the given download.
736 static double Curl_GetDownloadAmount(downloadinfo *di)
743 qcurl_easy_getinfo(di->curle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &length);
745 return di->bytes_received / length;
755 Curl_GetDownloadSpeed
757 returns the speed of the given download in bytes per second
760 static double Curl_GetDownloadSpeed(downloadinfo *di)
767 qcurl_easy_getinfo(di->curle, CURLINFO_SPEED_DOWNLOAD, &speed);
778 prints the download list
781 // TODO rewrite using Curl_GetDownloadInfo?
782 static void Curl_Info_f()
789 Con_Print("Currently running downloads:\n");
790 for(di = downloads; di; di = di->next)
792 double speed, percent;
793 Con_Printf(" %s -> %s ", di->url, di->filename);
794 percent = 100.0 * Curl_GetDownloadAmount(di);
795 speed = Curl_GetDownloadSpeed(di);
797 Con_Printf("(%.1f%% @ %.1f KiB/s)\n", percent, speed / 1024.0);
799 Con_Print("(queued)\n");
804 Con_Print("No downloads running.\n");
812 implements the "curl" console command
816 curl --cancel filename
821 curl [--pak] [--forthismap] [--for filename filename...] url
822 --pak: after downloading, load the package into the virtual file system
823 --for filename...: only download of at least one of the named files is missing
824 --forthismap: disconnect on failure
827 void Curl_Curl_f(void)
831 qboolean pak = false;
832 qboolean forthismap = false;
834 const char *name = 0;
838 Con_Print("libcurl DLL not found, this command is inactive.\n");
842 if(!cl_curl_enabled.integer)
844 Con_Print("curl support not enabled. Set cl_curl_enabled to 1 to enable.\n");
848 for(i = 0; i != Cmd_Argc(); ++i)
849 Con_DPrintf("%s ", Cmd_Argv(i));
854 Con_Print("usage:\ncurl --info, curl --cancel [filename], curl url\n");
858 url = Cmd_Argv(Cmd_Argc() - 1);
861 for(i = 1; i != end; ++i)
863 const char *a = Cmd_Argv(i);
864 if(!strcmp(a, "--info"))
869 else if(!strcmp(a, "--cancel"))
871 if(i == end - 1) // last argument
875 downloadinfo *di = Curl_Find(url);
876 Curl_EndDownload(di, CURL_DOWNLOAD_ABORTED, CURLE_OK);
880 else if(!strcmp(a, "--pak"))
884 else if(!strcmp(a, "--for"))
886 for(i = i + 1; i != end - 1; ++i)
888 if(!FS_FileExists(Cmd_Argv(i)))
889 goto needthefile; // why can't I have a "double break"?
891 // if we get here, we have all the files...
894 else if(!strcmp(a, "--forthismap"))
898 else if(!strcmp(a, "--as"))
906 else if(!strcmp(a, "--clear_autodownload"))
908 // mark all running downloads as "not for this map", so if they
909 // fail, it does not matter
910 Curl_Clear_forthismap();
913 else if(!strcmp(a, "--finish_autodownload"))
920 Con_Printf("invalid option %s\n", a);
926 Curl_Begin(url, name, pak, forthismap);
933 loads the commands and cvars this library uses
936 void Curl_Init_Commands(void)
938 Cvar_RegisterVariable (&cl_curl_enabled);
939 Cvar_RegisterVariable (&cl_curl_maxdownloads);
940 Cvar_RegisterVariable (&cl_curl_maxspeed);
941 Cvar_RegisterVariable (&sv_curl_defaulturl);
942 Cmd_AddCommand ("curl", Curl_Curl_f, "download data from an URL and add to search path");
949 returns an array of Curl_downloadinfo_t structs for usage by GUIs.
950 The number of elements in the array is returned in int *nDownloads.
951 const char **additional_info may be set to a string of additional user
952 information, or to NULL if no such display shall occur. The returned
953 array must be freed later using Z_Free.
956 Curl_downloadinfo_t *Curl_GetDownloadInfo(int *nDownloads, const char **additional_info)
960 Curl_downloadinfo_t *downinfo;
961 static char addinfo[128];
967 *additional_info = NULL;
972 for(di = downloads; di; di = di->next)
975 downinfo = (Curl_downloadinfo_t *) Z_Malloc(sizeof(*downinfo) * n);
977 for(di = downloads; di; di = di->next)
979 strlcpy(downinfo[i].filename, di->filename, sizeof(downinfo[i].filename));
982 downinfo[i].progress = Curl_GetDownloadAmount(di);
983 downinfo[i].speed = Curl_GetDownloadSpeed(di);
984 downinfo[i].queued = false;
988 downinfo[i].queued = true;
995 // TODO put something better here?
996 // maybe... check if the file is actually needed for the current map?
997 if(Curl_Have_forthismap())
999 dpsnprintf(addinfo, sizeof(addinfo), "please wait for the download to complete");
1000 *additional_info = addinfo;
1003 *additional_info = NULL;
1012 ====================
1015 finds the URL where to find a given package.
1017 For this, it reads a file "curl_urls.txt" of the following format:
1020 revdm*.pk3 http://revdm/downloads/are/here/
1021 * http://any/other/stuff/is/here/
1023 The URLs should end in /. If not, downloads will still work, but the cached files
1024 can't be just put into the data directory with the same download configuration
1025 (you might want to do this if you want to tag downloaded files from your
1026 server, but you should not). "-" means "don't download".
1028 If no single pattern matched, the cvar sv_curl_defaulturl is used as download
1031 Note: pak1.pak and data*.pk3 are excluded from autodownload at another point in
1032 this file for obvious reasons.
1033 ====================
1035 static const char *Curl_FindPackURL(const char *filename)
1037 static char foundurl[256];
1038 fs_offset_t filesize;
1039 char *buf = (char *) FS_LoadFile("curl_urls.txt", tempmempool, true, &filesize);
1042 // read lines of format "pattern url"
1044 char *pattern = NULL, *patternend = NULL, *url = NULL, *urlend = NULL;
1045 qboolean eof = false;
1057 if(pattern && url && patternend)
1063 if(matchpattern(filename, pattern, true))
1065 strlcpy(foundurl, url, sizeof(foundurl));
1077 if(pattern && !patternend)
1079 else if(url && !urlend)
1085 else if(pattern && patternend && !url)
1094 return sv_curl_defaulturl.string;
1097 typedef struct requirement_s
1099 struct requirement_s *next;
1100 char filename[MAX_QPATH];
1103 static requirement *requirements = NULL;
1107 ====================
1108 Curl_ClearRequirements
1110 Clears the list of required files for playing on the current map.
1111 This should be called at every map change.
1112 ====================
1114 void Curl_ClearRequirements()
1118 requirement *req = requirements;
1119 requirements = requirements->next;
1125 ====================
1128 Adds the given file to the list of requirements.
1129 ====================
1131 void Curl_RequireFile(const char *filename)
1133 requirement *req = (requirement *) Z_Malloc(sizeof(*requirements));
1134 req->next = requirements;
1135 strlcpy(req->filename, filename, sizeof(req->filename));
1140 ====================
1141 Curl_SendRequirements
1143 Makes the current host_clients download all files he needs.
1144 This is done by sending him the following console commands:
1146 curl --start_autodownload
1147 curl --pak --for maps/pushmoddm1.bsp --forthismap http://where/this/darn/map/is/pushmoddm1.pk3
1148 curl --finish_autodownload
1149 ====================
1151 void Curl_SendRequirements()
1153 // for each requirement, find the pack name
1154 char sendbuffer[4096] = "";
1156 qboolean foundone = false;
1158 for(req = requirements; req; req = req->next)
1161 const char *thispack = FS_WhichPack(req->filename);
1162 const char *packurl;
1167 p = strrchr(thispack, '/');
1171 packurl = Curl_FindPackURL(thispack);
1173 if(packurl && *packurl && strcmp(packurl, "-"))
1176 strlcat(sendbuffer, "curl --clear_autodownload\n", sizeof(sendbuffer));
1178 strlcat(sendbuffer, "curl --pak --forthismap --as ", sizeof(sendbuffer));
1179 strlcat(sendbuffer, thispack, sizeof(sendbuffer));
1180 strlcat(sendbuffer, " --for ", sizeof(sendbuffer));
1181 strlcat(sendbuffer, req->filename, sizeof(sendbuffer));
1182 strlcat(sendbuffer, " ", sizeof(sendbuffer));
1183 strlcat(sendbuffer, packurl, sizeof(sendbuffer));
1184 strlcat(sendbuffer, thispack, sizeof(sendbuffer));
1185 strlcat(sendbuffer, "\n", sizeof(sendbuffer));
1192 strlcat(sendbuffer, "curl --finish_autodownload\n", sizeof(sendbuffer));
1194 if(strlen(sendbuffer) + 1 < sizeof(sendbuffer))
1195 Host_ClientCommands("%s", sendbuffer);
1197 Con_Printf("Could not initiate autodownload due to URL buffer overflow\n");