]> git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
DP_QC_URI_POST extension (uri_post, uri_postbuf, crypto_uri_postbuf)
authordivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Thu, 25 Nov 2010 12:56:30 +0000 (12:56 +0000)
committerdivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Thu, 25 Nov 2010 12:56:30 +0000 (12:56 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@10627 d7cf8633-e32d-0410-b094-e92efae38249

12 files changed:
clvm_cmds.c
crypto-keygen-standalone.c
crypto.c
crypto.h
dpdefs/csprogsdefs.qc
dpdefs/dpextensions.qc
dpdefs/menudefs.qc
libcurl.c
libcurl.h
mvm_cmds.c
prvm_cmds.c
svvm_cmds.c

index f816eb35a7f6c1121fed8c2806fb11ee4bb1506a..c95864ba9b078cbeadd38312d2e334d795f67e72 100644 (file)
@@ -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)
index 7ae6d66e37523797337d6fa0f4c1a1a85139b011..95268400c0acd508db88f9d51cb2caeb0b29c143 100644 (file)
@@ -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
index b679ed2d9a8fb0df76d5198ba8f9649be225294b..751116965b62a85d8777ea4a3aef9e378c2601ca 100644 (file)
--- 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;
+}
index 19e9735d5955771310432edf8c7b64fa71fca89c..b78041f6a1ad6186d3c6da47fa008ff9d048f163 100644 (file)
--- 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                                            >
index 6c6ad5e59dd6bd9a43429cf8d35561c96060559b..60e15bec2d2574062dd51c738e6788cde37470d7 100644 (file)
@@ -859,3 +859,11 @@ string(string command, float bindmap) findkeysforcommand = #610;
 //<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
index 602465c3501da45685bbffb41db027781b9496ea..afe67c9b5b83faf675daf74800fa904e9b9ba09f 100644 (file)
@@ -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
index e34082cd35fde1f50b5f301ec905bbc3255b19d1..4ae9ad3f3ae13c2d02d3573bddcf0ca47f97f3a6 100644 (file)
@@ -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
index c6e2b472e96ecce8284780e8931d77dc410d0686..9b1f55673b9133120b4b6bff0248ff8f9a1dafe9 100644 (file)
--- 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;
index 56a94e2a82628735a51e011bae41eee017e0ff7c..4b098becc1dbce5baeea9970456567afa7f79db7 100644 (file)
--- 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);
index 044dedc73ca2f10f6c6ce8f831f98f87998c1f93..4f7e30197f7fd6944792b0bd3448aa8ebdef2e03 100644 (file)
@@ -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)
index 13672c5d8a815667ef6fb079224785bf64755d84..5702665bb1926ad1f6ebb2aefdcc3f8d81277bc3 100644 (file)
@@ -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;
        }
index c3319365d6c0b36c99a0cbf0d275645729090453..890dee78ba19f04a9abca04c34492bfb3c1945d2 100644 (file)
@@ -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)