]> git.xonotic.org Git - xonotic/darkplaces.git/blob - libcurl.c
s/QPATH/OSPATH/g in libcurl
[xonotic/darkplaces.git] / libcurl.c
1 #include "quakedef.h"
2 #include "fs.h"
3 #include "libcurl.h"
4
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"};
10
11 /*
12 =================================================================
13
14   Minimal set of definitions from libcurl
15
16   WARNING: for a matter of simplicity, several pointer types are
17   casted to "void*", and most enumerated values are not included
18
19 =================================================================
20 */
21
22 typedef struct CURL_s CURL;
23 typedef struct CURLM_s CURLM;
24 typedef enum
25 {
26         CURLE_OK = 0
27 }
28 CURLcode;
29 typedef enum
30 {
31         CURLM_CALL_MULTI_PERFORM=-1, /* please call curl_multi_perform() soon */
32         CURLM_OK = 0
33 }
34 CURLMcode;
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
43 typedef enum
44 {
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),
56 }
57 CURLoption;
58 typedef enum
59 {
60         CURLINFO_TEXT = 0,
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 */
67         CURLINFO_END
68 }
69 curl_infotype;
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
76 typedef enum
77 {
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,
106 }
107 CURLINFO;
108
109 typedef enum
110 {
111         CURLMSG_NONE, /* first, not used */
112         CURLMSG_DONE, /* This easy handle has completed. 'result' contains
113                                          the CURLcode of the transfer */
114         CURLMSG_LAST
115 }
116 CURLMSG;
117 typedef struct
118 {
119         CURLMSG msg;       /* what this message means */
120         CURL *easy_handle; /* the handle it concerns */
121         union
122         {
123                 void *whatever;    /* message-specific data */
124                 CURLcode result;   /* return code for transfer */
125         }
126         data;
127 }
128 CURLMsg;
129
130 static void (*qcurl_global_init) (long flags);
131 static void (*qcurl_global_cleanup) (void);
132
133 static CURL * (*qcurl_easy_init) (void);
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);
138
139 static CURLM * (*qcurl_multi_init) (void);
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);
146
147 static dllfunction_t curlfuncs[] =
148 {
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},
163         {NULL, NULL}
164 };
165
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
169
170 typedef struct downloadinfo_s
171 {
172         char filename[MAX_OSPATH];
173         char url[1024];
174         char referer[256];
175         qfile_t *stream;
176         fs_offset_t startpos;
177         CURL *curle;
178         qboolean started;
179         qboolean ispak;
180         unsigned long bytes_received;
181         struct downloadinfo_s *next, *prev;
182         qboolean forthismap;
183
184         unsigned char *buffer;
185         size_t buffersize;
186         curl_callback_t callback;
187         void *callback_data;
188 }
189 downloadinfo;
190 static downloadinfo *downloads = NULL;
191 static int numdownloads = 0;
192
193 static qboolean noclear = FALSE;
194
195 static int numdownloads_fail = 0;
196 static int numdownloads_success = 0;
197 static int numdownloads_added = 0;
198 static char command_when_done[256] = "";
199 static char command_when_error[256] = "";
200
201 /*
202 ====================
203 Curl_CommandWhenDone
204
205 Sets the command which is to be executed when the last download completes AND
206 all downloads since last server connect ended with a successful status.
207 Setting the command to NULL clears it.
208 ====================
209 */
210 void Curl_CommandWhenDone(const char *cmd)
211 {
212         if(!curl_dll)
213                 return;
214         if(cmd)
215                 strlcpy(command_when_done, cmd, sizeof(command_when_done));
216         else
217                 *command_when_done = 0;
218 }
219
220 /*
221 FIXME
222 Do not use yet. Not complete.
223 Problem: what counts as an error?
224 */
225
226 void Curl_CommandWhenError(const char *cmd)
227 {
228         if(!curl_dll)
229                 return;
230         if(cmd)
231                 strlcpy(command_when_error, cmd, sizeof(command_when_error));
232         else
233                 *command_when_error = 0;
234 }
235
236 /*
237 ====================
238 Curl_Clear_forthismap
239
240 Clears the "will disconnect on failure" flags.
241 ====================
242 */
243 void Curl_Clear_forthismap(void)
244 {
245         downloadinfo *di;
246         if(noclear)
247                 return;
248         for(di = downloads; di; di = di->next)
249                 di->forthismap = false;
250         Curl_CommandWhenError(NULL);
251         Curl_CommandWhenDone(NULL);
252         numdownloads_fail = 0;
253         numdownloads_success = 0;
254         numdownloads_added = 0;
255 }
256
257 /*
258 ====================
259 Curl_Have_forthismap
260
261 Returns true if a download needed for the current game is running.
262 ====================
263 */
264 qboolean Curl_Have_forthismap(void)
265 {
266         return numdownloads_added;
267 }
268
269 void Curl_Register_predownload(void)
270 {
271         Curl_CommandWhenDone("cl_begindownloads");
272         Curl_CommandWhenError("cl_begindownloads");
273 }
274
275 /*
276 ====================
277 Curl_CheckCommandWhenDone
278
279 Checks if a "done command" is to be executed.
280 All downloads finished, at least one success since connect, no single failure
281 -> execute the command.
282 */
283 static void Curl_CheckCommandWhenDone(void)
284 {
285         if(!curl_dll)
286                 return;
287         if(numdownloads_added && (numdownloads_success == numdownloads_added) && *command_when_done)
288         {
289                 Con_DPrintf("cURL downloads occurred, executing %s\n", command_when_done);
290                 Cbuf_AddText("\n");
291                 Cbuf_AddText(command_when_done);
292                 Cbuf_AddText("\n");
293                 Curl_Clear_forthismap();
294         }
295         else if(numdownloads_added && numdownloads_fail && *command_when_error)
296         {
297                 Con_DPrintf("cURL downloads FAILED, executing %s\n", command_when_error);
298                 Cbuf_AddText("\n");
299                 Cbuf_AddText(command_when_error);
300                 Cbuf_AddText("\n");
301                 Curl_Clear_forthismap();
302         }
303 }
304
305 /*
306 ====================
307 CURL_CloseLibrary
308
309 Load the cURL DLL
310 ====================
311 */
312 static qboolean CURL_OpenLibrary (void)
313 {
314         const char* dllnames [] =
315         {
316 #if defined(WIN64)
317                 "libcurl64.dll",
318 #elif defined(WIN32)
319                 "libcurl-4.dll",
320                 "libcurl-3.dll",
321 #elif defined(MACOSX)
322                 "libcurl.4.dylib", // Mac OS X Notyetreleased
323                 "libcurl.3.dylib", // Mac OS X Tiger
324                 "libcurl.2.dylib", // Mac OS X Panther
325 #else
326                 "libcurl.so.4",
327                 "libcurl.so.3",
328                 "libcurl.so", // FreeBSD
329 #endif
330                 NULL
331         };
332
333         // Already loaded?
334         if (curl_dll)
335                 return true;
336
337         // Load the DLL
338         return Sys_LoadLibrary (dllnames, &curl_dll, curlfuncs);
339 }
340
341
342 /*
343 ====================
344 CURL_CloseLibrary
345
346 Unload the cURL DLL
347 ====================
348 */
349 static void CURL_CloseLibrary (void)
350 {
351         Sys_UnloadLibrary (&curl_dll);
352 }
353
354
355 static CURLM *curlm = NULL;
356 static unsigned long bytes_received = 0; // used for bandwidth throttling
357 static double curltime = 0;
358
359 /*
360 ====================
361 CURL_fwrite
362
363 fwrite-compatible function that writes the data to a file. libcurl can call
364 this.
365 ====================
366 */
367 static size_t CURL_fwrite(void *data, size_t size, size_t nmemb, void *vdi)
368 {
369         fs_offset_t ret = -1;
370         size_t bytes = size * nmemb;
371         downloadinfo *di = (downloadinfo *) vdi;
372
373         if(di->buffer)
374         {
375                 if(di->bytes_received + bytes <= di->buffersize)
376                 {
377                         memcpy(di->buffer + di->bytes_received, data, bytes);
378                         ret = bytes;
379                 }
380                 // otherwise: buffer overrun, ret stays -1
381         }
382
383         if(di->stream)
384         {
385                 ret = FS_Write(di->stream, data, bytes);
386         }
387
388         bytes_received += bytes;
389         di->bytes_received += bytes;
390
391         return ret; // why not ret / nmemb?
392 }
393
394 typedef enum
395 {
396         CURL_DOWNLOAD_SUCCESS = 0,
397         CURL_DOWNLOAD_FAILED,
398         CURL_DOWNLOAD_ABORTED,
399         CURL_DOWNLOAD_SERVERERROR
400 }
401 CurlStatus;
402
403 static void curl_default_callback(int status, size_t length_received, unsigned char *buffer, void *cbdata)
404 {
405         downloadinfo *di = (downloadinfo *) cbdata;
406         switch(status)
407         {
408                 case CURLCBSTATUS_OK:
409                         Con_Printf("Download of %s: OK\n", di->filename);
410                         break;
411                 case CURLCBSTATUS_FAILED:
412                         Con_Printf("Download of %s: FAILED\n", di->filename);
413                         break;
414                 case CURLCBSTATUS_ABORTED:
415                         Con_Printf("Download of %s: ABORTED\n", di->filename);
416                         break;
417                 case CURLCBSTATUS_SERVERERROR:
418                         Con_Printf("Download of %s: (unknown server error)\n", di->filename);
419                         break;
420                 case CURLCBSTATUS_UNKNOWN:
421                         Con_Printf("Download of %s: (unknown client error)\n", di->filename);
422                         break;
423                 default:
424                         Con_Printf("Download of %s: %d\n", di->filename, status);
425                         break;
426         }
427 }
428
429 static void curl_quiet_callback(int status, size_t length_received, unsigned char *buffer, void *cbdata)
430 {
431         if(developer.integer)
432                 curl_default_callback(status, length_received, buffer, cbdata);
433 }
434
435 /*
436 ====================
437 Curl_EndDownload
438
439 stops a download. It receives a status (CURL_DOWNLOAD_SUCCESS,
440 CURL_DOWNLOAD_FAILED or CURL_DOWNLOAD_ABORTED) and in the second case the error
441 code from libcurl, or 0, if another error has occurred.
442 ====================
443 */
444 static qboolean Curl_Begin(const char *URL, const char *name, qboolean ispak, qboolean forthismap, unsigned char *buf, size_t bufsize, curl_callback_t callback, void *cbdata);
445 static void Curl_EndDownload(downloadinfo *di, CurlStatus status, CURLcode error)
446 {
447         qboolean ok = false;
448         if(!curl_dll)
449                 return;
450         switch(status)
451         {
452                 case CURL_DOWNLOAD_SUCCESS:
453                         ok = true;
454                         di->callback(CURLCBSTATUS_OK, di->bytes_received, di->buffer, di->callback_data);
455                         break;
456                 case CURL_DOWNLOAD_FAILED:
457                         di->callback(CURLCBSTATUS_FAILED, di->bytes_received, di->buffer, di->callback_data);
458                         break;
459                 case CURL_DOWNLOAD_ABORTED:
460                         di->callback(CURLCBSTATUS_ABORTED, di->bytes_received, di->buffer, di->callback_data);
461                         break;
462                 case CURL_DOWNLOAD_SERVERERROR:
463                         // reopen to enforce it to have zero bytes again
464                         if(di->stream)
465                         {
466                                 FS_Close(di->stream);
467                                 di->stream = FS_OpenRealFile(di->filename, "wb", false);
468                         }
469
470                         if(di->callback)
471                                 di->callback(error ? (int) error : CURLCBSTATUS_SERVERERROR, di->bytes_received, di->buffer, di->callback_data);
472                         break;
473                 default:
474                         if(di->callback)
475                                 di->callback(CURLCBSTATUS_UNKNOWN, di->bytes_received, di->buffer, di->callback_data);
476                         break;
477         }
478
479         if(di->curle)
480         {
481                 qcurl_multi_remove_handle(curlm, di->curle);
482                 qcurl_easy_cleanup(di->curle);
483         }
484
485         if(!di->callback && ok && !di->bytes_received)
486         {
487                 Con_Printf("ERROR: empty file\n");
488                 ok = false;
489         }
490
491         if(di->stream)
492                 FS_Close(di->stream);
493
494         if(ok && di->ispak)
495         {
496                 ok = FS_AddPack(di->filename, NULL, true);
497                 if(!ok)
498                 {
499                         // pack loading failed?
500                         // this is critical
501                         // better clear the file again...
502                         di->stream = FS_OpenRealFile(di->filename, "wb", false);
503                         FS_Close(di->stream);
504
505                         if(di->startpos && !di->callback)
506                         {
507                                 // this was a resume?
508                                 // then try to redownload it without reporting the error
509                                 Curl_Begin(di->url, di->filename, di->ispak, di->forthismap, NULL, 0, NULL, NULL);
510                                 di->forthismap = false; // don't count the error
511                         }
512                 }
513         }
514
515         if(di->prev)
516                 di->prev->next = di->next;
517         else
518                 downloads = di->next;
519         if(di->next)
520                 di->next->prev = di->prev;
521
522         --numdownloads;
523         if(di->forthismap)
524         {
525                 if(ok)
526                         ++numdownloads_success;
527                 else
528                         ++numdownloads_fail;
529         }
530         Z_Free(di);
531 }
532
533 /*
534 ====================
535 CheckPendingDownloads
536
537 checks if there are free download slots to start new downloads in.
538 To not start too many downloads at once, only one download is added at a time,
539 up to a maximum number of cl_curl_maxdownloads are running.
540 ====================
541 */
542 static void CheckPendingDownloads(void)
543 {
544         if(!curl_dll)
545                 return;
546         if(numdownloads < cl_curl_maxdownloads.integer)
547         {
548                 downloadinfo *di;
549                 for(di = downloads; di; di = di->next)
550                 {
551                         if(!di->started)
552                         {
553                                 if(!di->buffer)
554                                 {
555                                         Con_Printf("Downloading %s -> %s", di->url, di->filename);
556
557                                         di->stream = FS_OpenRealFile(di->filename, "ab", false);
558                                         if(!di->stream)
559                                         {
560                                                 Con_Printf("\nFAILED: Could not open output file %s\n", di->filename);
561                                                 Curl_EndDownload(di, CURL_DOWNLOAD_FAILED, CURLE_OK);
562                                                 return;
563                                         }
564                                         FS_Seek(di->stream, 0, SEEK_END);
565                                         di->startpos = FS_Tell(di->stream);
566
567                                         if(di->startpos > 0)
568                                                 Con_Printf(", resuming from position %ld", (long) di->startpos);
569                                         Con_Print("...\n");
570                                 }
571                                 else
572                                 {
573                                         Con_DPrintf("Downloading %s -> memory\n", di->url);
574                                         di->startpos = 0;
575                                 }
576
577                                 di->curle = qcurl_easy_init();
578                                 qcurl_easy_setopt(di->curle, CURLOPT_URL, di->url);
579                                 qcurl_easy_setopt(di->curle, CURLOPT_USERAGENT, engineversion);
580                                 qcurl_easy_setopt(di->curle, CURLOPT_REFERER, di->referer);
581                                 qcurl_easy_setopt(di->curle, CURLOPT_RESUME_FROM, (long) di->startpos);
582                                 qcurl_easy_setopt(di->curle, CURLOPT_FOLLOWLOCATION, 1);
583                                 qcurl_easy_setopt(di->curle, CURLOPT_WRITEFUNCTION, CURL_fwrite);
584                                 qcurl_easy_setopt(di->curle, CURLOPT_LOW_SPEED_LIMIT, (long) 256);
585                                 qcurl_easy_setopt(di->curle, CURLOPT_LOW_SPEED_TIME, (long) 45);
586                                 qcurl_easy_setopt(di->curle, CURLOPT_WRITEDATA, (void *) di);
587                                 qcurl_easy_setopt(di->curle, CURLOPT_PRIVATE, (void *) di);
588                                 qcurl_multi_add_handle(curlm, di->curle);
589                                 di->started = true;
590                                 ++numdownloads;
591                                 if(numdownloads >= cl_curl_maxdownloads.integer)
592                                         break;
593                         }
594                 }
595         }
596 }
597
598 /*
599 ====================
600 Curl_Init
601
602 this function MUST be called before using anything else in this file.
603 On Win32, this must be called AFTER WSAStartup has been done!
604 ====================
605 */
606 void Curl_Init(void)
607 {
608         CURL_OpenLibrary();
609         if(!curl_dll)
610                 return;
611         qcurl_global_init(CURL_GLOBAL_NOTHING);
612         curlm = qcurl_multi_init();
613 }
614
615 /*
616 ====================
617 Curl_Shutdown
618
619 Surprise... closes all the stuff. Please do this BEFORE shutting down LHNET.
620 ====================
621 */
622 void Curl_ClearRequirements(void);
623 void Curl_Shutdown(void)
624 {
625         if(!curl_dll)
626                 return;
627         Curl_ClearRequirements();
628         Curl_CancelAll();
629         CURL_CloseLibrary();
630         curl_dll = NULL;
631 }
632
633 /*
634 ====================
635 Curl_Find
636
637 Finds the internal information block for a download given by file name.
638 ====================
639 */
640 static downloadinfo *Curl_Find(const char *filename)
641 {
642         downloadinfo *di;
643         if(!curl_dll)
644                 return NULL;
645         for(di = downloads; di; di = di->next)
646                 if(!strcasecmp(di->filename, filename))
647                         return di;
648         return NULL;
649 }
650
651 void Curl_Cancel_ToMemory(curl_callback_t callback, void *cbdata)
652 {
653         downloadinfo *di;
654         if(!curl_dll)
655                 return;
656         for(di = downloads; di; )
657         {
658                 if(di->callback == callback && di->callback_data == cbdata)
659                 {
660                         di->callback = curl_quiet_callback; // do NOT call the callback
661                         Curl_EndDownload(di, CURL_DOWNLOAD_ABORTED, CURLE_OK);
662                         di = downloads;
663                 }
664                 else
665                         di = di->next;
666         }
667 }
668
669 /*
670 ====================
671 Curl_Begin
672
673 Starts a download of a given URL to the file name portion of this URL (or name
674 if given) in the "dlcache/" folder.
675 ====================
676 */
677 static qboolean Curl_Begin(const char *URL, const char *name, qboolean ispak, qboolean forthismap, unsigned char *buf, size_t bufsize, curl_callback_t callback, void *cbdata)
678 {
679         if(!curl_dll)
680         {
681                 return false;
682         }
683         else
684         {
685                 char fn[MAX_OSPATH];
686                 const char *p, *q;
687                 size_t length;
688                 downloadinfo *di;
689
690                 // Note: This extraction of the file name portion is NOT entirely correct.
691                 //
692                 // It does the following:
693                 //
694                 //   http://host/some/script.cgi/SomeFile.pk3?uid=ABCDE -> SomeFile.pk3
695                 //   http://host/some/script.php?uid=ABCDE&file=/SomeFile.pk3 -> SomeFile.pk3
696                 //   http://host/some/script.php?uid=ABCDE&file=SomeFile.pk3 -> script.php
697                 //
698                 // However, I'd like to keep this "buggy" behavior so that PHP script
699                 // authors can write download scripts without having to enable
700                 // AcceptPathInfo on Apache. They just have to ensure that their script
701                 // can be called with such a "fake" path name like
702                 // http://host/some/script.php?uid=ABCDE&file=/SomeFile.pk3
703                 //
704                 // By the way, such PHP scripts should either send the file or a
705                 // "Location:" redirect; PHP code example:
706                 //
707                 //   header("Location: http://www.example.com/");
708                 //
709                 // By the way, this will set User-Agent to something like
710                 // "Nexuiz build 22:27:55 Mar 17 2006" (engineversion) and Referer to
711                 // dp://serverhost:serverport/ so you can filter on this; an example
712                 // httpd log file line might be:
713                 //
714                 //   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"
715
716                 if(!name)
717                         name = URL;
718
719                 if(!buf)
720                 {
721                         p = strrchr(name, '/');
722                         p = p ? (p+1) : name;
723                         q = strchr(p, '?');
724                         length = q ? (size_t)(q - p) : strlen(p);
725                         dpsnprintf(fn, sizeof(fn), "dlcache/%.*s", (int)length, p);
726
727                         name = fn; // make it point back
728
729                         // already downloading the file?
730                         {
731                                 downloadinfo *di = Curl_Find(fn);
732                                 if(di)
733                                 {
734                                         Con_Printf("Can't download %s, already getting it from %s!\n", fn, di->url);
735
736                                         // however, if it was not for this map yet...
737                                         if(forthismap && !di->forthismap)
738                                         {
739                                                 di->forthismap = true;
740                                                 // this "fakes" a download attempt so the client will wait for
741                                                 // the download to finish and then reconnect
742                                                 ++numdownloads_added;
743                                         }
744
745                                         return false;
746                                 }
747                         }
748
749                         if(ispak && FS_FileExists(fn))
750                         {
751                                 qboolean already_loaded;
752                                 if(FS_AddPack(fn, &already_loaded, true))
753                                 {
754                                         Con_DPrintf("%s already exists, not downloading!\n", fn);
755                                         if(already_loaded)
756                                                 Con_DPrintf("(pak was already loaded)\n");
757                                         else
758                                         {
759                                                 if(forthismap)
760                                                 {
761                                                         ++numdownloads_added;
762                                                         ++numdownloads_success;
763                                                 }
764                                         }
765
766                                         return false;
767                                 }
768                                 else
769                                 {
770                                         qfile_t *f = FS_OpenRealFile(fn, "rb", false);
771                                         if(f)
772                                         {
773                                                 char buf[4] = {0};
774                                                 FS_Read(f, buf, sizeof(buf)); // no "-1", I will use memcmp
775
776                                                 if(memcmp(buf, "PK\x03\x04", 4) && memcmp(buf, "PACK", 4))
777                                                 {
778                                                         Con_DPrintf("Detected non-PAK %s, clearing and NOT resuming.\n", fn);
779                                                         FS_Close(f);
780                                                         f = FS_OpenRealFile(fn, "wb", false);
781                                                         if(f)
782                                                                 FS_Close(f);
783                                                 }
784                                                 else
785                                                 {
786                                                         // OK
787                                                         FS_Close(f);
788                                                 }
789                                         }
790                                 }
791                         }
792                 }
793
794                 // if we get here, we actually want to download... so first verify the
795                 // URL scheme (so one can't read local files using file://)
796                 if(strncmp(URL, "http://", 7) && strncmp(URL, "ftp://", 6) && strncmp(URL, "https://", 8))
797                 {
798                         Con_Printf("Curl_Begin(\"%s\"): nasty URL scheme rejected\n", URL);
799                         return false;
800                 }
801
802                 if(forthismap)
803                         ++numdownloads_added;
804                 di = (downloadinfo *) Z_Malloc(sizeof(*di));
805                 strlcpy(di->filename, name, sizeof(di->filename));
806                 strlcpy(di->url, URL, sizeof(di->url));
807                 dpsnprintf(di->referer, sizeof(di->referer), "dp://%s/", cls.netcon ? cls.netcon->address : "notconnected.invalid");
808                 di->forthismap = forthismap;
809                 di->stream = NULL;
810                 di->startpos = 0;
811                 di->curle = NULL;
812                 di->started = false;
813                 di->ispak = (ispak && !buf);
814                 di->bytes_received = 0;
815                 di->next = downloads;
816                 di->prev = NULL;
817                 if(di->next)
818                         di->next->prev = di;
819
820                 di->buffer = buf;
821                 di->buffersize = bufsize;
822                 if(callback == NULL)
823                 {
824                         di->callback = curl_default_callback;
825                         di->callback_data = di;
826                 }
827                 else
828                 {
829                         di->callback = callback;
830                         di->callback_data = cbdata;
831                 }
832
833                 downloads = di;
834                 return true;
835         }
836 }
837
838 qboolean Curl_Begin_ToFile(const char *URL, const char *name, qboolean ispak, qboolean forthismap)
839 {
840         return Curl_Begin(URL, name, ispak, forthismap, NULL, 0, NULL, NULL);
841 }
842 qboolean Curl_Begin_ToMemory(const char *URL, unsigned char *buf, size_t bufsize, curl_callback_t callback, void *cbdata)
843 {
844         return Curl_Begin(URL, NULL, false, false, buf, bufsize, callback, cbdata);
845 }
846
847 /*
848 ====================
849 Curl_Run
850
851 call this regularily as this will always download as much as possible without
852 blocking.
853 ====================
854 */
855 void Curl_Run(void)
856 {
857         noclear = FALSE;
858
859         if(!cl_curl_enabled.integer)
860                 return;
861
862         if(!curl_dll)
863                 return;
864
865         Curl_CheckCommandWhenDone();
866
867         if(!downloads)
868                 return;
869
870         if(realtime < curltime) // throttle
871                 return;
872
873         {
874                 int remaining;
875                 CURLMcode mc;
876
877                 do
878                 {
879                         mc = qcurl_multi_perform(curlm, &remaining);
880                 }
881                 while(mc == CURLM_CALL_MULTI_PERFORM);
882
883                 for(;;)
884                 {
885                         CURLMsg *msg = qcurl_multi_info_read(curlm, &remaining);
886                         if(!msg)
887                                 break;
888                         if(msg->msg == CURLMSG_DONE)
889                         {
890                                 downloadinfo *di;
891                                 CurlStatus failed = CURL_DOWNLOAD_SUCCESS;
892                                 CURLcode result;
893                                 qcurl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &di);
894                                 result = msg->data.result;
895                                 if(result)
896                                 {
897                                         failed = CURL_DOWNLOAD_FAILED;
898                                 }
899                                 else
900                                 {
901                                         long code;
902                                         qcurl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &code);
903                                         switch(code / 100)
904                                         {
905                                                 case 4: // e.g. 404?
906                                                 case 5: // e.g. 500?
907                                                         failed = CURL_DOWNLOAD_SERVERERROR;
908                                                         result = (CURLcode) code;
909                                                         break;
910                                         }
911                                 }
912
913                                 Curl_EndDownload(di, failed, result);
914                         }
915                 }
916         }
917
918         CheckPendingDownloads();
919
920         // when will we curl the next time?
921         // we will wait a bit to ensure our download rate is kept.
922         // we now know that realtime >= curltime... so set up a new curltime
923         if(cl_curl_maxspeed.value > 0)
924         {
925                 unsigned long bytes = bytes_received; // maybe smoothen a bit?
926                 curltime = realtime + bytes / (cl_curl_maxspeed.value * 1024.0);
927                 bytes_received -= bytes;
928         }
929         else
930                 curltime = realtime;
931 }
932
933 /*
934 ====================
935 Curl_CancelAll
936
937 Stops ALL downloads.
938 ====================
939 */
940 void Curl_CancelAll(void)
941 {
942         if(!curl_dll)
943                 return;
944
945         while(downloads)
946         {
947                 Curl_EndDownload(downloads, CURL_DOWNLOAD_ABORTED, CURLE_OK);
948                 // INVARIANT: downloads will point to the next download after that!
949         }
950 }
951
952 /*
953 ====================
954 Curl_Running
955
956 returns true iff there is a download running.
957 ====================
958 */
959 qboolean Curl_Running(void)
960 {
961         if(!curl_dll)
962                 return false;
963
964         return downloads != NULL;
965 }
966
967 /*
968 ====================
969 Curl_GetDownloadAmount
970
971 returns a value from 0.0 to 1.0 which represents the downloaded amount of data
972 for the given download.
973 ====================
974 */
975 static double Curl_GetDownloadAmount(downloadinfo *di)
976 {
977         if(!curl_dll)
978                 return -2;
979         if(di->curle)
980         {
981                 double length;
982                 qcurl_easy_getinfo(di->curle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &length);
983                 if(length > 0)
984                         return (di->startpos + di->bytes_received) / (di->startpos + length);
985                 else
986                         return 0;
987         }
988         else
989                 return -1;
990 }
991
992 /*
993 ====================
994 Curl_GetDownloadSpeed
995
996 returns the speed of the given download in bytes per second
997 ====================
998 */
999 static double Curl_GetDownloadSpeed(downloadinfo *di)
1000 {
1001         if(!curl_dll)
1002                 return -2;
1003         if(di->curle)
1004         {
1005                 double speed;
1006                 qcurl_easy_getinfo(di->curle, CURLINFO_SPEED_DOWNLOAD, &speed);
1007                 return speed;
1008         }
1009         else
1010                 return -1;
1011 }
1012
1013 /*
1014 ====================
1015 Curl_Info_f
1016
1017 prints the download list
1018 ====================
1019 */
1020 // TODO rewrite using Curl_GetDownloadInfo?
1021 static void Curl_Info_f(void)
1022 {
1023         downloadinfo *di;
1024         if(!curl_dll)
1025                 return;
1026         if(Curl_Running())
1027         {
1028                 Con_Print("Currently running downloads:\n");
1029                 for(di = downloads; di; di = di->next)
1030                 {
1031                         double speed, percent;
1032                         Con_Printf("  %s -> %s ",  di->url, di->filename);
1033                         percent = 100.0 * Curl_GetDownloadAmount(di);
1034                         speed = Curl_GetDownloadSpeed(di);
1035                         if(percent >= 0)
1036                                 Con_Printf("(%.1f%% @ %.1f KiB/s)\n", percent, speed / 1024.0);
1037                         else
1038                                 Con_Print("(queued)\n");
1039                 }
1040         }
1041         else
1042         {
1043                 Con_Print("No downloads running.\n");
1044         }
1045 }
1046
1047 /*
1048 ====================
1049 Curl_Curl_f
1050
1051 implements the "curl" console command
1052
1053 curl --info
1054 curl --cancel
1055 curl --cancel filename
1056 curl url
1057
1058 For internal use:
1059
1060 curl [--pak] [--forthismap] [--for filename filename...] url
1061         --pak: after downloading, load the package into the virtual file system
1062         --for filename...: only download of at least one of the named files is missing
1063         --forthismap: don't reconnect on failure
1064
1065 curl --clear_autodownload
1066         clears the download success/failure counters
1067
1068 curl --finish_autodownload
1069         if at least one download has been started, disconnect and drop to the menu
1070         once the last download completes successfully, reconnect to the current server
1071 ====================
1072 */
1073 void Curl_Curl_f(void)
1074 {
1075         int i;
1076         int end;
1077         qboolean pak = false;
1078         qboolean forthismap = false;
1079         const char *url;
1080         const char *name = 0;
1081
1082         if(!curl_dll)
1083         {
1084                 Con_Print("libcurl DLL not found, this command is inactive.\n");
1085                 return;
1086         }
1087
1088         if(!cl_curl_enabled.integer)
1089         {
1090                 Con_Print("curl support not enabled. Set cl_curl_enabled to 1 to enable.\n");
1091                 return;
1092         }
1093
1094         for(i = 0; i != Cmd_Argc(); ++i)
1095                 Con_DPrintf("%s ", Cmd_Argv(i));
1096         Con_DPrint("\n");
1097
1098         if(Cmd_Argc() < 2)
1099         {
1100                 Con_Print("usage:\ncurl --info, curl --cancel [filename], curl url\n");
1101                 return;
1102         }
1103
1104         url = Cmd_Argv(Cmd_Argc() - 1);
1105         end = Cmd_Argc();
1106
1107         for(i = 1; i != end; ++i)
1108         {
1109                 const char *a = Cmd_Argv(i);
1110                 if(!strcmp(a, "--info"))
1111                 {
1112                         Curl_Info_f();
1113                         return;
1114                 }
1115                 else if(!strcmp(a, "--cancel"))
1116                 {
1117                         if(i == end - 1) // last argument
1118                                 Curl_CancelAll();
1119                         else
1120                         {
1121                                 downloadinfo *di = Curl_Find(url);
1122                                 if(di)
1123                                         Curl_EndDownload(di, CURL_DOWNLOAD_ABORTED, CURLE_OK);
1124                                 else
1125                                         Con_Print("download not found\n");
1126                         }
1127                         return;
1128                 }
1129                 else if(!strcmp(a, "--pak"))
1130                 {
1131                         pak = true;
1132                 }
1133                 else if(!strcmp(a, "--for"))
1134                 {
1135                         for(i = i + 1; i != end - 1; ++i)
1136                         {
1137                                 if(!FS_FileExists(Cmd_Argv(i)))
1138                                         goto needthefile; // why can't I have a "double break"?
1139                         }
1140                         // if we get here, we have all the files...
1141                         return;
1142                 }
1143                 else if(!strcmp(a, "--forthismap"))
1144                 {
1145                         forthismap = true;
1146                 }
1147                 else if(!strcmp(a, "--as"))
1148                 {
1149                         if(i < end - 1)
1150                         {
1151                                 ++i;
1152                                 name = Cmd_Argv(i);
1153                         }
1154                 }
1155                 else if(!strcmp(a, "--clear_autodownload"))
1156                 {
1157                         // mark all running downloads as "not for this map", so if they
1158                         // fail, it does not matter
1159                         Curl_Clear_forthismap();
1160                         return;
1161                 }
1162                 else if(!strcmp(a, "--finish_autodownload"))
1163                 {
1164                         if(numdownloads_added)
1165                         {
1166                                 char donecommand[256];
1167                                 if(cls.netcon)
1168                                 {
1169                                         if(cl.loadbegun) // curling won't inhibit loading the map any more when at this stage, so bail out and force a reconnect
1170                                         {
1171                                                 dpsnprintf(donecommand, sizeof(donecommand), "connect %s", cls.netcon->address);
1172                                                 Curl_CommandWhenDone(donecommand);
1173                                                 noclear = TRUE;
1174                                                 CL_Disconnect();
1175                                                 noclear = FALSE;
1176                                                 Curl_CheckCommandWhenDone();
1177                                         }
1178                                         else
1179                                                 Curl_Register_predownload();
1180                                 }
1181                         }
1182                         return;
1183                 }
1184                 else if(*a == '-')
1185                 {
1186                         Con_Printf("invalid option %s\n", a);
1187                         return;
1188                 }
1189         }
1190
1191 needthefile:
1192         Curl_Begin_ToFile(url, name, pak, forthismap);
1193 }
1194
1195 /*
1196 static void curl_curlcat_callback(int code, size_t length_received, unsigned char *buffer, void *cbdata)
1197 {
1198         Con_Printf("Received %d bytes (status %d):\n%.*s\n", (int) length_received, code, (int) length_received, buffer);
1199         Z_Free(buffer);
1200 }
1201
1202 void Curl_CurlCat_f(void)
1203 {
1204         unsigned char *buf;
1205         const char *url = Cmd_Argv(1);
1206         buf = Z_Malloc(16384);
1207         Curl_Begin_ToMemory(url, buf, 16384, curl_curlcat_callback, NULL);
1208 }
1209 */
1210
1211 /*
1212 ====================
1213 Curl_Init_Commands
1214
1215 loads the commands and cvars this library uses
1216 ====================
1217 */
1218 void Curl_Init_Commands(void)
1219 {
1220         Cvar_RegisterVariable (&cl_curl_enabled);
1221         Cvar_RegisterVariable (&cl_curl_maxdownloads);
1222         Cvar_RegisterVariable (&cl_curl_maxspeed);
1223         Cvar_RegisterVariable (&sv_curl_defaulturl);
1224         Cvar_RegisterVariable (&sv_curl_serverpackages);
1225         Cmd_AddCommand ("curl", Curl_Curl_f, "download data from an URL and add to search path");
1226         //Cmd_AddCommand ("curlcat", Curl_CurlCat_f, "display data from an URL (debugging command)");
1227 }
1228
1229 /*
1230 ====================
1231 Curl_GetDownloadInfo
1232
1233 returns an array of Curl_downloadinfo_t structs for usage by GUIs.
1234 The number of elements in the array is returned in int *nDownloads.
1235 const char **additional_info may be set to a string of additional user
1236 information, or to NULL if no such display shall occur. The returned
1237 array must be freed later using Z_Free.
1238 ====================
1239 */
1240 Curl_downloadinfo_t *Curl_GetDownloadInfo(int *nDownloads, const char **additional_info)
1241 {
1242         int i;
1243         downloadinfo *di;
1244         Curl_downloadinfo_t *downinfo;
1245         static char addinfo[128];
1246
1247         if(!curl_dll)
1248         {
1249                 *nDownloads = 0;
1250                 if(additional_info)
1251                         *additional_info = NULL;
1252                 return NULL;
1253         }
1254
1255         i = 0;
1256         for(di = downloads; di; di = di->next)
1257                 ++i;
1258
1259         downinfo = (Curl_downloadinfo_t *) Z_Malloc(sizeof(*downinfo) * i);
1260         i = 0;
1261         for(di = downloads; di; di = di->next)
1262         {
1263                 // do not show infobars for background downloads
1264                 if(!developer.integer)
1265                         if(di->buffer)
1266                                 continue;
1267                 strlcpy(downinfo[i].filename, di->filename, sizeof(downinfo[i].filename));
1268                 if(di->curle)
1269                 {
1270                         downinfo[i].progress = Curl_GetDownloadAmount(di);
1271                         downinfo[i].speed = Curl_GetDownloadSpeed(di);
1272                         downinfo[i].queued = false;
1273                 }
1274                 else
1275                 {
1276                         downinfo[i].queued = true;
1277                 }
1278                 ++i;
1279         }
1280
1281         if(additional_info)
1282         {
1283                 // TODO: can I clear command_when_done as soon as the first download fails?
1284                 if(*command_when_done && !numdownloads_fail && numdownloads_added)
1285                 {
1286                         if(!strncmp(command_when_done, "connect ", 8))
1287                                 dpsnprintf(addinfo, sizeof(addinfo), "(will join %s when done)", command_when_done + 8);
1288                         else if(!strcmp(command_when_done, "cl_begindownloads"))
1289                                 dpsnprintf(addinfo, sizeof(addinfo), "(will enter the game when done)");
1290                         else
1291                                 dpsnprintf(addinfo, sizeof(addinfo), "(will do '%s' when done)", command_when_done);
1292                         *additional_info = addinfo;
1293                 }
1294                 else
1295                         *additional_info = NULL;
1296         }
1297
1298         *nDownloads = i;
1299         return downinfo;
1300 }
1301
1302
1303 /*
1304 ====================
1305 Curl_FindPackURL
1306
1307 finds the URL where to find a given package.
1308
1309 For this, it reads a file "curl_urls.txt" of the following format:
1310
1311         data*.pk3       -
1312         revdm*.pk3      http://revdm/downloads/are/here/
1313         *                       http://any/other/stuff/is/here/
1314
1315 The URLs should end in /. If not, downloads will still work, but the cached files
1316 can't be just put into the data directory with the same download configuration
1317 (you might want to do this if you want to tag downloaded files from your
1318 server, but you should not). "-" means "don't download".
1319
1320 If no single pattern matched, the cvar sv_curl_defaulturl is used as download
1321 location instead.
1322
1323 Note: pak1.pak and data*.pk3 are excluded from autodownload at another point in
1324 this file for obvious reasons.
1325 ====================
1326 */
1327 static const char *Curl_FindPackURL(const char *filename)
1328 {
1329         static char foundurl[1024];
1330         fs_offset_t filesize;
1331         char *buf = (char *) FS_LoadFile("curl_urls.txt", tempmempool, true, &filesize);
1332         if(buf && filesize)
1333         {
1334                 // read lines of format "pattern url"
1335                 char *p = buf;
1336                 char *pattern = NULL, *patternend = NULL, *url = NULL, *urlend = NULL;
1337                 qboolean eof = false;
1338
1339                 pattern = p;
1340                 while(!eof)
1341                 {
1342                         switch(*p)
1343                         {
1344                                 case 0:
1345                                         eof = true;
1346                                         // fallthrough
1347                                 case '\n':
1348                                 case '\r':
1349                                         if(pattern && url && patternend)
1350                                         {
1351                                                 if(!urlend)
1352                                                         urlend = p;
1353                                                 *patternend = 0;
1354                                                 *urlend = 0;
1355                                                 if(matchpattern(filename, pattern, true))
1356                                                 {
1357                                                         strlcpy(foundurl, url, sizeof(foundurl));
1358                                                         Z_Free(buf);
1359                                                         return foundurl;
1360                                                 }
1361                                         }
1362                                         pattern = NULL;
1363                                         patternend = NULL;
1364                                         url = NULL;
1365                                         urlend = NULL;
1366                                         break;
1367                                 case ' ':
1368                                 case '\t':
1369                                         if(pattern && !patternend)
1370                                                 patternend = p;
1371                                         else if(url && !urlend)
1372                                                 urlend = p;
1373                                         break;
1374                                 default:
1375                                         if(!pattern)
1376                                                 pattern = p;
1377                                         else if(pattern && patternend && !url)
1378                                                 url = p;
1379                                         break;
1380                         }
1381                         ++p;
1382                 }
1383         }
1384         if(buf)
1385                 Z_Free(buf);
1386         return sv_curl_defaulturl.string;
1387 }
1388
1389 typedef struct requirement_s
1390 {
1391         struct requirement_s *next;
1392         char filename[MAX_OSPATH];
1393 }
1394 requirement;
1395 static requirement *requirements = NULL;
1396
1397
1398 /*
1399 ====================
1400 Curl_RequireFile
1401
1402 Adds the given file to the list of requirements.
1403 ====================
1404 */
1405 void Curl_RequireFile(const char *filename)
1406 {
1407         requirement *req = (requirement *) Z_Malloc(sizeof(*requirements));
1408         req->next = requirements;
1409         strlcpy(req->filename, filename, sizeof(req->filename));
1410         requirements = req;
1411 }
1412
1413 /*
1414 ====================
1415 Curl_ClearRequirements
1416
1417 Clears the list of required files for playing on the current map.
1418 This should be called at every map change.
1419 ====================
1420 */
1421 void Curl_ClearRequirements(void)
1422 {
1423         const char *p;
1424         while(requirements)
1425         {
1426                 requirement *req = requirements;
1427                 requirements = requirements->next;
1428                 Z_Free(req);
1429         }
1430         p = sv_curl_serverpackages.string;
1431         Con_DPrintf("Require all of: %s\n", p);
1432         while(COM_ParseToken_Simple(&p, false, false))
1433         {
1434                 Con_DPrintf("Require: %s\n", com_token);
1435                 Curl_RequireFile(com_token);
1436         }
1437 }
1438
1439 /*
1440 ====================
1441 Curl_SendRequirements
1442
1443 Makes the current host_clients download all files he needs.
1444 This is done by sending him the following console commands:
1445
1446         curl --clear_autodownload
1447         curl --pak --for maps/pushmoddm1.bsp --forthismap http://where/this/darn/map/is/pushmoddm1.pk3
1448         curl --finish_autodownload
1449 ====================
1450 */
1451 void Curl_SendRequirements(void)
1452 {
1453         // for each requirement, find the pack name
1454         char sendbuffer[4096] = "";
1455         requirement *req;
1456         qboolean foundone = false;
1457
1458         for(req = requirements; req; req = req->next)
1459         {
1460                 const char *p;
1461                 const char *thispack = FS_WhichPack(req->filename);
1462                 const char *packurl;
1463
1464                 if(!thispack)
1465                         continue;
1466
1467                 p = strrchr(thispack, '/');
1468                 if(p)
1469                         thispack = p + 1;
1470
1471                 packurl = Curl_FindPackURL(thispack);
1472
1473                 if(packurl && *packurl && strcmp(packurl, "-"))
1474                 {
1475                         if(!foundone)
1476                                 strlcat(sendbuffer, "curl --clear_autodownload\n", sizeof(sendbuffer));
1477
1478                         strlcat(sendbuffer, "curl --pak --forthismap --as ", sizeof(sendbuffer));
1479                         strlcat(sendbuffer, thispack, sizeof(sendbuffer));
1480                         strlcat(sendbuffer, " --for ", sizeof(sendbuffer));
1481                         strlcat(sendbuffer, req->filename, sizeof(sendbuffer));
1482                         strlcat(sendbuffer, " ", sizeof(sendbuffer));
1483                         strlcat(sendbuffer, packurl, sizeof(sendbuffer));
1484                         strlcat(sendbuffer, thispack, sizeof(sendbuffer));
1485                         strlcat(sendbuffer, "\n", sizeof(sendbuffer));
1486
1487                         foundone = true;
1488                 }
1489         }
1490
1491         if(foundone)
1492                 strlcat(sendbuffer, "curl --finish_autodownload\n", sizeof(sendbuffer));
1493
1494         if(strlen(sendbuffer) + 1 < sizeof(sendbuffer))
1495                 Host_ClientCommands("%s", sendbuffer);
1496         else
1497                 Con_Printf("Could not initiate autodownload due to URL buffer overflow\n");
1498 }