DarkPlaces
Game engine based on the Quake 1 engine by id Software, developed by LadyHavoc
 
libcurl.c
Go to the documentation of this file.
1#include "quakedef.h"
2#include "fs.h"
3#include "libcurl.h"
4#include "thread.h"
5#include "com_list.h"
6#include "image.h"
7#include "jpeg.h"
8#include "image_png.h"
9
10static cvar_t curl_enabled = {CF_SHARED | CF_ARCHIVE, "curl_enabled","1", "whether libcurl may be used to GET files or POST data"};
11static cvar_t curl_maxdownloads = {CF_SHARED | CF_ARCHIVE, "curl_maxdownloads","3", "maximum number of concurrent HTTP/FTP downloads"};
12static cvar_t curl_maxspeed = {CF_SHARED | CF_ARCHIVE, "curl_maxspeed","0", "maximum download speed (KiB/s)"};
13static cvar_t curl_useragent = {CF_SHARED, "curl_useragent","1", "send the User-Agent string (note: turning this off may break stuff)"};
14static cvar_t curl_useragent_append = {CF_SHARED, "curl_useragent_append","", "a string to append to the User-Agent string (useful for name and version number of your mod)"};
15
16static cvar_t sv_curl_defaulturl = {CF_SERVER, "sv_curl_defaulturl","", "default autodownload source URL"};
17static cvar_t sv_curl_serverpackages = {CF_SERVER, "sv_curl_serverpackages","", "list of required files for the clients, separated by spaces"};
18static cvar_t sv_curl_maxspeed = {CF_SERVER, "sv_curl_maxspeed","0", "maximum download speed for clients downloading from sv_curl_defaulturl (KiB/s)"};
19
20static cvar_t developer_curl = {CF_SHARED, "developer_curl","0", "whether verbose libcurl output should be printed to stderr"};
21
22/*
23=================================================================
24
25 Minimal set of definitions from libcurl
26
27 WARNING: for a matter of simplicity, several pointer types are
28 casted to "void*", and most enumerated values are not included
29
30=================================================================
31*/
32
33typedef struct CURL_s CURL;
34typedef struct CURLM_s CURLM;
35typedef struct curl_slist curl_slist;
36typedef enum
37{
38 CURLE_OK = 0
41typedef enum
42{
43 CURLM_CALL_MULTI_PERFORM=-1, /* please call curl_multi_perform() soon */
44 CURLM_OK = 0
47#define CURL_GLOBAL_NOTHING 0
48#define CURL_GLOBAL_SSL 1
49#define CURL_GLOBAL_WIN32 2
50#define CURLOPTTYPE_LONG 0
51#define CURLOPTTYPE_OBJECTPOINT 10000
52#define CURLOPTTYPE_FUNCTIONPOINT 20000
53#define CURLOPTTYPE_OFF_T 30000
54#define CINIT(name,type,number) CURLOPT_ ## name = CURLOPTTYPE_ ## type + number
55typedef enum
56{
57 CINIT(WRITEDATA, OBJECTPOINT, 1),
58 CINIT(URL, OBJECTPOINT, 2),
59 CINIT(ERRORBUFFER, OBJECTPOINT, 10),
60 CINIT(WRITEFUNCTION, FUNCTIONPOINT, 11),
61 CINIT(POSTFIELDS, OBJECTPOINT, 15),
62 CINIT(REFERER, OBJECTPOINT, 16),
63 CINIT(USERAGENT, OBJECTPOINT, 18),
64 CINIT(LOW_SPEED_LIMIT, LONG , 19),
65 CINIT(LOW_SPEED_TIME, LONG, 20),
66 CINIT(RESUME_FROM, LONG, 21),
67 CINIT(HTTPHEADER, OBJECTPOINT, 23),
68 CINIT(VERBOSE, LONG, 41),
69 CINIT(POST, LONG, 47), /* HTTP POST method */
70 CINIT(FOLLOWLOCATION, LONG, 52), /* use Location: Luke! */
71 CINIT(POSTFIELDSIZE, LONG, 60),
72 CINIT(PRIVATE, OBJECTPOINT, 103),
73 CINIT(PROTOCOLS, LONG, 181),
74 CINIT(REDIR_PROTOCOLS, LONG, 182)
75}
77#define CURLPROTO_HTTP (1<<0)
78#define CURLPROTO_HTTPS (1<<1)
79#define CURLPROTO_FTP (1<<2)
92#define CURLINFO_STRING 0x100000
93#define CURLINFO_LONG 0x200000
94#define CURLINFO_DOUBLE 0x300000
95#define CURLINFO_SLIST 0x400000
96#define CURLINFO_MASK 0x0fffff
97#define CURLINFO_TYPEMASK 0xf00000
130
131typedef enum
132{
133 CURLMSG_NONE, /* first, not used */
134 CURLMSG_DONE, /* This easy handle has completed. 'result' contains
135 the CURLcode of the transfer */
138CURLMSG;
139typedef struct
140{
141 CURLMSG msg; /* what this message means */
142 CURL *easy_handle; /* the handle it concerns */
143 union
144 {
145 void *whatever; /* message-specific data */
146 CURLcode result; /* return code for transfer */
147 }
149}
150CURLMsg;
151
152static void (*qcurl_global_init) (long flags);
154
155static CURL * (*qcurl_easy_init) (void);
156static void (*qcurl_easy_cleanup) (CURL *handle);
157static CURLcode (*qcurl_easy_setopt) (CURL *handle, CURLoption option, ...);
158static CURLcode (*qcurl_easy_getinfo) (CURL *handle, CURLINFO info, ...);
159static const char * (*qcurl_easy_strerror) (CURLcode);
160
161static CURLM * (*qcurl_multi_init) (void);
162static CURLMcode (*qcurl_multi_perform) (CURLM *multi_handle, int *running_handles);
163static CURLMcode (*qcurl_multi_wait) (CURLM *multi_handle, void*, unsigned int extra_nfds, int timeout_ms, int *ret);
164static CURLMcode (*qcurl_multi_add_handle) (CURLM *multi_handle, CURL *easy_handle);
165static CURLMcode (*qcurl_multi_remove_handle) (CURLM *multi_handle, CURL *easy_handle);
166static CURLMsg * (*qcurl_multi_info_read) (CURLM *multi_handle, int *msgs_in_queue);
168static const char * (*qcurl_multi_strerror) (CURLcode);
169static curl_slist * (*qcurl_slist_append) (curl_slist *list, const char *string);
171
173{
174 {"curl_global_init", (void **) &qcurl_global_init},
175 {"curl_global_cleanup", (void **) &qcurl_global_cleanup},
176 {"curl_easy_init", (void **) &qcurl_easy_init},
177 {"curl_easy_cleanup", (void **) &qcurl_easy_cleanup},
178 {"curl_easy_setopt", (void **) &qcurl_easy_setopt},
179 {"curl_easy_strerror", (void **) &qcurl_easy_strerror},
180 {"curl_easy_getinfo", (void **) &qcurl_easy_getinfo},
181 {"curl_multi_init", (void **) &qcurl_multi_init},
182 {"curl_multi_perform", (void **) &qcurl_multi_perform},
183 {"curl_multi_wait", (void **) &qcurl_multi_wait},
184 {"curl_multi_add_handle", (void **) &qcurl_multi_add_handle},
185 {"curl_multi_remove_handle",(void **) &qcurl_multi_remove_handle},
186 {"curl_multi_info_read", (void **) &qcurl_multi_info_read},
187 {"curl_multi_cleanup", (void **) &qcurl_multi_cleanup},
188 {"curl_multi_strerror", (void **) &qcurl_multi_strerror},
189 {"curl_slist_append", (void **) &qcurl_slist_append},
190 {"curl_slist_free_all", (void **) &qcurl_slist_free_all},
191 {NULL, NULL}
192};
193
194// Handle for CURL DLL
196// will be checked at many places to find out if qcurl calls are allowed
197
198#define LOADTYPE_NONE 0
199#define LOADTYPE_PAK 1
200#define LOADTYPE_CACHEPIC 2
201#define LOADTYPE_SKINFRAME 3
202
204
205typedef struct downloadinfo_s
206{
207 char filename[MAX_OSPATH];
208 char url[1024];
209 char referer[256];
210 qfile_t *stream;
215 size_t bytes_received; // for buffer
216 double bytes_received_curl; // for throttling
217 double bytes_sent_curl; // for throttling
220 double maxspeed;
221 curl_slist *slist; // http headers
222
223 unsigned char *buffer;
227
228 const unsigned char *postbuf;
230 const char *post_content_type;
231 const char *extraheaders;
232}
234LIST_HEAD(downloads);
235static int numdownloads = 0;
236
237static qbool noclear = false;
238
239static int numdownloads_fail = 0;
240static int numdownloads_success = 0;
241static int numdownloads_added = 0;
242static char command_when_done[256] = "";
243static char command_when_error[256] = "";
244
245/*
246====================
247Curl_CommandWhenDone
248
249Sets the command which is to be executed when the last download completes AND
250all downloads since last server connect ended with a successful status.
251Setting the command to NULL clears it.
252====================
253*/
254static void Curl_CommandWhenDone(const char *cmd)
255{
256 if(!curl_dll)
257 return;
258 if(cmd)
260 else
262}
263
264/*
265FIXME
266Do not use yet. Not complete.
267Problem: what counts as an error?
268*/
269
270static void Curl_CommandWhenError(const char *cmd)
271{
272 if(!curl_dll)
273 return;
274 if(cmd)
276 else
278}
279
280/*
281====================
282Curl_Clear_forthismap
283
284Clears the "will disconnect on failure" flags.
285====================
286*/
302
303/*
304====================
305Curl_Have_forthismap
306
307Returns true if a download needed for the current game is running.
308====================
309*/
311{
312 return numdownloads_added != 0;
313}
314
316{
318 Curl_CommandWhenDone("cl_begindownloads");
319 Curl_CommandWhenError("cl_begindownloads");
321}
322
323/*
324====================
325Curl_CheckCommandWhenDone
326
327Checks if a "done command" is to be executed.
328All downloads finished, at least one success since connect, no single failure
329-> execute the command.
330*/
332{
333 if(!curl_dll)
334 return;
336 {
337 if(numdownloads_fail == 0)
338 {
339 Con_DPrintf("cURL downloads occurred, executing %s\n", command_when_done);
340 Cbuf_AddText(cmd_local, "\n");
342 Cbuf_AddText(cmd_local, "\n");
343 }
344 else
345 {
346 Con_DPrintf("cURL downloads FAILED, executing %s\n", command_when_error);
347 Cbuf_AddText(cmd_local, "\n");
349 Cbuf_AddText(cmd_local, "\n");
350 }
352 }
353}
354
355/*
356====================
357CURL_CloseLibrary
358
359Load the cURL DLL
360====================
361*/
363{
364 const char* dllnames [] =
365 {
366#if defined(WIN32)
367 "libcurl-4.dll",
368 "libcurl-3.dll",
369#elif defined(MACOSX)
370 "libcurl.4.dylib", // Mac OS X Notyetreleased
371 "libcurl.3.dylib", // Mac OS X Tiger
372 "libcurl.2.dylib", // Mac OS X Panther
373#else
374 "libcurl.so.4",
375 "libcurl.so.3",
376 "libcurl.so", // FreeBSD
377#endif
378 NULL
379 };
380
381 // Already loaded?
382 if (curl_dll)
383 return true;
384
385 // Load the DLL
386 return Sys_LoadDependency (dllnames, &curl_dll, curlfuncs);
387}
388
389
390/*
391====================
392CURL_CloseLibrary
393
394Unload the cURL DLL
395====================
396*/
397static void CURL_CloseLibrary (void)
398{
400}
401
402
403static CURLM *curlm = NULL;
404static double bytes_received = 0; // used for bandwidth throttling
405static double bytes_sent = 0; // used for bandwidth throttling
406static double curltime = 0;
407
408/*
409====================
410CURL_fwrite
411
412fwrite-compatible function that writes the data to a file. libcurl can call
413this.
414====================
415*/
416static size_t CURL_fwrite(void *data, size_t size, size_t nmemb, void *vdi)
417{
418 fs_offset_t ret = -1;
419 size_t bytes = size * nmemb;
420 downloadinfo *di = (downloadinfo *) vdi;
421
422 if(di->buffer)
423 {
424 if(di->bytes_received + bytes <= di->buffersize)
425 {
426 memcpy(di->buffer + di->bytes_received, data, bytes);
427 ret = bytes;
428 }
429 // otherwise: buffer overrun, ret stays -1
430 }
431
432 if(di->stream)
433 {
434 ret = FS_Write(di->stream, data, bytes);
435 }
436
437 di->bytes_received += bytes;
438
439 //Con_Printf("CURL_fwrite callback timestamp: %f bytes: %ld\n", host.realtime, ret);
440
441 return ret;
442 // Why not ret / nmemb?
443 // Because CURLOPT_WRITEFUNCTION docs say to return the number of bytes.
444 // Yes, this is incompatible to fwrite(2).
445}
446
455
456static void curl_default_callback(int status, size_t length_received, unsigned char *buffer, void *cbdata)
457{
458 downloadinfo *di = (downloadinfo *) cbdata;
459 switch(status)
460 {
461 case CURLCBSTATUS_OK:
462 Con_DPrintf("Download of %s: OK\n", di->filename);
463 break;
465 Con_DPrintf("Download of %s: FAILED\n", di->filename);
466 break;
468 Con_DPrintf("Download of %s: ABORTED\n", di->filename);
469 break;
471 Con_DPrintf("Download of %s: (unknown server error)\n", di->filename);
472 break;
474 Con_DPrintf("Download of %s: (unknown client error)\n", di->filename);
475 break;
476 default:
477 Con_DPrintf("Download of %s: %d\n", di->filename, status);
478 break;
479 }
480}
481
482static void curl_quiet_callback(int status, size_t length_received, unsigned char *buffer, void *cbdata)
483{
484 curl_default_callback(status, length_received, buffer, cbdata);
485}
486
487static unsigned char *decode_image(downloadinfo *di, const char *content_type)
488{
489 unsigned char *pixels = NULL;
490 fs_offset_t filesize = 0;
491 unsigned char *data = FS_LoadFile(di->filename, tempmempool, true, &filesize);
492 if(data)
493 {
494 int mip = 0;
495 if(!strcmp(content_type, "image/jpeg"))
496 pixels = JPEG_LoadImage_BGRA(data, filesize, &mip);
497 else if(!strcmp(content_type, "image/png"))
498 pixels = PNG_LoadImage_BGRA(data, filesize, &mip);
499 else if(filesize >= 7 && !strncmp((char *) data, "\xFF\xD8", 7))
500 pixels = JPEG_LoadImage_BGRA(data, filesize, &mip);
501 else if(filesize >= 7 && !strncmp((char *) data, "\x89PNG\x0D\x0A\x1A\x0A", 7))
502 pixels = PNG_LoadImage_BGRA(data, filesize, &mip);
503 else
504 Con_Printf("Did not detect content type: %s\n", content_type);
505 Mem_Free(data);
506 }
507 // do we call Image_MakeLinearColorsFromsRGB or not?
508 return pixels;
509}
510
511/*
512====================
513Curl_EndDownload
514
515stops a download. It receives a status (CURL_DOWNLOAD_SUCCESS,
516CURL_DOWNLOAD_FAILED or CURL_DOWNLOAD_ABORTED) and in the second case the error
517code from libcurl, or 0, if another error has occurred.
518====================
519*/
520static qbool Curl_Begin(const char *URL, const char *extraheaders, double maxspeed, const char *name, int loadtype, qbool forthismap, const char *post_content_type, const unsigned char *postbuf, size_t postbufsize, unsigned char *buf, size_t bufsize, curl_callback_t callback, void *cbdata);
521static void Curl_EndDownload(downloadinfo *di, CurlStatus status, CURLcode error, const char *content_type_)
522{
523 char content_type[64];
524 qbool ok = false;
525 if(!curl_dll)
526 return;
527 switch(status)
528 {
530 ok = true;
532 break;
535 break;
538 break;
540 // reopen to enforce it to have zero bytes again
541 if(di->stream)
542 {
543 FS_Close(di->stream);
544 di->stream = FS_OpenRealFile(di->filename, "wb", false);
545 }
546
547 if(di->callback)
549 break;
550 default:
551 if(di->callback)
553 break;
554 }
555 if(content_type_)
556 dp_strlcpy(content_type, content_type_, sizeof(content_type));
557 else
558 *content_type = 0;
559
560 if(di->curle)
561 {
564 if(di->slist)
566 }
567
568 if(!di->callback && ok && !di->bytes_received)
569 {
570 Con_Printf("ERROR: empty file\n");
571 ok = false;
572 }
573
574 if(di->stream)
575 FS_Close(di->stream);
576
577#define CLEAR_AND_RETRY() \
578 do \
579 { \
580 di->stream = FS_OpenRealFile(di->filename, "wb", false); \
581 FS_Close(di->stream); \
582 if(di->startpos && !di->callback) \
583 { \
584 Curl_Begin(di->url, di->extraheaders, di->maxspeed, di->filename, di->loadtype, di->forthismap, di->post_content_type, di->postbuf, di->postbufsize, NULL, 0, NULL, NULL); \
585 di->forthismap = false; \
586 } \
587 } \
588 while(0)
589
590 if(ok && di->loadtype == LOADTYPE_PAK)
591 {
592 ok = FS_AddPack(di->filename, NULL, true, true);
593 if(!ok)
595 }
596 else if(ok && di->loadtype == LOADTYPE_CACHEPIC)
597 {
598 const char *p;
599 unsigned char *pixels = NULL;
600
601 p = di->filename;
602#ifdef WE_ARE_EVIL
603 if(!strncmp(p, "dlcache/", 8))
604 p += 8;
605#endif
606
607 pixels = decode_image(di, content_type);
608 if(pixels)
610 else
612 }
613 else if(ok && di->loadtype == LOADTYPE_SKINFRAME)
614 {
615 const char *p;
616 unsigned char *pixels = NULL;
617
618 p = di->filename;
619#ifdef WE_ARE_EVIL
620 if(!strncmp(p, "dlcache/", 8))
621 p += 8;
622#endif
623
624 pixels = decode_image(di, content_type);
625 if(pixels)
626 R_SkinFrame_LoadInternalBGRA(p, TEXF_FORCE_RELOAD | TEXF_MIPMAP | TEXF_ALPHA, pixels, image_width, image_height, 0, 0, 0, false); // TODO what sRGB argument to put here?
627 else
629 }
630
631 List_Delete(&di->list);
632
633 --numdownloads;
634 if(di->forthismap)
635 {
636 if(ok)
638 else
640 }
641 Z_Free(di);
642}
643
644/*
645====================
646CleanURL
647
648Returns a "cleaned up" URL for display (to strip login data)
649====================
650*/
651static const char *CleanURL(const char *url, char *urlbuf, size_t urlbuflength)
652{
653 const char *p, *q, *r;
654
655 // if URL is of form anything://foo-without-slash@rest, replace by anything://rest
656 p = strstr(url, "://");
657 if(p)
658 {
659 q = strchr(p + 3, '@');
660 if(q)
661 {
662 r = strchr(p + 3, '/');
663 if(!r || q < r)
664 {
665 dpsnprintf(urlbuf, urlbuflength, "%.*s%s", (int)(p - url + 3), url, q + 1);
666 return urlbuf;
667 }
668 }
669 }
670
671 return url;
672}
673
674/*
675====================
676CheckPendingDownloads
677
678checks if there are free download slots to start new downloads in.
679To not start too many downloads at once, only one download is added at a time,
680up to a maximum number of cl_curl_maxdownloads are running.
681====================
682*/
683static void CheckPendingDownloads(void)
684{
685 const char *h;
686 char urlbuf[1024];
687 char vabuf[1024];
688 if(!curl_dll)
689 return;
691 {
692 downloadinfo *di;
693 List_For_Each_Entry(di, &downloads, downloadinfo, list)
694 {
695 if(!di->started)
696 {
697 if(!di->buffer)
698 {
699 Con_Printf("Downloading %s -> %s", CleanURL(di->url, urlbuf, sizeof(urlbuf)), di->filename);
700
701 di->stream = FS_OpenRealFile(di->filename, "ab", false);
702 if(!di->stream)
703 {
704 Con_Printf("\nFAILED: Could not open output file %s\n", di->filename);
706 return;
707 }
708 FS_Seek(di->stream, 0, SEEK_END);
709 di->startpos = FS_Tell(di->stream);
710
711 if(di->startpos > 0)
712 Con_Printf(", resuming from position %ld", (long) di->startpos);
713 Con_Print("...\n");
714 }
715 else
716 {
717 Con_DPrintf("Downloading %s -> memory\n", CleanURL(di->url, urlbuf, sizeof(urlbuf)));
718 di->startpos = 0;
719 }
720
721 di->curle = qcurl_easy_init();
722 di->slist = NULL;
723 qcurl_easy_setopt(di->curle, CURLOPT_URL, di->url);
725 {
726 const char *ua
727#ifdef HTTP_USER_AGENT
728 = HTTP_USER_AGENT;
729#else
731#endif
732 if(!ua)
733 ua = "";
735 ua = va(vabuf, sizeof(vabuf), "%s%s%s",
736 ua,
737 (ua[0] && ua[strlen(ua)-1] != ' ')
738 ? " "
739 : "",
741 qcurl_easy_setopt(di->curle, CURLOPT_USERAGENT, ua);
742 }
743 else
744 qcurl_easy_setopt(di->curle, CURLOPT_USERAGENT, "");
746 qcurl_easy_setopt(di->curle, CURLOPT_VERBOSE, (long) 1);
747 qcurl_easy_setopt(di->curle, CURLOPT_REFERER, di->referer);
748 qcurl_easy_setopt(di->curle, CURLOPT_RESUME_FROM, (long) di->startpos);
749 qcurl_easy_setopt(di->curle, CURLOPT_FOLLOWLOCATION, 1);
750 qcurl_easy_setopt(di->curle, CURLOPT_WRITEFUNCTION, CURL_fwrite);
751 qcurl_easy_setopt(di->curle, CURLOPT_LOW_SPEED_LIMIT, (long) 256);
752 qcurl_easy_setopt(di->curle, CURLOPT_LOW_SPEED_TIME, (long) 45);
753 qcurl_easy_setopt(di->curle, CURLOPT_WRITEDATA, (void *) di);
754 qcurl_easy_setopt(di->curle, CURLOPT_PRIVATE, (void *) di);
755 qcurl_easy_setopt(di->curle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP);
756 if(qcurl_easy_setopt(di->curle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP) != CURLE_OK)
757 {
758 Con_Printf("^1WARNING:^7 for security reasons, please upgrade to libcurl 7.19.4 or above. In a later version of DarkPlaces, HTTP redirect support will be disabled for this libcurl version.\n");
759 //qcurl_easy_setopt(di->curle, CURLOPT_FOLLOWLOCATION, 0);
760 }
761 if(di->post_content_type)
762 {
763 qcurl_easy_setopt(di->curle, CURLOPT_POST, 1);
764 qcurl_easy_setopt(di->curle, CURLOPT_POSTFIELDS, di->postbuf);
765 qcurl_easy_setopt(di->curle, CURLOPT_POSTFIELDSIZE, di->postbufsize);
766 di->slist = qcurl_slist_append(di->slist, va(vabuf, sizeof(vabuf), "Content-Type: %s", di->post_content_type));
767 }
768
769 // parse extra headers into slist
770 // \n separated list!
771 h = di->extraheaders;
772 while(h)
773 {
774 const char *hh = strchr(h, '\n');
775 if(hh)
776 {
777 char *buf = (char *) Mem_Alloc(tempmempool, hh - h + 1);
778 memcpy(buf, h, hh - h);
779 buf[hh - h] = 0;
780 di->slist = qcurl_slist_append(di->slist, buf);
781 h = hh + 1;
782 }
783 else
784 {
785 di->slist = qcurl_slist_append(di->slist, h);
786 h = NULL;
787 }
788 }
789
790 qcurl_easy_setopt(di->curle, CURLOPT_HTTPHEADER, di->slist);
791
792 qcurl_multi_add_handle(curlm, di->curle);
793 di->started = true;
794 ++numdownloads;
796 break;
797 }
798 }
799 }
800}
801
802/*
803====================
804Curl_Init
805
806this function MUST be called before using anything else in this file.
807On Win32, this must be called AFTER WSAStartup has been done!
808====================
809*/
810void Curl_Init(void)
811{
813 if(!curl_dll)
814 return;
818}
819
820/*
821====================
822Curl_Shutdown
823
824Surprise... closes all the stuff. Please do this BEFORE shutting down LHNET.
825====================
826*/
827void Curl_ClearRequirements(void);
829{
830 if(!curl_dll)
831 return;
836 curl_dll = NULL;
837}
838
839// for VM_checkextension()
841{
842 return curl_dll ? true : false;
843}
844
845/*
846====================
847Curl_Find
848
849Finds the internal information block for a download given by file name.
850====================
851*/
852static downloadinfo *Curl_Find(const char *filename)
853{
854 downloadinfo *di;
855 if(!curl_dll)
856 return NULL;
857 List_For_Each_Entry(di, &downloads, downloadinfo, list)
858 if(!strcasecmp(di->filename, filename))
859 return di;
860 return NULL;
861}
862
863void Curl_Cancel_ToMemory(curl_callback_t callback, void *cbdata)
864{
865 downloadinfo *di, *ndi;
866 if(!curl_dll)
867 return;
868 List_For_Each_Entry_Safe(di, ndi, &downloads, downloadinfo, list)
869 {
870 if(di->callback == callback && di->callback_data == cbdata)
871 {
872 di->callback = curl_quiet_callback; // do NOT call the callback
874 }
875 }
876}
877
878/*
879====================
880Curl_Begin
881
882Starts a download of a given URL to the file name portion of this URL (or name
883if given) in the "dlcache/" folder.
884====================
885*/
886static qbool Curl_Begin(const char *URL, const char *extraheaders, double maxspeed, const char *name, int loadtype, qbool forthismap, const char *post_content_type, const unsigned char *postbuf, size_t postbufsize, unsigned char *buf, size_t bufsize, curl_callback_t callback, void *cbdata)
887{
888 if(buf)
889 if(loadtype != LOADTYPE_NONE)
890 Host_Error("Curl_Begin: loadtype and buffer are both set");
891
893 {
894 return false;
895 }
896 else
897 {
898 char fn[MAX_OSPATH];
899 char urlbuf[1024];
900 const char *p, *q;
901 size_t length;
902 downloadinfo *di;
903
904 // if URL is protocol:///* or protocol://:port/*, insert the IP of the current server
905 p = strchr(URL, ':');
906 if(p)
907 {
908 if(!strncmp(p, ":///", 4) || !strncmp(p, "://:", 4))
909 {
910 char addressstring[128];
911 *addressstring = 0;
912 InfoString_GetValue(cls.userinfo, "*ip", addressstring, sizeof(addressstring));
913 q = strchr(addressstring, ':');
914 if(!q)
915 q = addressstring + strlen(addressstring);
916 if(*addressstring)
917 {
918 dpsnprintf(urlbuf, sizeof(urlbuf), "%.*s://%.*s%s", (int) (p - URL), URL, (int) (q - addressstring), addressstring, URL + (p - URL) + 3);
919 URL = urlbuf;
920 }
921 }
922 }
923
924 // Note: This extraction of the file name portion is NOT entirely correct.
925 //
926 // It does the following:
927 //
928 // http://host/some/script.cgi/SomeFile.pk3?uid=ABCDE -> SomeFile.pk3
929 // http://host/some/script.php?uid=ABCDE&file=/SomeFile.pk3 -> SomeFile.pk3
930 // http://host/some/script.php?uid=ABCDE&file=SomeFile.pk3 -> script.php
931 //
932 // However, I'd like to keep this "buggy" behavior so that PHP script
933 // authors can write download scripts without having to enable
934 // AcceptPathInfo on Apache. They just have to ensure that their script
935 // can be called with such a "fake" path name like
936 // http://host/some/script.php?uid=ABCDE&file=/SomeFile.pk3
937 //
938 // By the way, such PHP scripts should either send the file or a
939 // "Location:" redirect; PHP code example:
940 //
941 // header("Location: http://www.example.com/");
942 //
943 // By the way, this will set User-Agent to something like
944 // "Nexuiz build 22:27:55 Mar 17 2006" (engineversion) and Referer to
945 // dp://serverhost:serverport/ so you can filter on this; an example
946 // httpd log file line might be:
947 //
948 // 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"
949
951
952 if(buf)
953 {
954 if(!name)
955 name = CleanURL(URL, urlbuf, sizeof(urlbuf));
956 }
957 else
958 {
959 if(!name)
960 {
961 name = CleanURL(URL, urlbuf, sizeof(urlbuf));
962 p = strrchr(name, '/');
963 p = p ? (p+1) : name;
964 q = strchr(p, '?');
965 length = q ? (size_t)(q - p) : strlen(p);
966 dpsnprintf(fn, sizeof(fn), "dlcache/%.*s", (int)length, p);
967 }
968 else
969 {
970 dpsnprintf(fn, sizeof(fn), "dlcache/%s", name);
971 }
972
973 name = fn; // make it point back
974
975 // already downloading the file?
976 {
977 downloadinfo *existingdownloadinfo = Curl_Find(fn);
978 if(existingdownloadinfo)
979 {
980 Con_Printf("Can't download %s, already getting it from %s!\n", fn, CleanURL(existingdownloadinfo->url, urlbuf, sizeof(urlbuf)));
981
982 // however, if it was not for this map yet...
983 if(forthismap && !existingdownloadinfo->forthismap)
984 {
985 existingdownloadinfo->forthismap = true;
986 // this "fakes" a download attempt so the client will wait for
987 // the download to finish and then reconnect
989 }
990
992 return false;
993 }
994 }
995
996 if(FS_FileExists(fn))
997 {
998 if(loadtype == LOADTYPE_PAK)
999 {
1000 qbool already_loaded;
1001 if(FS_AddPack(fn, &already_loaded, true, true))
1002 {
1003 Con_DPrintf("%s already exists, not downloading!\n", fn);
1004 if(already_loaded)
1005 Con_DPrintf("(pak was already loaded)\n");
1006 else
1007 {
1008 if(forthismap)
1009 {
1012 }
1013 }
1014
1016 return false;
1017 }
1018 else
1019 {
1020 qfile_t *f = FS_OpenRealFile(fn, "rb", false);
1021 if(f)
1022 {
1023 char b[4] = {0};
1024 FS_Read(f, b, sizeof(b)); // no "-1", I will use memcmp
1025
1026 if(memcmp(b, "PK\x03\x04", 4) && memcmp(b, "PACK", 4))
1027 {
1028 Con_DPrintf("Detected non-PAK %s, clearing and NOT resuming.\n", fn);
1029 FS_Close(f);
1030 f = FS_OpenRealFile(fn, "wb", false);
1031 if(f)
1032 FS_Close(f);
1033 }
1034 else
1035 {
1036 // OK
1037 FS_Close(f);
1038 }
1039 }
1040 }
1041 }
1042 else
1043 {
1044 // never resume these
1045 qfile_t *f = FS_OpenRealFile(fn, "wb", false);
1046 if(f)
1047 FS_Close(f);
1048 }
1049 }
1050 }
1051
1052 // if we get here, we actually want to download... so first verify the
1053 // URL scheme (so one can't read local files using file://)
1054 if(strncmp(URL, "http://", 7) && strncmp(URL, "ftp://", 6) && strncmp(URL, "https://", 8))
1055 {
1056 Con_Printf("Curl_Begin(\"%s\"): nasty URL scheme rejected\n", URL);
1058 return false;
1059 }
1060
1061 if(forthismap)
1063 di = (downloadinfo *) Z_Malloc(sizeof(*di));
1064 dp_strlcpy(di->filename, name, sizeof(di->filename));
1065 dp_strlcpy(di->url, URL, sizeof(di->url));
1066 dpsnprintf(di->referer, sizeof(di->referer), "dp://%s/", cls.netcon ? cls.netcon->address : "notconnected.invalid");
1067 di->forthismap = forthismap;
1068 di->stream = NULL;
1069 di->startpos = 0;
1070 di->curle = NULL;
1071 di->started = false;
1072 di->loadtype = loadtype;
1073 di->maxspeed = maxspeed;
1074 di->bytes_received = 0;
1075 di->bytes_received_curl = 0;
1076 di->bytes_sent_curl = 0;
1077 di->extraheaders = extraheaders;
1078 di->buffer = buf;
1079 di->buffersize = bufsize;
1080 if(callback == NULL)
1081 {
1083 di->callback_data = di;
1084 }
1085 else
1086 {
1087 di->callback = callback;
1088 di->callback_data = cbdata;
1089 }
1090
1091 if(post_content_type)
1092 {
1093 di->post_content_type = post_content_type;
1094 di->postbuf = postbuf;
1095 di->postbufsize = postbufsize;
1096 }
1097 else
1098 {
1099 di->post_content_type = NULL;
1100 di->postbuf = NULL;
1101 di->postbufsize = 0;
1102 }
1103
1104 List_Add(&di->list, &downloads);
1105
1106 if (curl_mutex)
1108
1109 return true;
1110 }
1111}
1112
1113qbool Curl_Begin_ToFile(const char *URL, double maxspeed, const char *name, int loadtype, qbool forthismap)
1114{
1115 return Curl_Begin(URL, NULL, maxspeed, name, loadtype, forthismap, NULL, NULL, 0, NULL, 0, NULL, NULL);
1116}
1117qbool Curl_Begin_ToMemory(const char *URL, double maxspeed, unsigned char *buf, size_t bufsize, curl_callback_t callback, void *cbdata)
1118{
1119 return Curl_Begin(URL, NULL, maxspeed, NULL, false, false, NULL, NULL, 0, buf, bufsize, callback, cbdata);
1120}
1121qbool Curl_Begin_ToMemory_POST(const char *URL, const char *extraheaders, double maxspeed, const char *post_content_type, const unsigned char *postbuf, size_t postbufsize, unsigned char *buf, size_t bufsize, curl_callback_t callback, void *cbdata)
1122{
1123 return Curl_Begin(URL, extraheaders, maxspeed, NULL, false, false, post_content_type, postbuf, postbufsize, buf, bufsize, callback, cbdata);
1124}
1125
1126/*
1127====================
1128Curl_Frame
1129
1130call this regularily as this will always download as much as possible without
1131blocking.
1132====================
1133*/
1134void Curl_Frame(void)
1135{
1136 double maxspeed;
1137 downloadinfo *di;
1138
1139 noclear = false;
1140
1142 return;
1143
1144 if(!curl_dll)
1145 return;
1146
1148
1150
1151 if(List_Is_Empty(&downloads))
1152 {
1154 return;
1155 }
1156
1157 if(host.realtime < curltime) // throttle
1158 {
1160 return;
1161 }
1162
1163 {
1164 int remaining;
1165 CURLMcode mc;
1166
1167 do
1168 {
1169 mc = qcurl_multi_perform(curlm, &remaining);
1170 }
1171 while(mc == CURLM_CALL_MULTI_PERFORM);
1172
1173 List_For_Each_Entry(di, &downloads, downloadinfo, list)
1174 {
1175 double b = 0;
1176 if(di->curle)
1177 {
1179 bytes_sent += (b - di->bytes_sent_curl);
1180 di->bytes_sent_curl = b;
1182 bytes_sent += (b - di->bytes_received_curl);
1183 di->bytes_received_curl = b;
1184 }
1185 }
1186
1187 for(;;)
1188 {
1189 CURLMsg *msg = qcurl_multi_info_read(curlm, &remaining);
1190 if(!msg)
1191 break;
1192 if(msg->msg == CURLMSG_DONE)
1193 {
1194 const char *ct = NULL;
1196 CURLcode result;
1198 result = msg->data.result;
1199 if(result)
1200 {
1201 failed = CURL_DOWNLOAD_FAILED;
1202 }
1203 else
1204 {
1205 long code;
1207 switch(code / 100)
1208 {
1209 case 4: // e.g. 404?
1210 case 5: // e.g. 500?
1212 result = (CURLcode) code;
1213 break;
1214 }
1216 }
1217
1218 Curl_EndDownload(di, failed, result, ct);
1219 }
1220 }
1221 }
1222
1224
1225 // when will we curl the next time?
1226 // we will wait a bit to ensure our download rate is kept.
1227 // we now know that realtime >= curltime... so set up a new curltime
1228
1229 // use the slowest allowing download to derive the maxspeed... this CAN
1230 // be done better, but maybe later
1231 maxspeed = curl_maxspeed.value;
1232 List_For_Each_Entry(di, &downloads, downloadinfo, list)
1233 if(di->maxspeed > 0)
1234 if(di->maxspeed < maxspeed || maxspeed <= 0)
1235 maxspeed = di->maxspeed;
1236
1237 if(maxspeed > 0)
1238 {
1239 double bytes = bytes_sent + bytes_received; // maybe smoothen a bit?
1240 curltime = host.realtime + bytes / (maxspeed * 1024.0);
1241 bytes_sent = 0;
1242 bytes_received = 0;
1243 }
1244 else
1246
1248}
1249
1250/*
1251====================
1252Curl_Select
1253
1254Sleeps until there's some transfer progress or a timeout is reached,
1255unfortunately the timeout is only in milliseconds.
1256This allows good throughput even at very low FPS.
1257Less important on newer libcurl versions but still helps.
1258
1259Returns 0 immediately if there's no transfers to wait for,
1260or > 0 if a transfer is ready or the timeout was reached.
1261====================
1262*/
1263bool Curl_Select(int timeout_ms)
1264{
1265 CURLMcode err;
1266
1267 if (List_Is_Empty(&downloads))
1268 return false;
1269
1270 err = qcurl_multi_wait(curlm, NULL, 0, timeout_ms, NULL);
1271 if (err == CURLM_OK)
1272 return true;
1273 Con_Printf(CON_ERROR "curl_multi_wait() failed with code %d\n", err);
1274 return false;
1275}
1276
1277/*
1278====================
1279Curl_CancelAll
1280
1281Stops ALL downloads.
1282====================
1283*/
1285{
1286 if(!curl_dll)
1287 return;
1288
1290
1291 while(!List_Is_Empty(&downloads))
1292 {
1294 // INVARIANT: downloads will point to the next download after that!
1295 }
1296
1298}
1299
1300/*
1301====================
1302Curl_Running
1303
1304returns true if there is a download running.
1305====================
1306*/
1308{
1309 if(!curl_dll)
1310 return false;
1311
1312 return !List_Is_Empty(&downloads);
1313}
1314
1315/*
1316====================
1317Curl_GetDownloadAmount
1318
1319returns a value from 0.0 to 1.0 which represents the downloaded amount of data
1320for the given download.
1321====================
1322*/
1324{
1325 if(!curl_dll)
1326 return -2;
1327 if(di->curle)
1328 {
1329 double length;
1331 if(length > 0)
1332 return (di->startpos + di->bytes_received) / (di->startpos + length);
1333 else
1334 return 0;
1335 }
1336 else
1337 return -1;
1338}
1339
1340/*
1341====================
1342Curl_GetDownloadSpeed
1343
1344returns the speed of the given download in bytes per second
1345====================
1346*/
1348{
1349 if(!curl_dll)
1350 return -2;
1351 if(di->curle)
1352 {
1353 double speed;
1355 return speed;
1356 }
1357 else
1358 return -1;
1359}
1360
1361/*
1362====================
1363Curl_Info_f
1364
1365prints the download list
1366====================
1367*/
1368// TODO rewrite using Curl_GetDownloadInfo?
1370{
1371 downloadinfo *di;
1372 char urlbuf[1024];
1373 if(!curl_dll)
1374 return;
1375 if(Curl_Running())
1376 {
1378 Con_Print("Currently running downloads:\n");
1379 List_For_Each_Entry(di, &downloads, downloadinfo, list)
1380 {
1381 double speed, percent;
1382 Con_Printf(" %s -> %s ", CleanURL(di->url, urlbuf, sizeof(urlbuf)), di->filename);
1383 percent = 100.0 * Curl_GetDownloadAmount(di);
1384 speed = Curl_GetDownloadSpeed(di);
1385 if(percent >= 0)
1386 Con_Printf("(%.1f%% @ %.1f KiB/s)\n", percent, speed / 1024.0);
1387 else
1388 Con_Print("(queued)\n");
1389 }
1391 }
1392 else
1393 {
1394 Con_Print("No downloads running.\n");
1395 }
1396}
1397
1398/*
1399====================
1400Curl_Curl_f
1401
1402implements the "curl" console command
1403
1404curl --info
1405curl --cancel
1406curl --cancel filename
1407curl url
1408
1409For internal use:
1410
1411curl [--pak] [--forthismap] [--for filename filename...] url
1412 --pak: after downloading, load the package into the virtual file system
1413 --for filename...: only download of at least one of the named files is missing
1414 --forthismap: don't reconnect on failure
1415
1416curl --clear_autodownload
1417 clears the download success/failure counters
1418
1419curl --finish_autodownload
1420 if at least one download has been started, disconnect and drop to the menu
1421 once the last download completes successfully, reconnect to the current server
1422====================
1423*/
1425{
1426 double maxspeed = 0;
1427 int i;
1428 int end;
1429 int loadtype = LOADTYPE_NONE;
1430 qbool forthismap = false;
1431 const char *url;
1432 const char *name = 0;
1433
1434 if(!curl_dll)
1435 {
1436 Con_Print("libcurl DLL not found, this command is inactive.\n");
1437 return;
1438 }
1439
1441 {
1442 Con_Print("curl support not enabled. Set cl_curl_enabled to 1 to enable.\n");
1443 return;
1444 }
1445
1446 if(Cmd_Argc(cmd) < 2)
1447 {
1448 Con_Print("usage:\ncurl --info, curl --cancel [filename], curl url\n");
1449 return;
1450 }
1451
1452 url = Cmd_Argv(cmd, Cmd_Argc(cmd) - 1);
1453 end = Cmd_Argc(cmd);
1454
1455 for(i = 1; i != end; ++i)
1456 {
1457 const char *a = Cmd_Argv(cmd, i);
1458 if(!strcmp(a, "--info"))
1459 {
1461 return;
1462 }
1463 else if(!strcmp(a, "--cancel"))
1464 {
1465 if(i == end - 1) // last argument
1467 else
1468 {
1469 downloadinfo *di = Curl_Find(url);
1470 if(di)
1472 else
1473 Con_Print("download not found\n");
1474 }
1475 return;
1476 }
1477 else if(!strcmp(a, "--pak"))
1478 {
1479 loadtype = LOADTYPE_PAK;
1480 }
1481 else if(!strcmp(a, "--cachepic"))
1482 {
1483 loadtype = LOADTYPE_CACHEPIC;
1484 }
1485 else if(!strcmp(a, "--skinframe"))
1486 {
1487 loadtype = LOADTYPE_SKINFRAME;
1488 }
1489 else if(!strcmp(a, "--for")) // must be last option
1490 {
1491 for(i = i + 1; i != end - 1; ++i)
1492 {
1493 if(!FS_FileExists(Cmd_Argv(cmd, i)))
1494 goto needthefile; // why can't I have a "double break"?
1495 }
1496 // if we get here, we have all the files...
1497 return;
1498 }
1499 else if(!strcmp(a, "--forthismap"))
1500 {
1501 forthismap = true;
1502 }
1503 else if(!strcmp(a, "--as"))
1504 {
1505 if(i < end - 1)
1506 {
1507 ++i;
1508 name = Cmd_Argv(cmd, i);
1509 }
1510 }
1511 else if(!strcmp(a, "--clear_autodownload"))
1512 {
1513 // mark all running downloads as "not for this map", so if they
1514 // fail, it does not matter
1516 return;
1517 }
1518 else if(!strcmp(a, "--finish_autodownload"))
1519 {
1521 {
1522 char donecommand[256];
1523 if(cls.netcon)
1524 {
1525 if(cl.loadbegun) // curling won't inhibit loading the map any more when at this stage, so bail out and force a reconnect
1526 {
1527 dpsnprintf(donecommand, sizeof(donecommand), "connect %s", cls.netcon->address);
1528 Curl_CommandWhenDone(donecommand);
1529 noclear = true;
1530 CL_Disconnect();
1531 noclear = false;
1533 }
1534 else
1536 }
1537 }
1538 return;
1539 }
1540 else if(!strncmp(a, "--maxspeed=", 11))
1541 {
1542 maxspeed = atof(a + 11);
1543 }
1544 else if(*a == '-')
1545 {
1546 Con_Printf("curl: invalid option %s\n", a);
1547 // but we ignore the option
1548 }
1549 }
1550
1551needthefile:
1552 Curl_Begin_ToFile(url, maxspeed, name, loadtype, forthismap);
1553}
1554
1555/*
1556static void curl_curlcat_callback(int code, size_t length_received, unsigned char *buffer, void *cbdata)
1557{
1558 Con_Printf("Received %d bytes (status %d):\n%.*s\n", (int) length_received, code, (int) length_received, buffer);
1559 Z_Free(buffer);
1560}
1561
1562void Curl_CurlCat_f(cmd_state_t *cmd)
1563{
1564 unsigned char *buf;
1565 const char *url = Cmd_Argv(cmd, 1);
1566 buf = Z_Malloc(16384);
1567 Curl_Begin_ToMemory(url, buf, 16384, curl_curlcat_callback, NULL);
1568}
1569*/
1570
1571/*
1572====================
1573Curl_Init_Commands
1574
1575loads the commands and cvars this library uses
1576====================
1577*/
1579{
1585 Cvar_RegisterVirtual (&curl_enabled, "cl_curl_enabled");
1586 Cvar_RegisterVirtual (&curl_maxdownloads, "cl_curl_maxdownloads");
1587 Cvar_RegisterVirtual (&curl_maxspeed, "cl_curl_maxspeed");
1588 Cvar_RegisterVirtual (&curl_useragent, "cl_curl_useragent");
1589 Cvar_RegisterVirtual (&curl_useragent_append, "cl_curl_useragent_append");
1590
1594
1596
1597 Cmd_AddCommand(CF_CLIENT | CF_CLIENT_FROM_SERVER, "curl", Curl_Curl_f, "download data from an URL and add to search path");
1598 //Cmd_AddCommand(cmd_local, "curlcat", Curl_CurlCat_f, "display data from an URL (debugging command)");
1599}
1600
1601/*
1602====================
1603Curl_GetDownloadInfo
1604
1605returns an array of Curl_downloadinfo_t structs for usage by GUIs.
1606The number of elements in the array is returned in int *nDownloads.
1607const char **additional_info may be set to a string of additional user
1608information, or to NULL if no such display shall occur. The returned
1609array must be freed later using Z_Free.
1610====================
1611*/
1612Curl_downloadinfo_t *Curl_GetDownloadInfo(int *nDownloads, const char **additional_info, char *addinfo, size_t addinfolength)
1613{
1614 int i;
1615 downloadinfo *di;
1616 Curl_downloadinfo_t *downinfo;
1617
1618 if(!curl_dll)
1619 {
1620 *nDownloads = 0;
1621 if(additional_info)
1622 *additional_info = NULL;
1623 return NULL;
1624 }
1625
1627
1628 i = 0;
1629 List_For_Each_Entry(di, &downloads, downloadinfo, list)
1630 ++i;
1631
1632 downinfo = (Curl_downloadinfo_t *) Z_Malloc(sizeof(*downinfo) * i);
1633 i = 0;
1634 List_For_Each_Entry(di, &downloads, downloadinfo, list)
1635 {
1636 // do not show infobars for background downloads
1637 if(developer.integer <= 0)
1638 if(di->buffer)
1639 continue;
1640 dp_strlcpy(downinfo[i].filename, di->filename, sizeof(downinfo[i].filename));
1641 if(di->curle)
1642 {
1643 downinfo[i].progress = Curl_GetDownloadAmount(di);
1644 downinfo[i].speed = Curl_GetDownloadSpeed(di);
1645 downinfo[i].queued = false;
1646 }
1647 else
1648 {
1649 downinfo[i].queued = true;
1650 }
1651 ++i;
1652 }
1653
1654 if(additional_info)
1655 {
1656 // TODO: can I clear command_when_done as soon as the first download fails?
1658 {
1659 if(!strncmp(command_when_done, "connect ", 8))
1660 dpsnprintf(addinfo, addinfolength, "(will join %s when done)", command_when_done + 8);
1661 else if(!strcmp(command_when_done, "cl_begindownloads"))
1662 dpsnprintf(addinfo, addinfolength, "(will enter the game when done)");
1663 else
1664 dpsnprintf(addinfo, addinfolength, "(will do '%s' when done)", command_when_done);
1665 *additional_info = addinfo;
1666 }
1667 else
1668 *additional_info = NULL;
1669 }
1670
1671 *nDownloads = i;
1673 return downinfo;
1674}
1675
1676
1677/*
1678====================
1679Curl_FindPackURL
1680
1681finds the URL where to find a given package.
1682
1683For this, it reads a file "curl_urls.txt" of the following format:
1684
1685 data*.pk3 -
1686 revdm*.pk3 http://revdm/downloads/are/here/
1687 * http://any/other/stuff/is/here/
1688
1689The URLs should end in /. If not, downloads will still work, but the cached files
1690can't be just put into the data directory with the same download configuration
1691(you might want to do this if you want to tag downloaded files from your
1692server, but you should not). "-" means "don't download".
1693
1694If no single pattern matched, the cvar sv_curl_defaulturl is used as download
1695location instead.
1696
1697Note: pak1.pak and data*.pk3 are excluded from autodownload at another point in
1698this file for obvious reasons.
1699====================
1700*/
1701static const char *Curl_FindPackURL(const char *filename)
1702{
1703 static char foundurl[1024]; // invoked only by server
1704 fs_offset_t filesize;
1705 char *buf = (char *) FS_LoadFile("curl_urls.txt", tempmempool, true, &filesize);
1706 if(buf && filesize)
1707 {
1708 // read lines of format "pattern url"
1709 char *p = buf;
1710 char *pattern = NULL, *patternend = NULL, *url = NULL, *urlend = NULL;
1711 qbool eof = false;
1712
1713 pattern = p;
1714 while(!eof)
1715 {
1716 switch(*p)
1717 {
1718 case 0:
1719 eof = true;
1720 // fallthrough
1721 case '\n':
1722 case '\r':
1723 if(pattern && url && patternend)
1724 {
1725 if(!urlend)
1726 urlend = p;
1727 *patternend = 0;
1728 *urlend = 0;
1729 if(matchpattern(filename, pattern, true))
1730 {
1731 dp_strlcpy(foundurl, url, sizeof(foundurl));
1732 Z_Free(buf);
1733 return foundurl;
1734 }
1735 }
1736 pattern = NULL;
1737 patternend = NULL;
1738 url = NULL;
1739 urlend = NULL;
1740 break;
1741 case ' ':
1742 case '\t':
1743 if(pattern && !patternend)
1744 patternend = p;
1745 else if(url && !urlend)
1746 urlend = p;
1747 break;
1748 default:
1749 if(!pattern)
1750 pattern = p;
1751 else if(pattern && patternend && !url)
1752 url = p;
1753 break;
1754 }
1755 ++p;
1756 }
1757 }
1758 if(buf)
1759 Z_Free(buf);
1761}
1762
1763typedef struct requirement_s
1764{
1765 struct requirement_s *next;
1766 char filename[MAX_OSPATH];
1767}
1770
1771
1772/*
1773====================
1774Curl_RequireFile
1775
1776Adds the given file to the list of requirements.
1777====================
1778*/
1779void Curl_RequireFile(const char *filename)
1780{
1781 requirement *req = (requirement *) Z_Malloc(sizeof(*requirements));
1782 req->next = requirements;
1783 dp_strlcpy(req->filename, filename, sizeof(req->filename));
1784 requirements = req;
1785}
1786
1787/*
1788====================
1789Curl_ClearRequirements
1790
1791Clears the list of required files for playing on the current map.
1792This should be called at every map change.
1793====================
1794*/
1796{
1797 while(requirements)
1798 {
1801 Z_Free(req);
1802 }
1803}
1804
1805/*
1806====================
1807Curl_SendRequirements
1808
1809Makes the current host_clients download all files he needs.
1810This is done by sending him the following console commands:
1811
1812 curl --clear_autodownload
1813 curl --pak --for maps/pushmoddm1.bsp --forthismap http://where/this/darn/map/is/pushmoddm1.pk3
1814 curl --finish_autodownload
1815====================
1816*/
1817static qbool Curl_SendRequirement(const char *filename, qbool foundone, char *sendbuffer, size_t sendbuffer_len)
1818{
1819 const char *p;
1820 const char *thispack = FS_WhichPack(filename);
1821 const char *packurl;
1822
1823 if(!thispack || !*thispack)
1824 return false;
1825
1826 p = strrchr(thispack, '/');
1827 if(p)
1828 thispack = p + 1;
1829
1830 packurl = Curl_FindPackURL(thispack);
1831
1832 if(packurl && *packurl && strcmp(packurl, "-"))
1833 {
1834 if(!foundone)
1835 dp_strlcat(sendbuffer, "curl --clear_autodownload\n", sendbuffer_len);
1836
1837 dp_strlcat(sendbuffer, "curl --pak --forthismap --as ", sendbuffer_len);
1838 dp_strlcat(sendbuffer, thispack, sendbuffer_len);
1839 if(sv_curl_maxspeed.value > 0)
1840 dpsnprintf(sendbuffer + strlen(sendbuffer), sendbuffer_len - strlen(sendbuffer), " --maxspeed=%.1f", sv_curl_maxspeed.value);
1841 dp_strlcat(sendbuffer, " --for ", sendbuffer_len);
1842 dp_strlcat(sendbuffer, filename, sendbuffer_len);
1843 dp_strlcat(sendbuffer, " ", sendbuffer_len);
1844 dp_strlcat(sendbuffer, packurl, sendbuffer_len);
1845 dp_strlcat(sendbuffer, thispack, sendbuffer_len);
1846 dp_strlcat(sendbuffer, "\n", sendbuffer_len);
1847
1848 return true;
1849 }
1850
1851 return false;
1852}
1854{
1855 // for each requirement, find the pack name
1856 char sendbuffer[4096] = "";
1857 requirement *req;
1858 qbool foundone = false;
1859 const char *p;
1860
1861 for(req = requirements; req; req = req->next)
1862 foundone = Curl_SendRequirement(req->filename, foundone, sendbuffer, sizeof(sendbuffer)) || foundone;
1863
1865 while(COM_ParseToken_Simple(&p, false, false, true))
1866 foundone = Curl_SendRequirement(com_token, foundone, sendbuffer, sizeof(sendbuffer)) || foundone;
1867
1868 if(foundone)
1869 dp_strlcat(sendbuffer, "curl --finish_autodownload\n", sizeof(sendbuffer));
1870
1871 if(strlen(sendbuffer) + 1 < sizeof(sendbuffer))
1872 SV_ClientCommands("%s", sendbuffer);
1873 else
1874 Con_Printf("Could not initiate autodownload due to URL buffer overflow\n");
1875}
client_state_t cl
Definition cl_main.c:117
client_static_t cls
Definition cl_main.c:116
void CL_Disconnect(void)
Definition cl_main.c:478
@ ca_dedicated
Definition client.h:530
void Cbuf_AddText(cmd_state_t *cmd, const char *text)
Definition cmd.c:264
void Cmd_AddCommand(unsigned flags, const char *cmd_name, xcommand_t function, const char *description)
called by the init functions of other parts of the program to register commands and functions to call...
Definition cmd.c:1661
cmd_state_t * cmd_local
command interpreter for local commands injected by SVQC, CSQC, MQC, server or client engine code uses...
Definition cmd.c:25
#define CF_SHARED
Definition cmd.h:67
#define CF_SERVER
cvar/command that only the server can change/execute
Definition cmd.h:49
static int Cmd_Argc(cmd_state_t *cmd)
Definition cmd.h:249
static const char * Cmd_Argv(cmd_state_t *cmd, int arg)
Cmd_Argv(cmd, ) will return an empty string (not a NULL) if arg > argc, so string operations are alwa...
Definition cmd.h:254
#define CF_CLIENT
cvar/command that only the client can change/execute
Definition cmd.h:48
#define CF_CLIENT_FROM_SERVER
command that the server is allowed to execute on the client
Definition cmd.h:50
#define CF_ARCHIVE
cvar should have its set value saved to config.cfg and persist across sessions
Definition cmd.h:53
size_t InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuesize)
Returns the number of bytes written to *value excluding the \0 terminator.
static void List_Add(llist_t *node, llist_t *head)
Definition com_list.h:241
#define LIST_HEAD(name)
Definition com_list.h:38
#define List_First_Entry(ptr, type, member)
Definition com_list.h:50
static void List_Delete(llist_t *node)
Definition com_list.h:274
#define List_For_Each_Entry_Safe(pos, n, head, type, member)
Definition com_list.h:173
#define List_For_Each_Entry(pos, head, type, member)
Definition com_list.h:121
static qbool List_Is_Empty(const llist_t *list)
Definition com_list.h:211
char com_token[MAX_INPUTLINE]
Definition common.c:39
char * va(char *buf, size_t buflen, const char *format,...)
Definition common.c:972
qbool COM_ParseToken_Simple(const char **datapointer, qbool returnnewline, qbool parsebackslash, qbool parsecomments)
Definition common.c:463
int dpsnprintf(char *buffer, size_t buffersize, const char *format,...)
Returns the number of printed characters, excluding the final '\0' or returns -1 if the buffer isn't ...
Definition common.c:997
#define dp_strlcat(dst, src, dsize)
Definition common.h:304
#define dp_strlcpy(dst, src, dsize)
Definition common.h:303
void Con_Print(const char *msg)
Prints to all appropriate console targets, and adds timestamps.
Definition console.c:1504
void Con_DPrintf(const char *fmt,...)
A Con_Printf that only shows up if the "developer" cvar is set.
Definition console.c:1544
void Con_Printf(const char *fmt,...)
Prints to all appropriate console targets.
Definition console.c:1514
#define CON_ERROR
Definition console.h:102
vector size
float flags
void() predraw
const float true
void Cvar_RegisterVariable(cvar_t *variable)
registers a cvar that already has the name, string, and optionally the archive elements set.
Definition cvar.c:599
void Cvar_RegisterVirtual(cvar_t *variable, const char *name)
Definition cvar.c:513
char engineversion[128]
version string for the corner of the console, crash messages, status command, etc
Definition host.c:304
cachepic_t * Draw_NewPic(const char *picname, int width, int height, unsigned char *pixels, textype_t textype, int texflags)
Definition gl_draw.c:256
int matchpattern(const char *in, const char *pattern, int caseinsensitive)
Definition filematch.c:16
fs_offset_t FS_Read(qfile_t *file, void *buffer, size_t buffersize)
Definition fs.c:3066
fs_offset_t FS_Write(qfile_t *file, const void *data, size_t datasize)
Definition fs.c:3019
unsigned char * FS_LoadFile(const char *path, mempool_t *pool, qbool quiet, fs_offset_t *filesizepointer)
Definition fs.c:3540
fs_offset_t FS_Tell(qfile_t *file)
Definition fs.c:3461
int FS_Seek(qfile_t *file, fs_offset_t offset, int whence)
Definition fs.c:3359
qfile_t * FS_OpenRealFile(const char *filepath, const char *mode, qbool quiet)
Definition fs.c:2901
qbool FS_AddPack(const char *pakfile, qbool *already_loaded, qbool keep_plain_dirs, qbool dlcache)
Definition fs.c:1309
int FS_Close(qfile_t *file)
Definition fs.c:2970
const char * FS_WhichPack(const char *filename)
Definition fs.c:4091
const char * FS_FileExists(const char *filename)
Look for a file in the packages and in the filesystem Returns its canonical name (same case as used i...
Definition fs.c:3693
int64_t fs_offset_t
Definition fs.h:37
skinframe_t * R_SkinFrame_LoadInternalBGRA(const char *name, int textureflags, const unsigned char *skindata, int width, int height, int comparewidth, int compareheight, int comparecrc, qbool sRGB)
Definition gl_rmain.c:2546
GLsizei const GLchar ** string
Definition glquake.h:728
GLuint buffer
Definition glquake.h:630
GLenum GLuint GLenum GLsizei length
Definition glquake.h:657
GLsizeiptr const GLvoid * data
Definition glquake.h:639
GLint GLenum GLenum GLvoid * pixels
Definition glquake.h:706
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition glquake.h:657
const GLchar * name
Definition glquake.h:601
cvar_t developer
Definition host.c:48
host_static_t host
Definition host.c:41
void Host_Error(const char *error,...)
Definition host.c:85
int image_height
Definition image.c:10
int image_width
Definition image.c:9
unsigned char * PNG_LoadImage_BGRA(const unsigned char *raw, int filesize, int *miplevel)
Definition image_png.c:294
unsigned char * JPEG_LoadImage_BGRA(const unsigned char *f, int filesize, int *miplevel)
Definition jpeg.c:606
static void curl_default_callback(int status, size_t length_received, unsigned char *buffer, void *cbdata)
Definition libcurl.c:456
static CURLMcode(* qcurl_multi_wait)(CURLM *multi_handle, void *, unsigned int extra_nfds, int timeout_ms, int *ret)
Definition libcurl.c:163
static dllhandle_t curl_dll
Definition libcurl.c:195
static cvar_t curl_useragent
Definition libcurl.c:13
static const char *(* qcurl_multi_strerror)(CURLcode)
Definition libcurl.c:168
static void CURL_CloseLibrary(void)
Definition libcurl.c:397
static cvar_t curl_enabled
Definition libcurl.c:10
static char command_when_error[256]
Definition libcurl.c:243
static int numdownloads_success
Definition libcurl.c:240
void Curl_Register_predownload(void)
Definition libcurl.c:315
static int numdownloads_fail
Definition libcurl.c:239
static CURLM * curlm
Definition libcurl.c:403
static void Curl_Curl_f(cmd_state_t *cmd)
Definition libcurl.c:1424
void Curl_Init_Commands(void)
Definition libcurl.c:1578
static const char *(* qcurl_easy_strerror)(CURLcode)
Definition libcurl.c:159
static double bytes_received
Definition libcurl.c:404
void Curl_SendRequirements(void)
Definition libcurl.c:1853
struct CURLM_s CURLM
Definition libcurl.c:34
void Curl_Clear_forthismap(void)
Definition libcurl.c:287
bool Curl_Select(int timeout_ms)
Definition libcurl.c:1263
struct curl_slist curl_slist
Definition libcurl.c:35
static const char * Curl_FindPackURL(const char *filename)
Definition libcurl.c:1701
static qbool CURL_OpenLibrary(void)
Definition libcurl.c:362
Curl_downloadinfo_t * Curl_GetDownloadInfo(int *nDownloads, const char **additional_info, char *addinfo, size_t addinfolength)
Definition libcurl.c:1612
#define LOADTYPE_NONE
Definition libcurl.c:198
#define CURL_GLOBAL_SSL
Definition libcurl.c:48
static void(* qcurl_global_cleanup)(void)
Definition libcurl.c:153
#define CURLINFO_STRING
Definition libcurl.c:92
#define LOADTYPE_PAK
Definition libcurl.c:199
#define CURLINFO_DOUBLE
Definition libcurl.c:94
qbool Curl_Have_forthismap(void)
Definition libcurl.c:310
static qbool noclear
Definition libcurl.c:237
static double curltime
Definition libcurl.c:406
static double Curl_GetDownloadAmount(downloadinfo *di)
Definition libcurl.c:1323
static size_t CURL_fwrite(void *data, size_t size, size_t nmemb, void *vdi)
Definition libcurl.c:416
static int numdownloads
Definition libcurl.c:235
static void CheckPendingDownloads(void)
Definition libcurl.c:683
static CURL *(* qcurl_easy_init)(void)
Definition libcurl.c:155
static void(* qcurl_global_init)(long flags)
Definition libcurl.c:152
#define CURLINFO_SLIST
Definition libcurl.c:95
CurlStatus
Definition libcurl.c:448
@ CURL_DOWNLOAD_SUCCESS
Definition libcurl.c:449
@ CURL_DOWNLOAD_ABORTED
Definition libcurl.c:451
@ CURL_DOWNLOAD_SERVERERROR
Definition libcurl.c:452
@ CURL_DOWNLOAD_FAILED
Definition libcurl.c:450
#define CURLPROTO_HTTP
Definition libcurl.c:77
static void Curl_CommandWhenDone(const char *cmd)
Definition libcurl.c:254
static downloadinfo * Curl_Find(const char *filename)
Definition libcurl.c:852
void Curl_Frame(void)
Definition libcurl.c:1134
static cvar_t sv_curl_serverpackages
Definition libcurl.c:17
static const char * CleanURL(const char *url, char *urlbuf, size_t urlbuflength)
Definition libcurl.c:651
static void Curl_EndDownload(downloadinfo *di, CurlStatus status, CURLcode error, const char *content_type_)
Definition libcurl.c:521
static double Curl_GetDownloadSpeed(downloadinfo *di)
Definition libcurl.c:1347
static qbool Curl_SendRequirement(const char *filename, qbool foundone, char *sendbuffer, size_t sendbuffer_len)
Definition libcurl.c:1817
#define CINIT(name, type, number)
Definition libcurl.c:54
CURLoption
Definition libcurl.c:56
static requirement * requirements
Definition libcurl.c:1769
static CURLMcode(* qcurl_multi_add_handle)(CURLM *multi_handle, CURL *easy_handle)
Definition libcurl.c:164
void Curl_Init(void)
Definition libcurl.c:810
void * curl_mutex
Definition libcurl.c:203
static void Curl_CommandWhenError(const char *cmd)
Definition libcurl.c:270
CURLMSG
Definition libcurl.c:132
@ CURLMSG_DONE
Definition libcurl.c:134
@ CURLMSG_LAST
Definition libcurl.c:136
@ CURLMSG_NONE
Definition libcurl.c:133
static void(* qcurl_easy_cleanup)(CURL *handle)
Definition libcurl.c:156
void Curl_ClearRequirements(void)
Definition libcurl.c:1795
static CURLMcode(* qcurl_multi_perform)(CURLM *multi_handle, int *running_handles)
Definition libcurl.c:162
qbool Curl_Begin_ToFile(const char *URL, double maxspeed, const char *name, int loadtype, qbool forthismap)
Definition libcurl.c:1113
static curl_slist *(* qcurl_slist_append)(curl_slist *list, const char *string)
Definition libcurl.c:169
#define CURLPROTO_HTTPS
Definition libcurl.c:78
qbool Curl_Begin_ToMemory_POST(const char *URL, const char *extraheaders, double maxspeed, const char *post_content_type, const unsigned char *postbuf, size_t postbufsize, unsigned char *buf, size_t bufsize, curl_callback_t callback, void *cbdata)
Definition libcurl.c:1121
#define CLEAR_AND_RETRY()
static void Curl_CheckCommandWhenDone(void)
Definition libcurl.c:331
static cvar_t curl_useragent_append
Definition libcurl.c:14
qbool Curl_Available(void)
Definition libcurl.c:840
CURLMcode
Definition libcurl.c:42
@ CURLM_CALL_MULTI_PERFORM
Definition libcurl.c:43
@ CURLM_OK
Definition libcurl.c:44
static CURLcode(* qcurl_easy_getinfo)(CURL *handle, CURLINFO info,...)
Definition libcurl.c:158
curl_infotype
Definition libcurl.c:81
@ CURLINFO_SSL_DATA_OUT
Definition libcurl.c:88
@ CURLINFO_END
Definition libcurl.c:89
@ CURLINFO_HEADER_OUT
Definition libcurl.c:84
@ CURLINFO_DATA_IN
Definition libcurl.c:85
@ CURLINFO_TEXT
Definition libcurl.c:82
@ CURLINFO_DATA_OUT
Definition libcurl.c:86
@ CURLINFO_HEADER_IN
Definition libcurl.c:83
@ CURLINFO_SSL_DATA_IN
Definition libcurl.c:87
static CURLcode(* qcurl_easy_setopt)(CURL *handle, CURLoption option,...)
Definition libcurl.c:157
static int numdownloads_added
Definition libcurl.c:241
#define CURLPROTO_FTP
Definition libcurl.c:79
static CURLMcode(* qcurl_multi_remove_handle)(CURLM *multi_handle, CURL *easy_handle)
Definition libcurl.c:165
qbool Curl_Begin_ToMemory(const char *URL, double maxspeed, unsigned char *buf, size_t bufsize, curl_callback_t callback, void *cbdata)
Definition libcurl.c:1117
static cvar_t curl_maxdownloads
Definition libcurl.c:11
void Curl_CancelAll(void)
Definition libcurl.c:1284
CURLINFO
Definition libcurl.c:99
@ CURLINFO_SSL_ENGINES
Definition libcurl.c:127
@ CURLINFO_HEADER_SIZE
Definition libcurl.c:111
@ CURLINFO_SIZE_DOWNLOAD
Definition libcurl.c:108
@ CURLINFO_PRIVATE
Definition libcurl.c:121
@ CURLINFO_HTTPAUTH_AVAIL
Definition libcurl.c:123
@ CURLINFO_CONNECT_TIME
Definition libcurl.c:105
@ CURLINFO_FILETIME
Definition libcurl.c:114
@ CURLINFO_NUM_CONNECTS
Definition libcurl.c:126
@ CURLINFO_REQUEST_SIZE
Definition libcurl.c:112
@ CURLINFO_NONE
Definition libcurl.c:100
@ CURLINFO_SSL_VERIFYRESULT
Definition libcurl.c:113
@ CURLINFO_PRETRANSFER_TIME
Definition libcurl.c:106
@ CURLINFO_CONTENT_LENGTH_UPLOAD
Definition libcurl.c:116
@ CURLINFO_OS_ERRNO
Definition libcurl.c:125
@ CURLINFO_PROXYAUTH_AVAIL
Definition libcurl.c:124
@ CURLINFO_NAMELOOKUP_TIME
Definition libcurl.c:104
@ CURLINFO_SIZE_UPLOAD
Definition libcurl.c:107
@ CURLINFO_REDIRECT_COUNT
Definition libcurl.c:120
@ CURLINFO_CONTENT_TYPE
Definition libcurl.c:118
@ CURLINFO_SPEED_UPLOAD
Definition libcurl.c:110
@ CURLINFO_HTTP_CONNECTCODE
Definition libcurl.c:122
@ CURLINFO_REDIRECT_TIME
Definition libcurl.c:119
@ CURLINFO_CONTENT_LENGTH_DOWNLOAD
Definition libcurl.c:115
@ CURLINFO_EFFECTIVE_URL
Definition libcurl.c:101
@ CURLINFO_TOTAL_TIME
Definition libcurl.c:103
@ CURLINFO_SPEED_DOWNLOAD
Definition libcurl.c:109
@ CURLINFO_RESPONSE_CODE
Definition libcurl.c:102
@ CURLINFO_STARTTRANSFER_TIME
Definition libcurl.c:117
static void curl_quiet_callback(int status, size_t length_received, unsigned char *buffer, void *cbdata)
Definition libcurl.c:482
static char command_when_done[256]
Definition libcurl.c:242
static unsigned char * decode_image(downloadinfo *di, const char *content_type)
Definition libcurl.c:487
static CURLM *(* qcurl_multi_init)(void)
Definition libcurl.c:161
void Curl_RequireFile(const char *filename)
Definition libcurl.c:1779
static double bytes_sent
Definition libcurl.c:405
static cvar_t sv_curl_maxspeed
Definition libcurl.c:18
static qbool Curl_Begin(const char *URL, const char *extraheaders, double maxspeed, const char *name, int loadtype, qbool forthismap, const char *post_content_type, const unsigned char *postbuf, size_t postbufsize, unsigned char *buf, size_t bufsize, curl_callback_t callback, void *cbdata)
Definition libcurl.c:886
static dllfunction_t curlfuncs[]
Definition libcurl.c:172
static void(* qcurl_slist_free_all)(curl_slist *list)
Definition libcurl.c:170
static void(* qcurl_multi_cleanup)(CURLM *)
Definition libcurl.c:167
void Curl_Cancel_ToMemory(curl_callback_t callback, void *cbdata)
Definition libcurl.c:863
#define CURLINFO_LONG
Definition libcurl.c:93
void Curl_Shutdown(void)
Definition libcurl.c:828
static CURLMsg *(* qcurl_multi_info_read)(CURLM *multi_handle, int *msgs_in_queue)
Definition libcurl.c:166
static void Curl_Info_f(cmd_state_t *cmd)
Definition libcurl.c:1369
static cvar_t developer_curl
Definition libcurl.c:20
#define LOADTYPE_CACHEPIC
Definition libcurl.c:200
qbool Curl_Running(void)
Definition libcurl.c:1307
CURLcode
Definition libcurl.c:37
@ CURLE_OK
Definition libcurl.c:38
struct CURL_s CURL
Definition libcurl.c:33
static cvar_t curl_maxspeed
Definition libcurl.c:12
static cvar_t sv_curl_defaulturl
Definition libcurl.c:16
#define LOADTYPE_SKINFRAME
Definition libcurl.c:201
@ CURLCBSTATUS_UNKNOWN
Definition libcurl.h:11
@ CURLCBSTATUS_SERVERERROR
Definition libcurl.h:10
@ CURLCBSTATUS_OK
Definition libcurl.h:7
@ CURLCBSTATUS_FAILED
Definition libcurl.h:8
@ CURLCBSTATUS_ABORTED
Definition libcurl.h:9
void(* curl_callback_t)(int status, size_t length_received, unsigned char *buffer, void *cbdata)
Definition libcurl.h:13
float strlen(string s)
void error(string err,...)
void cmd(string command,...)
int i
#define MAX_OSPATH
max length of a filesystem pathname
Definition qdefs.h:175
#define NULL
Definition qtypes.h:12
bool qbool
Definition qtypes.h:9
#define TEXF_ALPHA
Definition r_textures.h:9
#define TEXF_MIPMAP
Definition r_textures.h:11
@ TEXTYPE_BGRA
Definition r_textures.h:53
#define TEXF_CLAMP
Definition r_textures.h:15
#define TEXF_FORCE_RELOAD
Definition r_textures.h:41
void SV_ClientCommands(const char *fmt,...) DP_FUNC_PRINTF(1)
Definition sv_send.c:135
dp_FragColor r
return ret
float f
dp_FragColor b
ret a
CURL * easy_handle
Definition libcurl.c:142
CURLMSG msg
Definition libcurl.c:141
union CURLMsg::@16 data
CURLcode result
Definition libcurl.c:146
void * whatever
Definition libcurl.c:145
char filename[MAX_QPATH]
Definition libcurl.h:40
qbool loadbegun
Definition client.h:1027
cactive_t state
Definition client.h:568
netconn_t * netcon
Definition client.h:630
char userinfo[MAX_USERINFO_STRING]
Definition client.h:669
command interpreter state - the tokenizing and execution of commands, as well as pointers to which cv...
Definition cmd.h:127
Definition cvar.h:66
float value
Definition cvar.h:74
int integer
Definition cvar.h:73
const char * string
Definition cvar.h:71
qbool started
Definition libcurl.c:213
double bytes_sent_curl
Definition libcurl.c:217
qfile_t * stream
Definition libcurl.c:210
size_t buffersize
Definition libcurl.c:224
CURL * curle
Definition libcurl.c:212
char referer[256]
Definition libcurl.c:209
size_t postbufsize
Definition libcurl.c:229
qbool forthismap
Definition libcurl.c:219
curl_callback_t callback
Definition libcurl.c:225
size_t bytes_received
Definition libcurl.c:215
char filename[MAX_OSPATH]
Definition libcurl.c:207
double maxspeed
Definition libcurl.c:220
unsigned char * buffer
Definition libcurl.c:223
double bytes_received_curl
Definition libcurl.c:216
const char * extraheaders
Definition libcurl.c:231
llist_t list
Definition libcurl.c:218
fs_offset_t startpos
Definition libcurl.c:211
const char * post_content_type
Definition libcurl.c:230
char url[1024]
Definition libcurl.c:208
curl_slist * slist
Definition libcurl.c:221
const unsigned char * postbuf
Definition libcurl.c:228
void * callback_data
Definition libcurl.c:226
double realtime
the accumulated mainloop time since application started (with filtering), without any slowmo or clamp...
Definition host.h:46
char address[128]
Definition netconn.h:231
char filename[MAX_OSPATH]
Definition libcurl.c:1766
struct requirement_s * next
Definition libcurl.c:1765
void * dllhandle_t
Definition sys.h:169
qbool Sys_LoadDependency(const char **dllnames, dllhandle_t *handle, const dllfunction_t *fcts)
Definition sys_shared.c:131
void Sys_FreeLibrary(dllhandle_t *handle)
Definition sys_shared.c:245
#define Thread_DestroyMutex(m)
Definition thread.h:16
qbool Thread_HasThreads(void)
Definition thread_null.c:13
#define Thread_CreateMutex()
Definition thread.h:15
#define Thread_LockMutex(m)
Definition thread.h:17
#define Thread_UnlockMutex(m)
Definition thread.h:18
mempool_t * tempmempool
Definition zone.c:794
#define Mem_Free(mem)
Definition zone.h:96
#define Mem_Alloc(pool, size)
Definition zone.h:92
#define Z_Malloc(size)
Definition zone.h:161
#define Z_Free(data)
Definition zone.h:164