diff --git a/config/scontext.cfg b/config/scontext.cfg new file mode 100644 index 00000000..9b490396 --- /dev/null +++ b/config/scontext.cfg @@ -0,0 +1,16 @@ +// define script contexts to allow certain commands +// in (untrusted) map configuration files +// do NOT modify + +scriptcontext 1 alias +scriptcontext 1 loadsky +scriptcontext 1 mapmodelreset +scriptcontext 1 mapmodel +scriptcontext 1 texturereset +scriptcontext 1 texture +scriptcontext 1 fog +scriptcontext 1 fogcolour +scriptcontext 1 mapsound + +// secure this configuration for the rest of the game +sealcontexts diff --git a/source/src/client.cpp b/source/src/client.cpp index 5129d4f6..14498fcc 100644 --- a/source/src/client.cpp +++ b/source/src/client.cpp @@ -431,15 +431,21 @@ void sendmap(char *mapname) } else mapname = getclientmap(); if(securemapcheck(mapname)) return; - int mapsize; + + int mapsize, cfgsize; uchar *mapdata = readmap(path(mapname), &mapsize); + uchar *cfgdata = readmcfg(path(mapname), &cfgsize); if(!mapdata) return; + if(!cfgdata) cfgsize = 0; + ENetPacket *packet = enet_packet_create(NULL, MAXTRANS + mapsize, ENET_PACKET_FLAG_RELIABLE); ucharbuf p(packet->data, packet->dataLength); + putint(p, SV_SENDMAP); sendstring(mapname, p); putint(p, mapsize); - if(65535 - p.length() < mapsize) + putint(p, cfgsize); + if(MAXMAPSENDSIZE - p.length() < mapsize) { conoutf("map %s is too large to send", mapname); delete[] mapdata; @@ -448,6 +454,12 @@ void sendmap(char *mapname) } p.put(mapdata, mapsize); delete[] mapdata; + if(cfgdata) + { + if(MAXMAPSENDSIZE - p.length() >= cfgsize) p.put(cfgdata, cfgsize); + delete[] cfgdata; + } + enet_packet_resize(packet, p.length()); sendpackettoserv(2, packet); conoutf("sending map %s to server...", mapname); diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index 238879f4..0b2a9874 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -638,18 +638,22 @@ void receivefile(uchar *data, int len) getstring(text, p); conoutf("received map \"%s\" from server, reloading..", &text); int mapsize = getint(p); - if(p.remaining() < mapsize) + int cfgsize = getint(p); + int size = mapsize + cfgsize; + if(p.remaining() < size) { p.forceoverread(); break; } if(securemapcheck(text)) { - p.len += mapsize; + p.len += size; break; } writemap(path(text), mapsize, &p.buf[p.len]); p.len += mapsize; + writecfg(path(text), cfgsize, &p.buf[p.len]); + p.len += cfgsize; changemapserv(text, gamemode); break; } diff --git a/source/src/command.cpp b/source/src/command.cpp index 92b0b2bc..1df7de69 100644 --- a/source/src/command.cpp +++ b/source/src/command.cpp @@ -6,6 +6,8 @@ void itoa(char *s, int i) { s_sprintf(s)("%d", i); } char *exchangestr(char *o, char *n) { delete[] o; return newstring(n); } +int execcontext = IEXC_CORE; + hashtable *idents = NULL; // contains ALL vars/commands/aliases bool persistidents = true; @@ -16,7 +18,7 @@ void alias(char *name, char *action) if(!b) { name = newstring(name); - ident b = { ID_ALIAS, name, 0, 0, 0, 0, 0, newstring(action), 0, persistidents }; + ident b = { ID_ALIAS, name, 0, 0, 0, 0, 0, newstring(action), 0, persistidents, execcontext }; idents->access(name, &b); } else if(b->type==ID_ALIAS) @@ -35,7 +37,7 @@ COMMAND(alias, ARG_2STR); int variable(char *name, int min, int cur, int max, int *storage, void (*fun)(), bool persist) { if(!idents) idents = new hashtable; - ident v = { ID_VAR, name, min, max, storage, fun, 0, 0, 0, persist }; + ident v = { ID_VAR, name, min, max, storage, fun, 0, 0, 0, persist, IEXC_CORE }; idents->access(name, &v); return cur; } @@ -53,7 +55,7 @@ char *getalias(char *name) bool addcommand(char *name, void (*fun)(), int narg) { if(!idents) idents = new hashtable; - ident c = { ID_COMMAND, name, 0, 0, 0, fun, narg, 0, 0, false }; + ident c = { ID_COMMAND, name, 0, 0, 0, fun, narg, 0, 0, false, IEXC_CORE }; idents->access(name, &c); return false; } @@ -142,75 +144,84 @@ int execute(char *p) // all evaluation happens here, val = ATOI(c); if(!val && *c!='0') conoutf("unknown command: %s", c); } - else switch(id->type) + else { - case ID_COMMAND: // game defined commands - switch(id->narg) // use very ad-hoc function signature, and just call it - { - case ARG_1INT: ((void (__cdecl *)(int))id->fun)(ATOI(w[1])); break; - case ARG_2INT: ((void (__cdecl *)(int, int))id->fun)(ATOI(w[1]), ATOI(w[2])); break; - case ARG_3INT: ((void (__cdecl *)(int, int, int))id->fun)(ATOI(w[1]), ATOI(w[2]), ATOI(w[3])); break; - case ARG_4INT: ((void (__cdecl *)(int, int, int, int))id->fun)(ATOI(w[1]), ATOI(w[2]), ATOI(w[3]), ATOI(w[4])); break; - case ARG_NONE: ((void (__cdecl *)())id->fun)(); break; - case ARG_1STR: ((void (__cdecl *)(char *))id->fun)(w[1]); break; - case ARG_2STR: ((void (__cdecl *)(char *, char *))id->fun)(w[1], w[2]); break; - case ARG_3STR: ((void (__cdecl *)(char *, char *, char*))id->fun)(w[1], w[2], w[3]); break; - case ARG_4STR: ((void (__cdecl *)(char *, char *, char*, char*))id->fun)(w[1], w[2], w[3], w[4]); break; - case ARG_5STR: ((void (__cdecl *)(char *, char *, char*, char*, char*))id->fun)(w[1], w[2], w[3], w[4], w[5]); break; - case ARG_6STR: ((void (__cdecl *)(char *, char *, char*, char*, char*, char*))id->fun)(w[1], w[2], w[3], w[4], w[5], w[6]); break; - case ARG_7STR: ((void (__cdecl *)(char *, char *, char*, char*, char*, char*, char*))id->fun)(w[1], w[2], w[3], w[4], w[5], w[6], w[7]); break; - case ARG_8STR: ((void (__cdecl *)(char *, char *, char*, char*, char*, char*, char*, char*))id->fun)(w[1], w[2], w[3], w[4], w[5], w[6], w[7], w[8]); break; - case ARG_DOWN: ((void (__cdecl *)(bool))id->fun)(addreleaseaction(id->name)!=NULL); break; - case ARG_1EXP: val = ((int (__cdecl *)(int))id->fun)(execute(w[1])); break; - case ARG_2EXP: val = ((int (__cdecl *)(int, int))id->fun)(execute(w[1]), execute(w[2])); break; - case ARG_1EST: val = ((int (__cdecl *)(char *))id->fun)(w[1]); break; - case ARG_2EST: val = ((int (__cdecl *)(char *, char *))id->fun)(w[1], w[2]); break; - case ARG_VARI: - { - int len = max(numargs-2, 0); - for(int i = 1; i id->context) + { + conoutf("not allowed in this execution context: %s", id->name); + continue; + } + + switch(id->type) + { + case ID_COMMAND: // game defined commands + switch(id->narg) // use very ad-hoc function signature, and just call it + { + case ARG_1INT: ((void (__cdecl *)(int))id->fun)(ATOI(w[1])); break; + case ARG_2INT: ((void (__cdecl *)(int, int))id->fun)(ATOI(w[1]), ATOI(w[2])); break; + case ARG_3INT: ((void (__cdecl *)(int, int, int))id->fun)(ATOI(w[1]), ATOI(w[2]), ATOI(w[3])); break; + case ARG_4INT: ((void (__cdecl *)(int, int, int, int))id->fun)(ATOI(w[1]), ATOI(w[2]), ATOI(w[3]), ATOI(w[4])); break; + case ARG_NONE: ((void (__cdecl *)())id->fun)(); break; + case ARG_1STR: ((void (__cdecl *)(char *))id->fun)(w[1]); break; + case ARG_2STR: ((void (__cdecl *)(char *, char *))id->fun)(w[1], w[2]); break; + case ARG_3STR: ((void (__cdecl *)(char *, char *, char*))id->fun)(w[1], w[2], w[3]); break; + case ARG_4STR: ((void (__cdecl *)(char *, char *, char*, char*))id->fun)(w[1], w[2], w[3], w[4]); break; + case ARG_5STR: ((void (__cdecl *)(char *, char *, char*, char*, char*))id->fun)(w[1], w[2], w[3], w[4], w[5]); break; + case ARG_6STR: ((void (__cdecl *)(char *, char *, char*, char*, char*, char*))id->fun)(w[1], w[2], w[3], w[4], w[5], w[6]); break; + case ARG_7STR: ((void (__cdecl *)(char *, char *, char*, char*, char*, char*, char*))id->fun)(w[1], w[2], w[3], w[4], w[5], w[6], w[7]); break; + case ARG_8STR: ((void (__cdecl *)(char *, char *, char*, char*, char*, char*, char*, char*))id->fun)(w[1], w[2], w[3], w[4], w[5], w[6], w[7], w[8]); break; + case ARG_DOWN: ((void (__cdecl *)(bool))id->fun)(addreleaseaction(id->name)!=NULL); break; + case ARG_1EXP: val = ((int (__cdecl *)(int))id->fun)(execute(w[1])); break; + case ARG_2EXP: val = ((int (__cdecl *)(int, int))id->fun)(execute(w[1]), execute(w[2])); break; + case ARG_1EST: val = ((int (__cdecl *)(char *))id->fun)(w[1]); break; + case ARG_2EST: val = ((int (__cdecl *)(char *, char *))id->fun)(w[1], w[2]); break; + case ARG_VARI: { - strcat(r, w[i]); // make string-list out of all arguments - if(i==numargs-1) break; - strcat(r, " "); + int len = max(numargs-2, 0); + for(int i = 1; ifun)(r); + delete[] r; + break; } - ((void (__cdecl *)(char *))id->fun)(r); - delete[] r; - break; } - } - break; - - case ID_VAR: // game defined variables - if(!w[1][0]) conoutf("%s = %d", c, *id->storage); // var with no value just prints its current value - else if(id->min>id->max) conoutf("variable %s is read-only", id->name); - else - { - int i1 = ATOI(w[1]); - if(i1min || i1>id->max) + break; + + case ID_VAR: // game defined variables + if(!w[1][0]) conoutf("%s = %d", c, *id->storage); // var with no value just prints its current value + else if(id->min>id->max) conoutf("variable %s is read-only", id->name); + else { - i1 = i1min ? id->min : id->max; // clamp to valid range - conoutf("valid range for %s is %d..%d", id->name, id->min, id->max); + int i1 = ATOI(w[1]); + if(i1min || i1>id->max) + { + i1 = i1min ? id->min : id->max; // clamp to valid range + conoutf("valid range for %s is %d..%d", id->name, id->min, id->max); + } + *id->storage = i1; + if(id->fun) ((void (__cdecl *)())id->fun)(); // call trigger function if available } - *id->storage = i1; - if(id->fun) ((void (__cdecl *)())id->fun)(); // call trigger function if available - } - break; - - case ID_ALIAS: // alias, also used as functions and (global) variables - for(int i = 1; iexecuting; - id->executing = id->action; - val = execute(id->action); - if(id->executing!=id->action && id->executing!=wasexecuting) delete[] id->executing; - id->executing = wasexecuting; - break; + break; + + case ID_ALIAS: // alias, also used as functions and (global) variables + for(int i = 1; iexecuting; + id->executing = id->action; + val = execute(id->action); + if(id->executing!=id->action && id->executing!=wasexecuting) delete[] id->executing; + id->executing = wasexecuting; + break; + } } loopj(numargs) delete[] w[j]; } @@ -358,3 +369,37 @@ void identnames(vector &names, bool builtinonly) { enumerateht(*idents) if(!builtinonly || idents->enumc->data.type != ID_ALIAS) names.add(idents->enumc->key); } + +void changescriptcontext(int newcontext) +{ + if(newcontext < execcontext) // clean up aliases created in the old context + { + enumerateht(*idents) + { + ident *id = &idents->enumc->data; + if(id->type == ID_ALIAS && id->context > newcontext) + { + idents->remove(idents->enumc->key); + i--; + } + } + } + execcontext = newcontext; +} + + +bool contextsealed = false; + +void scriptcontext(char *context, char *idname) +{ + if(contextsealed) return; + ident *id = idents->access(idname); + if(!id) return; + int c = atoi(context); + if(c >= 0 && c < IEXC_NUM) id->context = c; +} + +void sealcontexts() { contextsealed = true; } + +COMMAND(scriptcontext, ARG_2STR); +COMMAND(sealcontexts, ARG_NONE); \ No newline at end of file diff --git a/source/src/command.h b/source/src/command.h index de7ad7a4..41a7bdb6 100644 --- a/source/src/command.h +++ b/source/src/command.h @@ -10,6 +10,7 @@ struct ident int narg; // ID_VAR, ID_COMMAND char *action, *executing; // ID_ALIAS bool persist; + int context; }; enum // function signatures for script functions, see command.cpp @@ -23,6 +24,8 @@ enum // function signatures for script functions, see command.cpp ARG_VARI }; +enum { IEXC_CORE = 0, IEXC_FOREIGN, IEXC_NUM }; // script execution context + // nasty macros for registering script functions, abuses globals to avoid excessive infrastructure #define COMMANDN(name, fun, nargs) static bool __dummy_##fun = addcommand(#name, (void (*)())fun, nargs) #define COMMAND(name, nargs) COMMANDN(name, name, nargs) diff --git a/source/src/main.cpp b/source/src/main.cpp index a45a35fc..f84adec5 100644 --- a/source/src/main.cpp +++ b/source/src/main.cpp @@ -385,6 +385,7 @@ int main(int argc, char **argv) docmenu = addmenu("reference", NULL, true, renderdocmenu); persistidents = false; + exec("config/scontext.cfg"); exec("config/keymap.cfg"); exec("config/menus.cfg"); exec("config/prefabs.cfg"); diff --git a/source/src/protocol.h b/source/src/protocol.h index b125e793..a51ea350 100644 --- a/source/src/protocol.h +++ b/source/src/protocol.h @@ -6,6 +6,7 @@ #define PROTOCOL_VERSION 1126 // bump when protocol changes #define DEMO_VERSION 1 // bump when demo format changes #define DEMO_MAGIC "ASSAULTCUBE_DEMO" +#define MAXMAPSENDSIZE 65536 // network messages codes, c2s, c2c, s2c enum diff --git a/source/src/protos.h b/source/src/protos.h index 83c2eca4..5a2c740d 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -35,6 +35,7 @@ extern void alias(char *name, char *action); extern char *getalias(char *name); extern void writecfg(); extern void identnames(vector &names, bool builtinonly); +extern void changescriptcontext(int newcontext); // console extern void keypress(int code, bool isdown, int cooked); @@ -326,8 +327,10 @@ extern void render_particles(int time); // worldio extern void save_world(char *fname); extern void load_world(char *mname); -extern void writemap(char *mname, int msize, uchar *mdata); -extern uchar *readmap(char *mname, int *msize); +extern void writemap(char *name, int size, uchar *data); +extern void writecfg(char *name, int size, uchar *data); +extern uchar *readmap(char *name, int *size); +extern uchar *readmcfg(char *name, int *size); // physics extern float raycube(const vec &o, const vec &ray, vec &surface); diff --git a/source/src/server.cpp b/source/src/server.cpp index cb0cc68f..03bac7c5 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -1512,17 +1512,19 @@ void sendwhois(int sender, int cn) // sending of maps between clients string copyname; -int copysize; +int copysize, copymapsize, copycfgsize; uchar *copydata = NULL; -void sendmapserv(int n, string mapname, int mapsize, uchar *mapdata) +void sendmapserv(int n, string mapname, int mapsize, int cfgsize, uchar *data) { - if(!mapname[0] || mapsize <= 0 || mapsize > 256*256) return; + if(!mapname[0] || mapsize <= 0 || mapsize + cfgsize > MAXMAPSENDSIZE) return; s_strcpy(copyname, mapname); - copysize = mapsize; + copymapsize = mapsize; + copycfgsize = cfgsize; + copysize = mapsize + cfgsize; DELETEA(copydata); - copydata = new uchar[mapsize]; - memcpy(copydata, mapdata, mapsize); + copydata = new uchar[copysize]; + memcpy(copydata, data, copysize); } ENetPacket *getmapserv(int n) @@ -1532,7 +1534,8 @@ ENetPacket *getmapserv(int n) ucharbuf p(packet->data, packet->dataLength); putint(p, SV_RECVMAP); sendstring(copyname, p); - putint(p, copysize); + putint(p, copymapsize); + putint(p, copycfgsize); p.put(copydata, copysize); enet_packet_resize(packet, p.length()); return packet; @@ -1905,13 +1908,14 @@ void process(ENetPacket *packet, int sender, int chan) // sender may be -1 getstring(text, p); filtertext(text, text); int mapsize = getint(p); - if(p.remaining() < mapsize) + int cfgsize = getint(p); + if(p.remaining() < mapsize + cfgsize) { p.forceoverread(); break; } - sendmapserv(sender, text, mapsize, &p.buf[p.len]); - p.len += mapsize; + sendmapserv(sender, text, mapsize, cfgsize, &p.buf[p.len]); + p.len += mapsize + cfgsize; break; } diff --git a/source/src/tools.cpp b/source/src/tools.cpp index 714d1e4f..9de952a4 100644 --- a/source/src/tools.cpp +++ b/source/src/tools.cpp @@ -147,9 +147,9 @@ gzFile opengzfile(const char *filename, const char *mode) return gzopen(found, mode); } -char *loadfile(const char *fn, int *size) +char *loadfile(const char *fn, int *size, const char *mode) { - FILE *f = openfile(fn, "rb"); + FILE *f = openfile(fn, mode ? mode : "rb"); if(!f) return NULL; fseek(f, 0, SEEK_END); int len = ftell(f); diff --git a/source/src/tools.h b/source/src/tools.h index b9aebf64..b25e7fba 100644 --- a/source/src/tools.h +++ b/source/src/tools.h @@ -475,7 +475,7 @@ extern void addpackagedir(const char *dir); extern const char *findfile(const char *filename, const char *mode); extern FILE *openfile(const char *filename, const char *mode); extern gzFile opengzfile(const char *filename, const char *mode); -extern char *loadfile(const char *fn, int *size); +extern char *loadfile(const char *fn, int *size, const char *mode = NULL); extern bool listdir(const char *dir, const char *ext, vector &files); extern int listfiles(const char *dir, const char *ext, vector &files); extern bool cmpb(void *b, int n, enet_uint32 c); diff --git a/source/src/worldio.cpp b/source/src/worldio.cpp index 8b2e8a50..cccbafff 100644 --- a/source/src/worldio.cpp +++ b/source/src/worldio.cpp @@ -104,25 +104,43 @@ void toptimize() // FIXME: only does 2x2, make atleast for 4x4 also // these two are used by getmap/sendmap.. transfers compressed maps directly -void writemap(char *mname, int msize, uchar *mdata) +void writemap(char *name, int msize, uchar *mdata) { - setnames(mname); + setnames(name); backup(cgzname, bakname); FILE *f = openfile(cgzname, "wb"); if(!f) { conoutf("could not write map to %s", cgzname); return; } fwrite(mdata, 1, msize, f); fclose(f); - conoutf("wrote map %s as file %s", mname, cgzname); + conoutf("wrote map %s as file %s", name, cgzname); } -uchar *readmap(char *mname, int *msize) +uchar *readmap(char *name, int *size) { - setnames(mname); - uchar *mdata = (uchar *)loadfile(cgzname, msize); - if(!mdata) { conoutf("could not read map %s", cgzname); return NULL; } - return mdata; + setnames(name); + uchar *data = (uchar *)loadfile(cgzname, size); + if(!data) { conoutf("could not read map %s", cgzname); return NULL; } + return data; } +void writecfg(char *name, int size, uchar *data) +{ + setnames(name); + FILE *f = openfile(mcfname, "w"); + if(!f) { conoutf("could not write config to %s", mcfname); return; } + fwrite(data, 1, size, f); + fclose(f); + conoutf("wrote map config to %s", mcfname); +} + +uchar *readmcfg(char *name, int *size) +{ + setnames(name); + uchar *data = (uchar*)loadfile(mcfname, size, "r"); + return data; +} + + // save map as .cgz file. uses 2 layers of compression: first does simple run-length // encoding and leaves out data for certain kinds of cubes, then zlib removes the // last bits of redundancy. Both passes contribute greatly to the miniscule map sizes. @@ -358,8 +376,10 @@ void load_world(char *mname) // still supports all map formats that have conoutf("%s", hdr.maptitle); startmap(mname); execfile("config/default_map_settings.cfg"); + changescriptcontext(IEXC_FOREIGN); execfile(pcfname); execfile(mcfname); + changescriptcontext(IEXC_CORE); c2skeepalive(); loopi(256) if(texuse[i]) lookuptexture(i); c2skeepalive();