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)
359 if(!ok && di->forthismap)
361 // BAD. Something went totally wrong.
362 // The best we can do is clean up the forthismap flags...
363 Curl_Clear_forthismap();
369 di->prev->next = di->next;
371 downloads = di->next;
373 di->next->prev = di->prev;
381 CheckPendingDownloads
383 checks if there are free download slots to start new downloads in.
384 To not start too many downloads at once, only one download is added at a time,
385 up to a maximum number of cl_curl_maxdownloads are running.
388 static void CheckPendingDownloads()
392 if(numdownloads < cl_curl_maxdownloads.integer)
395 for(di = downloads; di; di = di->next)
399 Con_Printf("Downloading %s -> %s", di->url, di->filename);
401 di->stream = FS_Open(di->filename, "ab", false, false);
404 Con_Printf("\nFAILED: Could not open output file %s\n", di->filename);
405 Curl_EndDownload(di, CURL_DOWNLOAD_FAILED, CURLE_OK);
409 FS_Seek(di->stream, 0, SEEK_END);
410 di->startpos = FS_Tell(di->stream);
412 Con_Printf(", resuming from position %ld", (long) di->startpos);
415 di->curle = qcurl_easy_init();
416 qcurl_easy_setopt(di->curle, CURLOPT_URL, di->url);
417 qcurl_easy_setopt(di->curle, CURLOPT_USERAGENT, engineversion);
418 qcurl_easy_setopt(di->curle, CURLOPT_REFERER, di->referer);
419 qcurl_easy_setopt(di->curle, CURLOPT_RESUME_FROM, (long) di->startpos);
420 qcurl_easy_setopt(di->curle, CURLOPT_FOLLOWLOCATION, 1);
421 qcurl_easy_setopt(di->curle, CURLOPT_WRITEFUNCTION, CURL_fwrite);
422 qcurl_easy_setopt(di->curle, CURLOPT_WRITEDATA, (void *) di);
423 qcurl_easy_setopt(di->curle, CURLOPT_PRIVATE, (void *) di);
424 qcurl_multi_add_handle(curlm, di->curle);
427 if(numdownloads >= cl_curl_maxdownloads.integer)
438 this function MUST be called before using anything else in this file.
439 On Win32, this must be called AFTER WSAStartup has been done!
447 qcurl_global_init(CURL_GLOBAL_NOTHING);
448 curlm = qcurl_multi_init();
455 Surprise... closes all the stuff. Please do this BEFORE shutting down LHNET.
458 void Curl_ClearRequirements();
463 Curl_ClearRequirements();
473 Finds the internal information block for a download given by file name.
476 static downloadinfo *Curl_Find(const char *filename)
481 for(di = downloads; di; di = di->next)
482 if(!strcasecmp(di->filename, filename))
491 Starts a download of a given URL to the file name portion of this URL (or name
492 if given) in the "dlcache/" folder.
495 void Curl_Begin(const char *URL, const char *name, qboolean ispak, qboolean forthismap)
506 // Note: This extraction of the file name portion is NOT entirely correct.
508 // It does the following:
510 // http://host/some/script.cgi/SomeFile.pk3?uid=ABCDE -> SomeFile.pk3
511 // http://host/some/script.php?uid=ABCDE&file=/SomeFile.pk3 -> SomeFile.pk3
512 // http://host/some/script.php?uid=ABCDE&file=SomeFile.pk3 -> script.php
514 // However, I'd like to keep this "buggy" behavior so that PHP script
515 // authors can write download scripts without having to enable
516 // AcceptPathInfo on Apache. They just have to ensure that their script
517 // can be called with such a "fake" path name like
518 // http://host/some/script.php?uid=ABCDE&file=/SomeFile.pk3
520 // By the way, such PHP scripts should either send the file or a
521 // "Location:" redirect; PHP code example:
523 // header("Location: http://www.example.com/");
525 // By the way, this will set User-Agent to something like
526 // "Nexuiz build 22:27:55 Mar 17 2006" (engineversion) and Referer to
527 // dp://serverhost:serverport/ so you can filter on this; an example
528 // httpd log file line might be:
530 // 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"
534 p = strrchr(name, '/');
535 p = p ? (p+1) : name;
537 length = q ? (size_t)(q - p) : strlen(p);
538 dpsnprintf(fn, sizeof(fn), "dlcache/%.*s", (int)length, p);
541 // already downloading the file?
543 downloadinfo *di = Curl_Find(fn);
546 Con_Printf("Can't download %s, already getting it from %s!\n", fn, di->url);
548 // however, if it was not for this map yet...
550 di->forthismap = true;
556 if(ispak && FS_FileExists(fn))
558 qboolean already_loaded;
559 if(FS_AddPack(fn, &already_loaded, true))
561 Con_DPrintf("%s already exists, not downloading!\n", fn);
563 Con_DPrintf("(pak was already loaded)\n");
574 qfile_t *f = FS_Open(fn, "rb", false, false);
578 FS_Read(f, buf, sizeof(buf)); // no "-1", I will use memcmp
580 if(memcmp(buf, "PK\x03\x04", 4) && memcmp(buf, "PACK", 4))
582 Con_DPrintf("Detected non-PAK %s, clearing and NOT resuming.\n", fn);
584 f = FS_Open(fn, "w", false, false);
597 di = (downloadinfo *) Z_Malloc(sizeof(*di));
598 strlcpy(di->filename, fn, sizeof(di->filename));
599 strlcpy(di->url, URL, sizeof(di->url));
600 dpsnprintf(di->referer, sizeof(di->referer), "dp://%s/", cls.netcon ? cls.netcon->address : "notconnected.invalid");
601 di->forthismap = forthismap;
607 di->bytes_received = 0;
608 di->next = downloads;
621 call this regularily as this will always download as much as possible without
627 if(!cl_curl_enabled.integer)
636 if(realtime < curltime) // throttle
645 mc = qcurl_multi_perform(curlm, &remaining);
647 while(mc == CURLM_CALL_MULTI_PERFORM);
651 CURLMsg *msg = qcurl_multi_info_read(curlm, &remaining);
654 if(msg->msg == CURLMSG_DONE)
657 CurlStatus failed = CURL_DOWNLOAD_SUCCESS;
660 qcurl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &di);
661 result = msg->data.result;
664 failed = CURL_DOWNLOAD_FAILED;
669 qcurl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &code);
674 failed = CURL_DOWNLOAD_SERVERERROR;
680 Curl_EndDownload(di, failed, result);
685 CheckPendingDownloads();
687 // when will we curl the next time?
688 // we will wait a bit to ensure our download rate is kept.
689 // we now know that realtime >= curltime... so set up a new curltime
690 if(cl_curl_maxspeed.value > 0)
692 unsigned long bytes = bytes_received; // maybe smoothen a bit?
693 curltime = realtime + bytes / (cl_curl_maxspeed.value * 1024.0);
694 bytes_received -= bytes;
707 void Curl_CancelAll()
714 Curl_EndDownload(downloads, CURL_DOWNLOAD_ABORTED, CURLE_OK);
715 // INVARIANT: downloads will point to the next download after that!
723 returns true iff there is a download running.
726 qboolean Curl_Running()
731 return downloads != NULL;
736 Curl_GetDownloadAmount
738 returns a value from 0.0 to 1.0 which represents the downloaded amount of data
739 for the given download.
742 static double Curl_GetDownloadAmount(downloadinfo *di)
749 qcurl_easy_getinfo(di->curle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &length);
751 return di->bytes_received / length;
761 Curl_GetDownloadSpeed
763 returns the speed of the given download in bytes per second
766 static double Curl_GetDownloadSpeed(downloadinfo *di)
773 qcurl_easy_getinfo(di->curle, CURLINFO_SPEED_DOWNLOAD, &speed);
784 prints the download list
787 // TODO rewrite using Curl_GetDownloadInfo?
788 static void Curl_Info_f()
795 Con_Print("Currently running downloads:\n");
796 for(di = downloads; di; di = di->next)
798 double speed, percent;
799 Con_Printf(" %s -> %s ", di->url, di->filename);
800 percent = 100.0 * Curl_GetDownloadAmount(di);
801 speed = Curl_GetDownloadSpeed(di);
803 Con_Printf("(%.1f%% @ %.1f KiB/s)\n", percent, speed / 1024.0);
805 Con_Print("(queued)\n");
810 Con_Print("No downloads running.\n");
818 implements the "curl" console command
822 curl --cancel filename
827 curl [--pak] [--forthismap] [--for filename filename...] url
828 --pak: after downloading, load the package into the virtual file system
829 --for filename...: only download of at least one of the named files is missing
830 --forthismap: disconnect on failure
833 void Curl_Curl_f(void)
837 qboolean pak = false;
838 qboolean forthismap = false;
840 const char *name = 0;
844 Con_Print("libcurl DLL not found, this command is inactive.\n");
848 if(!cl_curl_enabled.integer)
850 Con_Print("curl support not enabled. Set cl_curl_enabled to 1 to enable.\n");
854 for(i = 0; i != Cmd_Argc(); ++i)
855 Con_DPrintf("%s ", Cmd_Argv(i));
860 Con_Print("usage:\ncurl --info, curl --cancel [filename], curl url\n");
864 url = Cmd_Argv(Cmd_Argc() - 1);
867 for(i = 1; i != end; ++i)
869 const char *a = Cmd_Argv(i);
870 if(!strcmp(a, "--info"))
875 else if(!strcmp(a, "--cancel"))
877 if(i == end - 1) // last argument
881 downloadinfo *di = Curl_Find(url);
882 Curl_EndDownload(di, CURL_DOWNLOAD_ABORTED, CURLE_OK);
886 else if(!strcmp(a, "--pak"))
890 else if(!strcmp(a, "--for"))
892 for(i = i + 1; i != end - 1; ++i)
894 if(!FS_FileExists(Cmd_Argv(i)))
895 goto needthefile; // why can't I have a "double break"?
897 // if we get here, we have all the files...
900 else if(!strcmp(a, "--forthismap"))
904 else if(!strcmp(a, "--as"))
912 else if(!strcmp(a, "--clear_autodownload"))
914 // mark all running downloads as "not for this map", so if they
915 // fail, it does not matter
916 Curl_Clear_forthismap();
919 else if(!strcmp(a, "--finish_autodownload"))
926 Con_Printf("invalid option %s\n", a);
932 Curl_Begin(url, name, pak, forthismap);
939 loads the commands and cvars this library uses
942 void Curl_Init_Commands(void)
944 Cvar_RegisterVariable (&cl_curl_enabled);
945 Cvar_RegisterVariable (&cl_curl_maxdownloads);
946 Cvar_RegisterVariable (&cl_curl_maxspeed);
947 Cvar_RegisterVariable (&sv_curl_defaulturl);
948 Cmd_AddCommand ("curl", Curl_Curl_f, "download data from an URL and add to search path");
955 returns an array of Curl_downloadinfo_t structs for usage by GUIs.
956 The number of elements in the array is returned in int *nDownloads.
957 const char **additional_info may be set to a string of additional user
958 information, or to NULL if no such display shall occur. The returned
959 array must be freed later using Z_Free.
962 Curl_downloadinfo_t *Curl_GetDownloadInfo(int *nDownloads, const char **additional_info)
966 Curl_downloadinfo_t *downinfo;
967 static char addinfo[128];
973 *additional_info = NULL;
978 for(di = downloads; di; di = di->next)
981 downinfo = (Curl_downloadinfo_t *) Z_Malloc(sizeof(*downinfo) * n);
983 for(di = downloads; di; di = di->next)
985 strlcpy(downinfo[i].filename, di->filename, sizeof(downinfo[i].filename));
988 downinfo[i].progress = Curl_GetDownloadAmount(di);
989 downinfo[i].speed = Curl_GetDownloadSpeed(di);
990 downinfo[i].queued = false;
994 downinfo[i].queued = true;
1001 // TODO put something better here?
1002 // maybe... check if the file is actually needed for the current map?
1003 if(Curl_Have_forthismap())
1005 dpsnprintf(addinfo, sizeof(addinfo), "please wait for the download to complete");
1006 *additional_info = addinfo;
1009 *additional_info = NULL;
1018 ====================
1021 finds the URL where to find a given package.
1023 For this, it reads a file "curl_urls.txt" of the following format:
1026 revdm*.pk3 http://revdm/downloads/are/here/
1027 * http://any/other/stuff/is/here/
1029 The URLs should end in /. If not, downloads will still work, but the cached files
1030 can't be just put into the data directory with the same download configuration
1031 (you might want to do this if you want to tag downloaded files from your
1032 server, but you should not). "-" means "don't download".
1034 If no single pattern matched, the cvar sv_curl_defaulturl is used as download
1037 Note: pak1.pak and data*.pk3 are excluded from autodownload at another point in
1038 this file for obvious reasons.
1039 ====================
1041 static const char *Curl_FindPackURL(const char *filename)
1043 static char foundurl[256];
1044 fs_offset_t filesize;
1045 char *buf = (char *) FS_LoadFile("curl_urls.txt", tempmempool, true, &filesize);
1048 // read lines of format "pattern url"
1050 char *pattern = NULL, *patternend = NULL, *url = NULL, *urlend = NULL;
1051 qboolean eof = false;
1063 if(pattern && url && patternend)
1069 if(matchpattern(filename, pattern, true))
1071 strlcpy(foundurl, url, sizeof(foundurl));
1083 if(pattern && !patternend)
1085 else if(url && !urlend)
1091 else if(pattern && patternend && !url)
1100 return sv_curl_defaulturl.string;
1103 typedef struct requirement_s
1105 struct requirement_s *next;
1106 char filename[MAX_QPATH];
1109 static requirement *requirements = NULL;
1113 ====================
1114 Curl_ClearRequirements
1116 Clears the list of required files for playing on the current map.
1117 This should be called at every map change.
1118 ====================
1120 void Curl_ClearRequirements()
1124 requirement *req = requirements;
1125 requirements = requirements->next;
1131 ====================
1134 Adds the given file to the list of requirements.
1135 ====================
1137 void Curl_RequireFile(const char *filename)
1139 requirement *req = (requirement *) Z_Malloc(sizeof(*requirements));
1140 req->next = requirements;
1141 strlcpy(req->filename, filename, sizeof(req->filename));
1146 ====================
1147 Curl_SendRequirements
1149 Makes the current host_clients download all files he needs.
1150 This is done by sending him the following console commands:
1152 curl --start_autodownload
1153 curl --pak --for maps/pushmoddm1.bsp --forthismap http://where/this/darn/map/is/pushmoddm1.pk3
1154 curl --finish_autodownload
1155 ====================
1157 void Curl_SendRequirements()
1159 // for each requirement, find the pack name
1160 char sendbuffer[4096] = "";
1162 qboolean foundone = false;
1164 for(req = requirements; req; req = req->next)
1167 const char *thispack = FS_WhichPack(req->filename);
1168 const char *packurl;
1173 p = strrchr(thispack, '/');
1177 packurl = Curl_FindPackURL(thispack);
1179 if(packurl && *packurl && strcmp(packurl, "-"))
1182 strlcat(sendbuffer, "curl --clear_autodownload\n", sizeof(sendbuffer));
1184 strlcat(sendbuffer, "curl --pak --forthismap --as ", sizeof(sendbuffer));
1185 strlcat(sendbuffer, thispack, sizeof(sendbuffer));
1186 strlcat(sendbuffer, " --for ", sizeof(sendbuffer));
1187 strlcat(sendbuffer, req->filename, sizeof(sendbuffer));
1188 strlcat(sendbuffer, " ", sizeof(sendbuffer));
1189 strlcat(sendbuffer, packurl, sizeof(sendbuffer));
1190 strlcat(sendbuffer, thispack, sizeof(sendbuffer));
1191 strlcat(sendbuffer, "\n", sizeof(sendbuffer));
1198 strlcat(sendbuffer, "curl --finish_autodownload\n", sizeof(sendbuffer));
1200 if(strlen(sendbuffer) + 1 < sizeof(sendbuffer))
1201 Host_ClientCommands("%s", sendbuffer);
1203 Con_Printf("Could not initiate autodownload due to URL buffer overflow\n");