61 const unsigned char c = *(
string++);
63 if (c >= 32 && c <= 127)
72 if (!isalnum(c) && c !=
'_' && c !=
'-')
81static int CL_HTTP_Progress (
void* clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
93 cls.downloadPercent = (
int)((dlnow / dltotal) * 100.0f);
95 cls.downloadPercent = 0;
110 size_t len = strlen(filePath);
111 for (
int i = 0;
i <
len;
i++) {
112 if (!isalnum(filePath[
i]) && filePath[
i] !=
';' && filePath[
i] !=
'/' &&
113 filePath[
i] !=
'?' && filePath[
i] !=
':' && filePath[
i] !=
'@' && filePath[
i] !=
'&' &&
114 filePath[
i] !=
'=' && filePath[
i] !=
'+' && filePath[
i] !=
'$' && filePath[
i] !=
',' &&
115 filePath[
i] !=
'[' && filePath[
i] !=
']' && filePath[
i] !=
'-' && filePath[
i] !=
'_' &&
116 filePath[
i] !=
'.' && filePath[
i] !=
'!' && filePath[
i] !=
'~' && filePath[
i] !=
'*' &&
117 filePath[
i] !=
'\'' && filePath[
i] !=
'(' && filePath[
i] !=
')') {
118 sprintf(p,
"%%%02x", filePath[
i]);
129 len = strlen(escaped);
131 while ((p = strstr (p,
"./"))) {
132 memmove(p, p + 2,
len - (p - escaped) - 1);
147 if (extension !=
nullptr &&
Q_streq(extension,
"filelist")) {
179 dl->
curl = curl_easy_init();
183 curl_easy_setopt(dl->
curl, CURLOPT_ENCODING,
"");
185 curl_easy_setopt(dl->
curl, CURLOPT_VERBOSE, 1);
187 curl_easy_setopt(dl->
curl, CURLOPT_NOPROGRESS, 0);
189 curl_easy_setopt(dl->
curl, CURLOPT_WRITEDATA, dl->
file);
190 curl_easy_setopt(dl->
curl, CURLOPT_WRITEFUNCTION,
nullptr);
192 curl_easy_setopt(dl->
curl, CURLOPT_WRITEDATA, dl);
193 curl_easy_setopt(dl->
curl, CURLOPT_WRITEFUNCTION,
HTTP_Recv);
197 curl_easy_setopt(dl->
curl, CURLOPT_FAILONERROR, 1);
199 curl_easy_setopt(dl->
curl, CURLOPT_FOLLOWLOCATION, 1);
200 curl_easy_setopt(dl->
curl, CURLOPT_MAXREDIRS, 5);
201 curl_easy_setopt(dl->
curl, CURLOPT_WRITEHEADER, dl);
204 curl_easy_setopt(dl->
curl, CURLOPT_PROGRESSDATA, dl);
206 curl_easy_setopt(dl->
curl, CURLOPT_REFERER,
cls.downloadReferer);
207 curl_easy_setopt(dl->
curl, CURLOPT_URL, dl->
URL);
208 curl_easy_setopt(dl->
curl, CURLOPT_NOSIGNAL, 1);
210 if (curl_multi_add_handle(
multi, dl->
curl) != CURLM_OK) {
218 Com_Printf(
"CL_StartHTTPDownload: Fetching %s...\n", dl->
URL);
234 cls.downloadQueue = 0;
239 multi = curl_multi_init();
257 for (
dlqueue_t* q =
cls.downloadQueue; q; q = q->next) {
263 cls.downloadServer[0] = 0;
273 for (
int i = 0;
i < 4;
i++) {
293 for (; *anchor; anchor = &(*anchor)->
next) {
295 if (
Q_streq(ufoPath, (*anchor)->ufoPath))
308 if (extension !=
nullptr && !
Q_strcasecmp(extension,
"bsp")) {
310 const size_t len = strlen(ufoPath);
330 if (
cls.downloadServer[0] ==
'\0')
395 size_t length = strlen(path);
406 Com_Printf(
"NOTICE: Filelist is requesting a .pk3 file (%s)\n", path);
423 Com_Printf(
"WARNING: Illegal file type '%s' in filelist.\n", path);
428 if (path[0] ==
'@') {
430 Com_Printf(
"WARNING: @ prefix used on a pk3 file (%s) in filelist.\n", path);
440 strchr(path,
'\\') || (!pak && !strchr(path,
'/')) || (pak && strchr(path,
'/'))) {
441 Com_Printf(
"WARNING: Illegal path '%s' in filelist.\n", path);
446 if (gameLocal || pak) {
470 while ((*anchor)->next) anchor = &(*anchor)->
next;
476 cls.downloadQueue = d;
495 char* p = strchr(list,
'\n');
520 for (
dlqueue_t* q =
cls.downloadQueue; q; q = q->next) {
535 for (
int i = 0;
i < 4;
i++) {
549 curl_multi_remove_handle(
multi, dl->
curl);
550 curl_easy_cleanup(dl->
curl);
558 curl_multi_cleanup(
multi);
571 CURLMsg* msg = curl_multi_info_read(
multi, &messagesInQueue);
575 Com_Printf(
"CL_FinishHTTPDownload: Odd, no message for us...\n");
579 if (msg->msg != CURLMSG_DONE) {
580 Com_Printf(
"CL_FinishHTTPDownload: Got some weird message...\n");
584 CURL* curl = msg->easy_handle;
587 for (
int i = 0;
i < 4;
i++) {
588 if (
cls.HTTPHandles[
i].curl == curl) {
589 dl = &
cls.HTTPHandles[
i];
617 cls.downloadName[0] = 0;
618 cls.downloadPosition = 0;
620 CURLcode result = msg->data.result;
628 case CURLE_HTTP_RETURNED_ERROR:
630 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode);
631 if (responseCode == 404) {
633 if (extension !=
nullptr &&
Q_streq(extension,
"pk3"))
639 curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD_T, &fileSize);
640 if (fileSize > 512) {
643 result = CURLE_FILESIZE_EXCEEDED;
644 Com_Printf(
"Oversized 404 body received (%d bytes), aborting HTTP downloading.\n", (
int)fileSize);
646 curl_multi_remove_handle(
multi, dl->
curl);
649 }
else if (responseCode == 200) {
658 case CURLE_COULDNT_RESOLVE_HOST:
659 case CURLE_COULDNT_CONNECT:
660 case CURLE_COULDNT_RESOLVE_PROXY:
663 Com_Printf(
"Fatal HTTP error: %s\n", curl_easy_strerror(result));
664 curl_multi_remove_handle(
multi, dl->
curl);
675 Com_Printf(
"HTTP download failed: %s\n", curl_easy_strerror(result));
676 curl_multi_remove_handle(
multi, dl->
curl);
688 int i = strlen(tempName);
689 if (
Q_streq(tempName +
i - 4,
".pk3")) {
697 curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &timeTaken);
698 curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD_T, &fileSize);
705 curl_multi_remove_handle(
multi, dl->
curl);
707 Com_Printf(
"HTTP(%s): %ld bytes, %.2fkB/sec [%d remaining files]\n",
709 }
while (messagesInQueue > 0);
715 cls.downloadServer[0] = 0;
729 for (
dlqueue_t* q =
cls.downloadQueue; q; q = q->next) {
738 const size_t len = strlen(q->ufoPath);
757 if (!
cls.downloadServer[0])
769 ret = curl_multi_perform(
multi, &newHandleCount);
776 }
while (ret == CURLM_CALL_MULTI_PERFORM);
778 if (ret != CURLM_OK) {
779 Com_Printf(
"curl_multi_perform error. Aborting HTTP downloads.\n");
bool CL_CheckOrDownloadFile(const char *filename)
static void StripHighBits(char *string)
static void CL_CheckAndQueueDownload(char *path)
Validate a path supplied by a filelist.
bool CL_PendingHTTPDownloads(void)
See if we're still busy with some downloads. Called by precacher just before it loads the map since w...
static bool isvalidchar(int c)
void CL_RunHTTPDownloads(void)
This calls curl_multi_perform do actually do stuff. Called every frame while connecting to minimise l...
static cvar_t * cl_http_filelists
static dlhandle_t * CL_GetFreeDLHandle(void)
Find a free download handle to start another queue entry on.
static int CL_HTTP_Progress(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
libcurl callback to update progress info. Mainly just used as a way to cancel the transfer if require...
void CL_HTTP_Cleanup(void)
UFO is exiting or we're changing servers. Clean up.
static int abortDownloads
static void CL_StartHTTPDownload(dlqueue_t *entry, dlhandle_t *dl)
Actually starts a download by adding it to the curl multi handle.
void CL_CancelHTTPDownloads(bool permKill)
Cancel all downloads and nuke the queue.
static bool downloadingPK3
static void CL_ParseFileList(dlhandle_t *dl)
A filelist is in memory, scan and validate it and queue up the files.
void CL_SetHTTPServer(const char *URL)
A new server is specified, so we nuke all our state.
static void CL_FinishHTTPDownload(void)
A download finished, find out what it was, whether there were any errors and if so,...
bool CL_QueueHTTPDownload(const char *ufoPath)
Called from the precache check to queue a download.
static cvar_t * cl_http_max_connections
static cvar_t * cl_http_downloads
static void CL_EscapeHTTPPath(const char *filePath, char *escaped)
Properly escapes a path with HTTP encoding. libcurl's function seems to treat '/' and such as illegal...
static void CL_ReVerifyHTTPQueue(void)
A pk3 file just downloaded, let's see if we can remove some stuff from the queue which is in the ....
void HTTP_InitStartup(void)
static void CL_StartNextHTTPDownload(void)
Start another HTTP download if possible.
void CL_RequestNextDownload(void)
Primary header for client.
void Com_Error(int code, const char *fmt,...)
void Com_Printf(const char *const fmt,...)
cvar_t * Cvar_Get(const char *var_name, const char *var_value, int flags, const char *desc)
Init or return a cvar.
void FS_CreatePath(const char *path)
Creates any directories needed to store the given filename.
void FS_RemoveFile(const char *osPath)
int FS_CheckFile(const char *fmt,...)
Just returns the filelength and -1 if the file wasn't found.
int FS_LoadFile(const char *path, byte **buffer)
Filenames are relative to the quake search path.
void FS_RestartFilesystem(const char *gamedir)
Restart the filesystem (reload all pk3 files).
bool FS_RenameFile(const char *from, const char *to, bool relative)
Renames a file.
const char * FS_Gamedir(void)
Called to find where to write a file (savegames, etc).
size_t HTTP_Recv(void *ptr, size_t size, size_t nmemb, void *stream)
libcurl callback for HTTP_GetURL
size_t HTTP_Header(void *ptr, size_t size, size_t nmemb, void *stream)
libcurl callback to update header info.
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
#define Mem_AllocType(type)
QGL_EXTERN GLuint GLchar GLuint * len
QGL_EXTERN GLuint GLsizei GLsizei * length
#define Q_strcasecmp(a, b)
bool Q_strnull(const char *string)
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
const char * Com_GetExtension(const char *path)
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
char filePath[MAX_OSPATH]
FILE * Sys_Fopen(const char *filename, const char *mode)
int Sys_Remove(const char *filename)