typedef struct CURL_s CURL;
typedef struct CURLM_s CURLM;
+typedef struct curl_slist curl_slist;
typedef enum
{
CURLE_OK = 0
CINIT(URL, OBJECTPOINT, 2),
CINIT(ERRORBUFFER, OBJECTPOINT, 10),
CINIT(WRITEFUNCTION, FUNCTIONPOINT, 11),
+ CINIT(POSTFIELDS, OBJECTPOINT, 15),
CINIT(REFERER, OBJECTPOINT, 16),
CINIT(USERAGENT, OBJECTPOINT, 18),
+ CINIT(LOW_SPEED_LIMIT, LONG , 19),
+ CINIT(LOW_SPEED_TIME, LONG, 20),
CINIT(RESUME_FROM, LONG, 21),
+ CINIT(HTTPHEADER, OBJECTPOINT, 23),
+ CINIT(POST, LONG, 47), /* HTTP POST method */
CINIT(FOLLOWLOCATION, LONG, 52), /* use Location: Luke! */
+ CINIT(POSTFIELDSIZE, LONG, 60),
CINIT(PRIVATE, OBJECTPOINT, 103),
- CINIT(LOW_SPEED_LIMIT, LONG , 19),
- CINIT(LOW_SPEED_TIME, LONG, 20),
CINIT(PROTOCOLS, LONG, 181),
CINIT(REDIR_PROTOCOLS, LONG, 182)
}
static CURLMsg * (*qcurl_multi_info_read) (CURLM *multi_handle, int *msgs_in_queue);
static void (*qcurl_multi_cleanup) (CURLM *);
static const char * (*qcurl_multi_strerror) (CURLcode);
+static curl_slist * (*qcurl_slist_append) (curl_slist *list, const char *string);
+static void (*qcurl_slist_free_all) (curl_slist *list);
static dllfunction_t curlfuncs[] =
{
{"curl_multi_info_read", (void **) &qcurl_multi_info_read},
{"curl_multi_cleanup", (void **) &qcurl_multi_cleanup},
{"curl_multi_strerror", (void **) &qcurl_multi_strerror},
+ {"curl_slist_append", (void **) &qcurl_slist_append},
+ {"curl_slist_free_all", (void **) &qcurl_slist_free_all},
{NULL, NULL}
};
CURL *curle;
qboolean started;
qboolean ispak;
- unsigned long bytes_received;
+ unsigned long bytes_received; // for buffer
+ double bytes_received_curl; // for throttling
+ double bytes_sent_curl; // for throttling
struct downloadinfo_s *next, *prev;
qboolean forthismap;
double maxspeed;
+ curl_slist *slist; // http headers
unsigned char *buffer;
size_t buffersize;
curl_callback_t callback;
void *callback_data;
+
+ const unsigned char *postbuf;
+ size_t postbufsize;
+ const char *post_content_type;
+ const char *extraheaders;
}
downloadinfo;
static downloadinfo *downloads = NULL;
static CURLM *curlm = NULL;
-static unsigned long bytes_received = 0; // used for bandwidth throttling
+static double bytes_received = 0; // used for bandwidth throttling
+static double bytes_sent = 0; // used for bandwidth throttling
static double curltime = 0;
/*
ret = FS_Write(di->stream, data, bytes);
}
- bytes_received += bytes;
di->bytes_received += bytes;
return ret; // why not ret / nmemb?
code from libcurl, or 0, if another error has occurred.
====================
*/
-static qboolean Curl_Begin(const char *URL, double maxspeed, const char *name, qboolean ispak, qboolean forthismap, unsigned char *buf, size_t bufsize, curl_callback_t callback, void *cbdata);
+static qboolean Curl_Begin(const char *URL, const char *extraheaders, double maxspeed, const char *name, qboolean ispak, qboolean 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);
static void Curl_EndDownload(downloadinfo *di, CurlStatus status, CURLcode error)
{
qboolean ok = false;
{
qcurl_multi_remove_handle(curlm, di->curle);
qcurl_easy_cleanup(di->curle);
+ if(di->slist)
+ qcurl_slist_free_all(di->slist);
}
if(!di->callback && ok && !di->bytes_received)
{
// this was a resume?
// then try to redownload it without reporting the error
- Curl_Begin(di->url, di->maxspeed, di->filename, di->ispak, di->forthismap, NULL, 0, NULL, NULL);
+ Curl_Begin(di->url, di->extraheaders, di->maxspeed, di->filename, di->ispak, di->forthismap, di->post_content_type, di->postbuf, di->postbufsize, NULL, 0, NULL, NULL);
di->forthismap = false; // don't count the error
}
}
*/
static void CheckPendingDownloads(void)
{
+ const char *h;
if(!curl_dll)
return;
if(numdownloads < cl_curl_maxdownloads.integer)
}
di->curle = qcurl_easy_init();
+ di->slist = NULL;
qcurl_easy_setopt(di->curle, CURLOPT_URL, di->url);
qcurl_easy_setopt(di->curle, CURLOPT_USERAGENT, engineversion);
qcurl_easy_setopt(di->curle, CURLOPT_REFERER, di->referer);
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");
//qcurl_easy_setopt(di->curle, CURLOPT_FOLLOWLOCATION, 0);
}
+ if(di->post_content_type)
+ {
+ qcurl_easy_setopt(di->curle, CURLOPT_POST, 1);
+ qcurl_easy_setopt(di->curle, CURLOPT_POSTFIELDS, di->postbuf);
+ qcurl_easy_setopt(di->curle, CURLOPT_POSTFIELDSIZE, di->postbufsize);
+ di->slist = qcurl_slist_append(di->slist, va("Content-Type: %s", di->post_content_type));
+ }
+
+ // parse extra headers into slist
+ // \n separated list!
+ h = di->extraheaders;
+ while(h)
+ {
+ const char *hh = strchr(h, '\n');
+ if(hh)
+ {
+ char *buf = (char *) Mem_Alloc(tempmempool, hh - h + 1);
+ memcpy(buf, h, hh - h);
+ buf[hh - h] = 0;
+ di->slist = qcurl_slist_append(di->slist, buf);
+ h = hh + 1;
+ }
+ else
+ {
+ di->slist = qcurl_slist_append(di->slist, h);
+ h = NULL;
+ }
+ }
+
+ qcurl_easy_setopt(di->curle, CURLOPT_HTTPHEADER, di->slist);
+
qcurl_multi_add_handle(curlm, di->curle);
di->started = true;
if given) in the "dlcache/" folder.
====================
*/
-static qboolean Curl_Begin(const char *URL, double maxspeed, const char *name, qboolean ispak, qboolean forthismap, unsigned char *buf, size_t bufsize, curl_callback_t callback, void *cbdata)
+static qboolean Curl_Begin(const char *URL, const char *extraheaders, double maxspeed, const char *name, qboolean ispak, qboolean 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)
{
if(!curl_dll)
{
di->ispak = (ispak && !buf);
di->maxspeed = maxspeed;
di->bytes_received = 0;
+ di->bytes_received_curl = 0;
+ di->bytes_sent_curl = 0;
+ di->extraheaders = extraheaders;
di->next = downloads;
di->prev = NULL;
if(di->next)
di->callback_data = cbdata;
}
+ if(post_content_type)
+ {
+ di->post_content_type = post_content_type;
+ di->postbuf = postbuf;
+ di->postbufsize = postbufsize;
+ }
+ else
+ {
+ di->post_content_type = NULL;
+ di->postbuf = NULL;
+ di->postbufsize = 0;
+ }
+
downloads = di;
return true;
}
qboolean Curl_Begin_ToFile(const char *URL, double maxspeed, const char *name, qboolean ispak, qboolean forthismap)
{
- return Curl_Begin(URL, maxspeed, name, ispak, forthismap, NULL, 0, NULL, NULL);
+ return Curl_Begin(URL, NULL, maxspeed, name, ispak, forthismap, NULL, NULL, 0, NULL, 0, NULL, NULL);
}
qboolean Curl_Begin_ToMemory(const char *URL, double maxspeed, unsigned char *buf, size_t bufsize, curl_callback_t callback, void *cbdata)
{
- return Curl_Begin(URL, maxspeed, NULL, false, false, buf, bufsize, callback, cbdata);
+ return Curl_Begin(URL, NULL, maxspeed, NULL, false, false, NULL, NULL, 0, buf, bufsize, callback, cbdata);
+}
+qboolean 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)
+{
+ return Curl_Begin(URL, extraheaders, maxspeed, NULL, false, false, post_content_type, postbuf, postbufsize, buf, bufsize, callback, cbdata);
}
/*
}
while(mc == CURLM_CALL_MULTI_PERFORM);
+ for(di = downloads; di; di = di->next)
+ {
+ double b = 0;
+ qcurl_easy_getinfo(di->curle, CURLINFO_SIZE_UPLOAD, &b);
+ bytes_sent += (b - di->bytes_sent_curl);
+ di->bytes_sent_curl = b;
+ qcurl_easy_getinfo(di->curle, CURLINFO_SIZE_DOWNLOAD, &b);
+ bytes_sent += (b - di->bytes_received_curl);
+ di->bytes_received_curl = b;
+ }
+
for(;;)
{
CURLMsg *msg = qcurl_multi_info_read(curlm, &remaining);
if(maxspeed > 0)
{
- unsigned long bytes = bytes_received; // maybe smoothen a bit?
+ double bytes = bytes_sent + bytes_received; // maybe smoothen a bit?
curltime = realtime + bytes / (cl_curl_maxspeed.value * 1024.0);
- bytes_received -= bytes;
+ bytes_sent = 0;
+ bytes_received = 0;
}
else
curltime = realtime;
*/
void Curl_ClearRequirements(void)
{
- const char *p;
while(requirements)
{
requirement *req = requirements;
requirements = requirements->next;
Z_Free(req);
}
- p = sv_curl_serverpackages.string;
- Con_DPrintf("Require all of: %s\n", p);
- while(COM_ParseToken_Simple(&p, false, false))
- {
- Con_DPrintf("Require: %s\n", com_token);
- Curl_RequireFile(com_token);
- }
}
/*
curl --finish_autodownload
====================
*/
+static qboolean Curl_SendRequirement(const char *filename, qboolean foundone, char *sendbuffer, size_t sendbuffer_len)
+{
+ const char *p;
+ const char *thispack = FS_WhichPack(filename);
+ const char *packurl;
+
+ if(!thispack)
+ return false;
+
+ p = strrchr(thispack, '/');
+ if(p)
+ thispack = p + 1;
+
+ packurl = Curl_FindPackURL(thispack);
+
+ if(packurl && *packurl && strcmp(packurl, "-"))
+ {
+ if(!foundone)
+ strlcat(sendbuffer, "curl --clear_autodownload\n", sendbuffer_len);
+
+ strlcat(sendbuffer, "curl --pak --forthismap --as ", sendbuffer_len);
+ strlcat(sendbuffer, thispack, sendbuffer_len);
+ if(sv_curl_maxspeed.value > 0)
+ dpsnprintf(sendbuffer + strlen(sendbuffer), sendbuffer_len - strlen(sendbuffer), " --maxspeed=%.1f", sv_curl_maxspeed.value);
+ strlcat(sendbuffer, " --for ", sendbuffer_len);
+ strlcat(sendbuffer, filename, sendbuffer_len);
+ strlcat(sendbuffer, " ", sendbuffer_len);
+ strlcat(sendbuffer, packurl, sendbuffer_len);
+ strlcat(sendbuffer, thispack, sendbuffer_len);
+ strlcat(sendbuffer, "\n", sendbuffer_len);
+
+ return true;
+ }
+
+ return false;
+}
void Curl_SendRequirements(void)
{
// for each requirement, find the pack name
char sendbuffer[4096] = "";
requirement *req;
qboolean foundone = false;
+ const char *p;
for(req = requirements; req; req = req->next)
- {
- const char *p;
- const char *thispack = FS_WhichPack(req->filename);
- const char *packurl;
-
- if(!thispack)
- continue;
-
- p = strrchr(thispack, '/');
- if(p)
- thispack = p + 1;
-
- packurl = Curl_FindPackURL(thispack);
+ foundone = Curl_SendRequirement(req->filename, foundone, sendbuffer, sizeof(sendbuffer)) || foundone;
- if(packurl && *packurl && strcmp(packurl, "-"))
- {
- if(!foundone)
- strlcat(sendbuffer, "curl --clear_autodownload\n", sizeof(sendbuffer));
-
- strlcat(sendbuffer, "curl --pak --forthismap --as ", sizeof(sendbuffer));
- strlcat(sendbuffer, thispack, sizeof(sendbuffer));
- if(sv_curl_maxspeed.value > 0)
- dpsnprintf(sendbuffer + strlen(sendbuffer), sizeof(sendbuffer) - strlen(sendbuffer), " --maxspeed=%.1f", sv_curl_maxspeed.value);
- strlcat(sendbuffer, " --for ", sizeof(sendbuffer));
- strlcat(sendbuffer, req->filename, sizeof(sendbuffer));
- strlcat(sendbuffer, " ", sizeof(sendbuffer));
- strlcat(sendbuffer, packurl, sizeof(sendbuffer));
- strlcat(sendbuffer, thispack, sizeof(sendbuffer));
- strlcat(sendbuffer, "\n", sizeof(sendbuffer));
-
- foundone = true;
- }
- }
+ p = sv_curl_serverpackages.string;
+ while(COM_ParseToken_Simple(&p, false, false))
+ foundone = Curl_SendRequirement(com_token, foundone, sendbuffer, sizeof(sendbuffer)) || foundone;
if(foundone)
strlcat(sendbuffer, "curl --finish_autodownload\n", sizeof(sendbuffer));