From 1dfae27462dba735f317cac38f3b7deb415ed154 Mon Sep 17 00:00:00 2001 From: divverent Date: Thu, 25 Nov 2010 12:56:30 +0000 Subject: [PATCH] DP_QC_URI_POST extension (uri_post, uri_postbuf, crypto_uri_postbuf) git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@10627 d7cf8633-e32d-0410-b094-e92efae38249 --- clvm_cmds.c | 2 +- crypto-keygen-standalone.c | 118 +++++++++++++++++++++++++++++++------ crypto.c | 16 ++++- crypto.h | 2 + dpdefs/csprogsdefs.qc | 8 +++ dpdefs/dpextensions.qc | 28 ++++++++- dpdefs/menudefs.qc | 12 ++-- libcurl.c | 84 ++++++++++++++++++++++---- libcurl.h | 5 +- mvm_cmds.c | 3 +- prvm_cmds.c | 91 +++++++++++++++++++++++++++- svvm_cmds.c | 3 +- 12 files changed, 327 insertions(+), 45 deletions(-) diff --git a/clvm_cmds.c b/clvm_cmds.c index f816eb35..c95864ba 100644 --- a/clvm_cmds.c +++ b/clvm_cmds.c @@ -4517,7 +4517,7 @@ NULL, // #509 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) diff --git a/crypto-keygen-standalone.c b/crypto-keygen-standalone.c index 7ae6d66e..95268400 100644 --- a/crypto-keygen-standalone.c +++ b/crypto-keygen-standalone.c @@ -91,49 +91,80 @@ static size_t Crypto_UnParsePack(char *buf, size_t len, unsigned long header, co 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); @@ -156,8 +187,11 @@ void USAGE(const char *me) "%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 ); } @@ -235,9 +269,11 @@ int main(int argc, char **argv) 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; @@ -253,7 +289,7 @@ int main(int argc, char **argv) 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) { @@ -310,6 +346,10 @@ int main(int argc, char **argv) // test mode mask |= 0x200; break; + case 'f': + datafile = optarg; + mask |= 0x400; + break; case 'X': infix = optarg; break; @@ -425,6 +465,16 @@ int main(int argc, char **argv) } } + 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: @@ -553,6 +603,38 @@ int main(int argc, char **argv) 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 diff --git a/crypto.c b/crypto.c index b679ed2d..75111696 100644 --- a/crypto.c +++ b/crypto.c @@ -142,6 +142,7 @@ static size_t Crypto_UnParsePack(char *buf, size_t len, unsigned long header, co #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 @@ -189,6 +190,7 @@ static D0_EXPORT D0_WARN_UNUSED_RESULT D0_BOOL (*qd0_blind_id_sessionkey_public_ 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}, @@ -224,6 +226,7 @@ static dllfunction_t d0_blind_id_funcs[] = {"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 @@ -726,7 +729,7 @@ qboolean Crypto_RetrieveHostKey(lhnetaddress_t *peeraddress, int *keyid, char *k } 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; @@ -2357,3 +2360,14 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, 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; +} diff --git a/crypto.h b/crypto.h index 19e9735d..b78041f6 100644 --- a/crypto.h +++ b/crypto.h @@ -53,6 +53,8 @@ const char *Crypto_GetInfoResponseDataString(void); 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 > diff --git a/dpdefs/csprogsdefs.qc b/dpdefs/csprogsdefs.qc index 6c6ad5e5..60e15bec 100644 --- a/dpdefs/csprogsdefs.qc +++ b/dpdefs/csprogsdefs.qc @@ -859,3 +859,11 @@ string(string command, float bindmap) findkeysforcommand = #610; // 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 diff --git a/dpdefs/dpextensions.qc b/dpdefs/dpextensions.qc index 602465c3..afe67c9b 100644 --- a/dpdefs/dpextensions.qc +++ b/dpdefs/dpextensions.qc @@ -968,6 +968,7 @@ string(string format, ...) sprintf = #627; // 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 # @@ -1139,6 +1140,15 @@ string(string in) uri_unescape = #511; //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 @@ -1375,6 +1385,17 @@ float(entity clent) clienttype = #455; // returns one of the CLIENTTYPE_* consta //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 @@ -1866,14 +1887,14 @@ float Q3SURFACEFLAG_LADDER = 8; // climbable surface 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) @@ -2380,4 +2401,7 @@ float JOINTTYPE_HINGE2 = 5; // hinge2; uses origin (anchor), angles (axis1), vel .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 diff --git a/dpdefs/menudefs.qc b/dpdefs/menudefs.qc index e34082cd..4ae9ad3f 100644 --- a/dpdefs/menudefs.qc +++ b/dpdefs/menudefs.qc @@ -453,9 +453,11 @@ float(string key) stringtokeynum = #341; //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 diff --git a/libcurl.c b/libcurl.c index c6e2b472..9b1f5567 100644 --- a/libcurl.c +++ b/libcurl.c @@ -22,6 +22,7 @@ static cvar_t cl_curl_enabled = {CVAR_SAVE, "cl_curl_enabled","1", "whether clie typedef struct CURL_s CURL; typedef struct CURLM_s CURLM; +typedef struct curl_slist curl_slist; typedef enum { CURLE_OK = 0 @@ -47,13 +48,17 @@ typedef enum 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) } @@ -149,6 +154,8 @@ static CURLMcode (*qcurl_multi_remove_handle) (CURLM *multi_handle, CURL *easy_h 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[] = { @@ -166,6 +173,8 @@ 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} }; @@ -183,15 +192,22 @@ typedef struct downloadinfo_s 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; @@ -358,7 +374,8 @@ static void CURL_CloseLibrary (void) 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; /* @@ -390,7 +407,6 @@ static size_t CURL_fwrite(void *data, size_t size, size_t nmemb, void *vdi) ret = FS_Write(di->stream, data, bytes); } - bytes_received += bytes; di->bytes_received += bytes; return ret; // why not ret / nmemb? @@ -445,7 +461,7 @@ CURL_DOWNLOAD_FAILED or CURL_DOWNLOAD_ABORTED) and in the second case the error 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; @@ -484,6 +500,8 @@ static void Curl_EndDownload(downloadinfo *di, CurlStatus status, CURLcode error { 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) @@ -506,11 +524,11 @@ static void Curl_EndDownload(downloadinfo *di, CurlStatus status, CURLcode error 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 } } @@ -610,6 +628,7 @@ static void CheckPendingDownloads(void) } 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); @@ -626,6 +645,14 @@ static void CheckPendingDownloads(void) 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; @@ -716,7 +743,7 @@ Starts a download of a given URL to the file name portion of this URL (or name 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) { @@ -876,6 +903,8 @@ static qboolean Curl_Begin(const char *URL, double maxspeed, const char *name, q 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) @@ -894,6 +923,19 @@ static qboolean Curl_Begin(const char *URL, double maxspeed, const char *name, q 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; } @@ -901,11 +943,15 @@ static qboolean Curl_Begin(const char *URL, double maxspeed, const char *name, q 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); } /* @@ -947,6 +993,17 @@ void Curl_Run(void) } 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); @@ -997,9 +1054,10 @@ void Curl_Run(void) 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; diff --git a/libcurl.h b/libcurl.h index 56a94e2a..4b098bec 100644 --- a/libcurl.h +++ b/libcurl.h @@ -12,10 +12,13 @@ typedef void (*curl_callback_t) (int status, size_t length_received, unsigned ch 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); diff --git a/mvm_cmds.c b/mvm_cmds.c index 044dedc7..4f7e3019 100644 --- a/mvm_cmds.c +++ b/mvm_cmds.c @@ -41,6 +41,7 @@ const char *vm_m_extensions = "DP_QC_UNLIMITEDTEMPSTRINGS " "DP_QC_URI_ESCAPE " "DP_QC_URI_GET " +"DP_QC_URI_POST " "DP_QC_WHICHPACK " "FTE_STRINGS " ; @@ -1385,7 +1386,7 @@ NULL, // #509 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) diff --git a/prvm_cmds.c b/prvm_cmds.c index 13672c5d..5702665b 100644 --- a/prvm_cmds.c +++ b/prvm_cmds.c @@ -5801,6 +5801,8 @@ typedef struct 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; @@ -5811,6 +5813,8 @@ static void uri_to_string_callback(int status, size_t length_received, unsigned 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; } @@ -5830,6 +5834,8 @@ static void uri_to_string_callback(int status, size_t length_received, unsigned } PRVM_End; + if(handle->postdata) + Z_Free(handle->postdata); Z_Free(handle); } @@ -5841,26 +5847,107 @@ void VM_uri_get (void) 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; } diff --git a/svvm_cmds.c b/svvm_cmds.c index c3319365..890dee78 100644 --- a/svvm_cmds.c +++ b/svvm_cmds.c @@ -126,6 +126,7 @@ const char *vm_sv_extensions = "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 " @@ -3651,7 +3652,7 @@ NULL, // #509 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) -- 2.39.2