VM_uri_escape, // #510 string(string in) uri_escape = #510;
VM_uri_unescape, // #511 string(string in) uri_unescape = #511;
VM_etof, // #512 float(entity ent) num_for_edict = #512 (DP_QC_NUM_FOR_EDICT)
-VM_uri_get, // #513 float(string uril, float id) uri_get = #512; (DP_QC_URI_GET)
+VM_uri_get, // #513 float(string uri, float id, [string post_contenttype, string post_delim, [float buf]]) uri_get = #513; (DP_QC_URI_GET, DP_QC_URI_POST)
VM_tokenize_console, // #514 float(string str) tokenize_console = #514; (DP_QC_TOKENIZE_CONSOLE)
VM_argv_start_index, // #515 float(float idx) argv_start_index = #515; (DP_QC_TOKENIZE_CONSOLE)
VM_argv_end_index, // #516 float(float idx) argv_end_index = #516; (DP_QC_TOKENIZE_CONSOLE)
return pos;
}
-void file2lumps(const char *fn, unsigned long header, const char **lumps, size_t *lumpsize, size_t nlumps)
+void file2buf(const char *fn, char **data, size_t *datasize)
{
FILE *f;
- char buf[65536];
- size_t n;
+ *data = NULL;
+ *datasize = 0;
+ size_t n = 0, dn = 0;
f = fopen(fn, "rb");
if(!f)
+ {
+ return;
+ }
+ for(;;)
+ {
+ *data = realloc(*data, *datasize += 8192);
+ if(!*data)
+ {
+ *datasize = 0;
+ return;
+ }
+ dn = fread(*data + n, 1, *datasize - n, f);
+ if(!dn)
+ break;
+ n += dn;
+ }
+ fclose(f);
+ *datasize = n;
+}
+
+int buf2file(const char *fn, const char *data, size_t n)
+{
+ FILE *f;
+ f = fopen(fn, "wb");
+ if(!f)
+ return 0;
+ n = fwrite(data, n, 1, f);
+ if(fclose(f) || !n)
+ return 0;
+ return 1;
+}
+
+void file2lumps(const char *fn, unsigned long header, const char **lumps, size_t *lumpsize, size_t nlumps)
+{
+ char *buf;
+ size_t n;
+ file2buf(fn, &buf, &n);
+ if(!buf)
{
fprintf(stderr, "could not open %s\n", fn);
exit(1);
}
- n = fread(buf, 1, sizeof(buf), f);
- fclose(f);
if(!Crypto_ParsePack(buf, n, header, lumps, lumpsize, nlumps))
{
fprintf(stderr, "could not parse %s as %c%c%c%c (%d lumps expected)\n", fn, (int) header & 0xFF, (int) (header >> 8) & 0xFF, (int) (header >> 16) & 0xFF, (int) (header >> 24) & 0xFF, (int) nlumps);
+ free(buf);
exit(1);
}
+ free(buf);
}
mode_t umask_save;
void lumps2file(const char *fn, unsigned long header, const char *const *lumps, size_t *lumpsize, size_t nlumps, D0_BOOL private)
{
- FILE *f;
char buf[65536];
size_t n;
if(private)
umask(umask_save | 0077);
else
umask(umask_save);
- f = fopen(fn, "wb");
- if(!f)
- {
- fprintf(stderr, "could not open %s\n", fn);
- exit(1);
- }
if(!(n = Crypto_UnParsePack(buf, sizeof(buf), header, lumps, lumpsize, nlumps)))
{
fprintf(stderr, "could not unparse for %s\n", fn);
exit(1);
}
- n = fwrite(buf, n, 1, f);
- if(fclose(f) || !n)
+ if(!buf2file(fn, buf, n))
{
fprintf(stderr, "could not write %s\n", fn);
exit(1);
"%s -p public.d0pk -i id.d0pi\n"
"%s -p public.d0pk -I idkey.d0si\n"
"%s -0 -p public.d0pk -I idkey.d0si\n"
- "%s -0 -p public.d0pk\n",
- me, me, me, me, me, me, me, me, me, me, me, me, me, me
+ "%s -0 -p public.d0pk\n"
+ "%s -p public.d0pk -I idkey.d0si -f file-to-sign.dat -o file-signed.dat\n"
+ "%s -p public.d0pk -f file-signed.dat -o file-content.dat\n"
+ "%s -p public.d0pk -f file-signed.dat -o file-content.dat -O idkey.d0pi\n",
+ me, me, me, me, me, me, me, me, me, me, me, me, me, me, me, me, me
);
}
int opt;
size_t lumpsize[2];
const char *lumps[2];
+ char *databuf_in; size_t databufsize_in;
+ char *databuf_out; size_t databufsize_out;
char lumps_w0[65536];
char lumps_w1[65536];
- const char *pubkeyfile = NULL, *privkeyfile = NULL, *pubidfile = NULL, *prividfile = NULL, *idreqfile = NULL, *idresfile = NULL, *outfile = NULL, *outfile2 = NULL, *camouflagefile = NULL;
+ const char *pubkeyfile = NULL, *privkeyfile = NULL, *pubidfile = NULL, *prividfile = NULL, *idreqfile = NULL, *idresfile = NULL, *outfile = NULL, *outfile2 = NULL, *camouflagefile = NULL, *datafile = NULL;
char fp64[513]; size_t fp64size = 512;
int mask = 0;
int bits = 1024;
umask_save = umask(0022);
ctx = d0_blind_id_new();
- while((opt = getopt(argc, argv, "p:P:i:I:j:J:o:O:c:b:x:X:y:Fn:C0")) != -1)
+ while((opt = getopt(argc, argv, "f:p:P:i:I:j:J:o:O:c:b:x:X:y:Fn:C0")) != -1)
{
switch(opt)
{
// test mode
mask |= 0x200;
break;
+ case 'f':
+ datafile = optarg;
+ mask |= 0x400;
+ break;
case 'X':
infix = optarg;
break;
}
}
+ if(mask & 0x400)
+ {
+ file2buf(datafile, &databuf_in, &databufsize_in);
+ if(!databuf_in)
+ {
+ fprintf(stderr, "could not decode private ID\n");
+ exit(1);
+ }
+ }
+
switch(mask)
{
// modes of operation:
CHECK(d0_blind_id_fingerprint64_public_id(ctx, fp64, &fp64size));
printf("%.*s\n", (int)fp64size, fp64);
break;
+ case 0x449:
+ // public key, private ID, data -> signed data
+ databufsize_out = databufsize_in + 8192;
+ databuf_out = malloc(databufsize_out);
+ CHECK(d0_blind_id_sign_with_private_id_sign(ctx, 1, 0, databuf_in, databufsize_in, databuf_out, &databufsize_out));
+ buf2file(outfile, databuf_out, databufsize_out);
+ break;
+ case 0x441:
+ case 0x4C1:
+ // public key, data -> signed data, optional public ID
+ {
+ D0_BOOL status;
+ databufsize_out = databufsize_in;
+ databuf_out = malloc(databufsize_out);
+ CHECK(d0_blind_id_sign_with_private_id_verify(ctx, 1, 0, databuf_in, databufsize_in, databuf_out, &databufsize_out, &status));
+ CHECK(d0_blind_id_fingerprint64_public_id(ctx, fp64, &fp64size));
+ printf("%d\n", (int)status);
+ printf("%.*s\n", (int)fp64size, fp64);
+ buf2file(outfile, databuf_out, databufsize_out);
+
+ if(outfile2)
+ {
+ lumps[0] = lumps_w0;
+ lumpsize[0] = sizeof(lumps_w0);
+ lumps[1] = lumps_w1;
+ lumpsize[1] = sizeof(lumps_w1);
+ CHECK(d0_blind_id_write_public_key(ctx, lumps_w0, &lumpsize[0]));
+ CHECK(d0_blind_id_write_private_id_modulus(ctx, lumps_w1, &lumpsize[1]));
+ lumps2file(outfile2, FOURCC_D0PK, lumps, lumpsize, 2, 0);
+ }
+ }
+ break;
/*
case 0x09:
// public key, private ID file -> test whether key is properly signed
#define qd0_blind_id_INITIALIZE d0_blind_id_INITIALIZE
#define qd0_blind_id_SHUTDOWN d0_blind_id_SHUTDOWN
#define qd0_blind_id_util_sha256 d0_blind_id_util_sha256
+#define qd0_blind_id_sign_with_private_id_sign d0_blind_id_sign_with_private_id_sign
#else
static D0_EXPORT D0_WARN_UNUSED_RESULT D0_BOOL (*qd0_blind_id_INITIALIZE) (void);
static D0_EXPORT void (*qd0_blind_id_SHUTDOWN) (void);
static D0_EXPORT void (*qd0_blind_id_util_sha256) (char *out, const char *in, size_t n);
+static D0_EXPORT D0_WARN_UNUSED_RESULT D0_BOOL (*qd0_blind_id_sign_with_private_id_sign) (d0_blind_id_t *ctx, D0_BOOL is_first, D0_BOOL send_modulus, const char *message, size_t msglen, char *outbuf, size_t *outbuflen);
static dllfunction_t d0_blind_id_funcs[] =
{
{"d0_blind_id_new", (void **) &qd0_blind_id_new},
{"d0_blind_id_INITIALIZE", (void **) &qd0_blind_id_INITIALIZE},
{"d0_blind_id_SHUTDOWN", (void **) &qd0_blind_id_SHUTDOWN},
{"d0_blind_id_util_sha256", (void **) &qd0_blind_id_util_sha256},
+ {"d0_blind_id_sign_with_private_id_sign", (void **) &qd0_blind_id_sign_with_private_id_sign},
{NULL, NULL}
};
// end of d0_blind_id interface
}
int Crypto_RetrieveLocalKey(int keyid, char *keyfp, size_t keyfplen, char *idfp, size_t idfplen) // return value: -1 if more to come, +1 if valid, 0 if end of list
{
- if(keyid < 0 || keyid > MAX_PUBKEYS)
+ if(keyid < 0 || keyid >= MAX_PUBKEYS)
return 0;
if(keyfp)
*keyfp = 0;
return CRYPTO_NOMATCH;
}
+
+size_t Crypto_SignData(const void *data, size_t datasize, int keyid, void *signed_data, size_t signed_size)
+{
+ if(keyid < 0 || keyid >= MAX_PUBKEYS)
+ return 0;
+ if(!pubkeys_havepriv[keyid])
+ return 0;
+ if(qd0_blind_id_sign_with_private_id_sign(pubkeys[keyid], true, false, data, datasize, signed_data, &signed_size))
+ return signed_size;
+ return 0;
+}
qboolean Crypto_RetrieveHostKey(lhnetaddress_t *peeraddress, int *keyid, char *keyfp, size_t keyfplen, char *idfp, size_t idfplen, int *aeslevel);
int Crypto_RetrieveLocalKey(int keyid, char *keyfp, size_t keyfplen, char *idfp, size_t idfplen); // return value: -1 if more to come, +1 if valid, 0 if end of list
+qboolean Crypto_SignData(const void *data, size_t datasize, int keyid, void *signed_data, size_t signed_size);
+
// netconn protocol:
// non-crypto:
// getchallenge >
//<already in EXT_CSQC> string(float keynum) keynumtostring = #340;
//description: key bind setting/getting including support for switchable
//bindmaps.
+
+//DP_CRYPTO
+//idea: divVerent
+//darkplaces implementation: divVerent
+//builtin definitions: (CSQC)
+float(string url, float id, string content_type, string delim, float buf, float keyid) crypto_uri_postbuf = #513;
+//description:
+//use -1 as buffer handle to justs end delim as postdata
// ouxXc take a float if no length is specified or h is, and an int/entity if l is specified as length, and cast it to an unsigned int
// eEfFgG take a float if no length is specified or h is, and an int/entity if l is specified as length, and cast it to a double
// s takes a string
+// vV takes a vector, and processes the three components as if it were a gG for all three components, separated by space
// For conversions s and c, the flag # makes precision and width interpreted
// as byte count, by default it is interpreted as character count in UTF-8
// enabled engines. No other conversions can create wide characters, and #
//if 1 is returned by uri_get, the callback will be called in the future
float(string url, float id) uri_get = #513;
+//DP_QC_URI_POST
+//idea: divVerent
+//darkplaces implementation: divVerent
+//loads text from an URL into a string after POSTing via HTTP
+//works like uri_get, but uri_post sends data with Content-Type: content_type to the server
+//and uri_post sends the string buffer buf, joined using the delimiter delim
+float(string url, float id, string content_type, string data) uri_post = #513;
+float(string url, float id, string content_type, string delim, float buf) uri_postbuf = #513;
+
//DP_SKELETONOBJECTS
//idea: LordHavoc
//darkplaces implementation: LordHavoc
//implementation notes:
//entity customization is done before per-client culling (visibility for instance) because the entity may be doing setorigin to display itself in different locations on different clients, may be altering its .modelindex, .effects and other fields important to culling, so customized entities increase cpu usage (non-customized entities can use all the early culling they want however, as they are not changing on a per client basis).
+//DP_SV_DISCARDABLEDEMO
+//idea: parasti
+//darkplaces implementation: parasti
+//field definitions:
+.float discardabledemo;
+//description:
+//when this field is set to a non-zero value on a player entity, a possibly recorded server-side demo for the player is discarded
+//Note that this extension only works if:
+// auto demos are enabled (the cvar sv_autodemo_perclient is set)
+// discarding demos is enabled (the cvar sv_autodemo_perclient_discardable is set)
+
//DP_SV_DRAWONLYTOCLIENT
//idea: LordHavoc
//darkplaces implementation: LordHavoc
float Q3SURFACEFLAG_NOIMPACT = 16; // projectiles should remove themselves on impact (this is set on sky)
float Q3SURFACEFLAG_NOMARKS = 32; // projectiles should not leave marks, such as decals (this is set on sky)
float Q3SURFACEFLAG_FLESH = 64; // projectiles should do a fleshy effect (blood?) on impact
-//float Q3SURFACEFLAG_NODRAW = 128; // compiler hint (not important to qc)
+float Q3SURFACEFLAG_NODRAW = 128; // compiler hint (not important to qc)
//float Q3SURFACEFLAG_HINT = 256; // compiler hint (not important to qc)
//float Q3SURFACEFLAG_SKIP = 512; // compiler hint (not important to qc)
//float Q3SURFACEFLAG_NOLIGHTMAP = 1024; // compiler hint (not important to qc)
//float Q3SURFACEFLAG_POINTLIGHT = 2048; // compiler hint (not important to qc)
float Q3SURFACEFLAG_METALSTEPS = 4096; // walking on this surface should make metal step sounds
float Q3SURFACEFLAG_NOSTEPS = 8192; // walking on this surface should not make footstep sounds
-//float Q3SURFACEFLAG_NONSOLID = 16384; // compiler hint (not important to qc)
+float Q3SURFACEFLAG_NONSOLID = 16384; // compiler hint (not important to qc)
//float Q3SURFACEFLAG_LIGHTFILTER = 32768; // compiler hint (not important to qc)
//float Q3SURFACEFLAG_ALPHASHADOW = 65536; // compiler hint (not important to qc)
//float Q3SURFACEFLAG_NODLIGHT = 131072; // compiler hint (not important to qc)
.string crypto_encryptmethod; // the string "AES128" if encrypting, and string_null if plaintext
.string crypto_signmethod; // the string "HMAC-SHA256" if signing, and string_null if plaintext
// there is no field crypto_myidfp, as that info contains no additional information the QC may have a use for
+//builtin definitions: (SVQC)
+float(string url, float id, string content_type, string delim, float buf, float keyid) crypto_uri_postbuf = #513;
//description:
+//use -1 as buffer handle to justs end delim as postdata
//idea: divVerent
//darkplaces implementation: divVerent
//field definitions: (MENUQC)
-string crypto_getkeyfp(string serveraddress) = #633; // retrieves the cached host key's CA fingerprint of a server given by IP address
-string crypto_getidfp(string serveraddress) = #634; // retrieves the cached host key fingerprint of a server given by IP address
-string crypto_getencryptlevel(string serveraddress) = #635; // 0 if never encrypting, 1 supported, 2 requested, 3 required, appended by list of allowed methods in order of preference ("AES128"), preceded by a space each
-string crypto_getmykeyfp(float i) = #636; // retrieves the CA key fingerprint of a given CA slot, or "" if slot is unused but more to come, or string_null if end of list
-string crypto_getmyidfp(float i) = #637; // retrieves the ID fingerprint of a given CA slot, or "" if slot is unused but more to come, or string_null if end of list
+string(string serveraddress) crypto_getkeyfp = #633; // retrieves the cached host key's CA fingerprint of a server given by IP address
+string(string serveraddress) crypto_getidfp = #634; // retrieves the cached host key fingerprint of a server given by IP address
+string(string serveraddress) crypto_getencryptlevel = #635; // 0 if never encrypting, 1 supported, 2 requested, 3 required, appended by list of allowed methods in order of preference ("AES128"), preceded by a space each
+string(float i) crypto_getmykeyfp = #636; // retrieves the CA key fingerprint of a given CA slot, or "" if slot is unused but more to come, or string_null if end of list
+string(float i) crypto_getmyidfp = #637; // retrieves the ID fingerprint of a given CA slot, or "" if slot is unused but more to come, or string_null if end of list
+float(string url, float id, string content_type, string delim, float buf, float keyid) crypto_uri_postbuf = #513;
//description:
+//use -1 as buffer handle to justs end delim as postdata
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;
}
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, 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)
di->stream = FS_OpenRealFile(di->filename, "wb", false);
FS_Close(di->stream);
- if(di->startpos && !di->callback)
+ if(di->startpos && !di->callback && !di->post_content_type)
{
// 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->maxspeed, di->filename, di->ispak, di->forthismap, NULL, NULL, 0, NULL, 0, NULL, NULL);
di->forthismap = false; // don't count the error
}
}
}
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));
+ }
+ 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, 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->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, 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, maxspeed, NULL, false, false, NULL, NULL, 0, buf, bufsize, callback, cbdata);
+}
+qboolean Curl_Begin_ToMemory_POST(const char *URL, 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, 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_Run(void);
qboolean Curl_Running(void);
qboolean Curl_Begin_ToFile(const char *URL, double maxspeed, const char *name, qboolean ispak, qboolean forthismap);
+
qboolean Curl_Begin_ToMemory(const char *URL, double maxspeed, unsigned char *buf, size_t bufsize, curl_callback_t callback, void *cbdata);
- // NOTE: if it returns false, the callback will NOT get called, so free your buffer then!
+qboolean Curl_Begin_ToMemory_POST(const char *URL, 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);
+ // NOTE: if these return false, the callback will NOT get called, so free your buffer then!
void Curl_Cancel_ToMemory(curl_callback_t callback, void *cbdata);
// removes all downloads with the given callback and cbdata (this does NOT call the callbacks!)
+
void Curl_Init(void);
void Curl_Init_Commands(void);
void Curl_Shutdown(void);
"DP_QC_UNLIMITEDTEMPSTRINGS "
"DP_QC_URI_ESCAPE "
"DP_QC_URI_GET "
+"DP_QC_URI_POST "
"DP_QC_WHICHPACK "
"FTE_STRINGS "
;
VM_uri_escape, // #510 string(string in) uri_escape = #510;
VM_uri_unescape, // #511 string(string in) uri_unescape = #511;
VM_etof, // #512 float(entity ent) num_for_edict = #512 (DP_QC_NUM_FOR_EDICT)
-VM_uri_get, // #513 float(string uril, float id) uri_get = #513; (DP_QC_URI_GET)
+VM_uri_get, // #513 float(string uri, float id, [string post_contenttype, string post_delim, [float buf]]) uri_get = #513; (DP_QC_URI_GET, DP_QC_URI_POST)
VM_tokenize_console, // #514 float(string str) tokenize_console = #514; (DP_QC_TOKENIZE_CONSOLE)
VM_argv_start_index, // #515 float(float idx) argv_start_index = #515; (DP_QC_TOKENIZE_CONSOLE)
VM_argv_end_index, // #516 float(float idx) argv_end_index = #516; (DP_QC_TOKENIZE_CONSOLE)
double starttime;
float id;
char buffer[MAX_INPUTLINE];
+ unsigned char *postdata; // free when uri_to_prog_t is freed
+ size_t postlen;
}
uri_to_prog_t;
if(!PRVM_ProgLoaded(handle->prognr))
{
// curl reply came too late... so just drop it
+ if(handle->postdata)
+ Z_Free(handle->postdata);
Z_Free(handle);
return;
}
}
PRVM_End;
+ if(handle->postdata)
+ Z_Free(handle->postdata);
Z_Free(handle);
}
float id;
qboolean ret;
uri_to_prog_t *handle;
+ const char *posttype = NULL;
+ const char *postseparator = NULL;
+ int poststringbuffer = -1;
+ int postkeyid = -1;
if(!prog->funcoffsets.URI_Get_Callback)
PRVM_ERROR("uri_get called by %s without URI_Get_Callback defined", PRVM_NAME);
- VM_SAFEPARMCOUNT(2, VM_uri_get);
+ VM_SAFEPARMCOUNTRANGE(2, 6, VM_uri_get);
url = PRVM_G_STRING(OFS_PARM0);
id = PRVM_G_FLOAT(OFS_PARM1);
+ if(prog->argc >= 3)
+ posttype = PRVM_G_STRING(OFS_PARM2);
+ if(prog->argc >= 4)
+ postseparator = PRVM_G_STRING(OFS_PARM3);
+ if(prog->argc >= 5)
+ poststringbuffer = PRVM_G_FLOAT(OFS_PARM4);
+ if(prog->argc >= 6)
+ postkeyid = PRVM_G_FLOAT(OFS_PARM4);
handle = (uri_to_prog_t *) Z_Malloc(sizeof(*handle)); // this can't be the prog's mem pool, as curl may call the callback later!
handle->prognr = PRVM_GetProgNr();
handle->starttime = prog->starttime;
handle->id = id;
- ret = Curl_Begin_ToMemory(url, 0, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle);
+ if(postseparator)
+ {
+ size_t l = strlen(postseparator);
+ if(poststringbuffer >= 0)
+ {
+ size_t ltotal;
+ int i;
+ // "implode"
+ prvm_stringbuffer_t *stringbuffer;
+ stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, poststringbuffer);
+ if(!stringbuffer)
+ {
+ VM_Warning("uri_get: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+ ltotal = 0;
+ for(i = 0;i < stringbuffer->num_strings;i++)
+ {
+ if(i > 0)
+ ltotal += l;
+ if(stringbuffer->strings[i])
+ ltotal += strlen(stringbuffer->strings[i]);
+ }
+ handle->postdata = Z_Malloc(ltotal);
+ handle->postlen = ltotal;
+ ltotal = 0;
+ for(i = 0;i < stringbuffer->num_strings;i++)
+ {
+ if(i > 0)
+ {
+ memcpy(handle->postdata + ltotal, postseparator, l);
+ ltotal += l;
+ }
+ if(stringbuffer->strings[i])
+ {
+ memcpy(handle->postdata + ltotal, stringbuffer->strings[i], strlen(stringbuffer->strings[i]));
+ ltotal += strlen(stringbuffer->strings[i]);
+ }
+ }
+ if(ltotal != handle->postlen)
+ PRVM_ERROR ("%s: string buffer content size mismatch, possible overrun", PRVM_NAME);
+ }
+ else
+ {
+ handle->postdata = Z_Malloc(l);
+ handle->postlen = l;
+ memcpy(handle->postdata, postseparator, l);
+ }
+ if(postkeyid >= 0)
+ {
+ unsigned char *signed_data = Z_Malloc(handle->postlen + 8192);
+ size_t signed_size = Crypto_SignData(handle->postdata, handle->postlen, postkeyid, signed_data, handle->postlen + 8192);
+ if(!signed_size)
+ {
+ VM_Warning("uri_get: could not sign with key id %i\n", postkeyid);
+ return;
+ }
+ handle->postdata = signed_data;
+ handle->postlen = signed_size;
+ }
+ ret = Curl_Begin_ToMemory_POST(url, 0, posttype, handle->postdata, handle->postlen, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle);
+ }
+ else
+ {
+ handle->postdata = NULL;
+ handle->postlen = 0;
+ ret = Curl_Begin_ToMemory(url, 0, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle);
+ }
if(ret)
{
PRVM_G_INT(OFS_RETURN) = 1;
}
else
{
+ if(handle->postdata)
+ Z_Free(handle->postdata);
Z_Free(handle);
PRVM_G_INT(OFS_RETURN) = 0;
}
"DP_QC_UNLIMITEDTEMPSTRINGS "
"DP_QC_URI_ESCAPE "
"DP_QC_URI_GET "
+"DP_QC_URI_POST "
"DP_QC_VECTOANGLES_WITH_ROLL "
"DP_QC_VECTORVECTORS "
"DP_QC_WHICHPACK "
VM_uri_escape, // #510 string(string in) uri_escape = #510;
VM_uri_unescape, // #511 string(string in) uri_unescape = #511;
VM_etof, // #512 float(entity ent) num_for_edict = #512 (DP_QC_NUM_FOR_EDICT)
-VM_uri_get, // #513 float(string uril, float id) uri_get = #513; (DP_QC_URI_GET)
+VM_uri_get, // #513 float(string uri, float id, [string post_contenttype, string post_delim, [float buf]]) uri_get = #513; (DP_QC_URI_GET, DP_QC_URI_POST)
VM_tokenize_console, // #514 float(string str) tokenize_console = #514; (DP_QC_TOKENIZE_CONSOLE)
VM_argv_start_index, // #515 float(float idx) argv_start_index = #515; (DP_QC_TOKENIZE_CONSOLE)
VM_argv_end_index, // #516 float(float idx) argv_end_index = #516; (DP_QC_TOKENIZE_CONSOLE)