From e0b6a6a71e9a9b50c26873abfc6d0fe50fadddc2 Mon Sep 17 00:00:00 2001 From: Geo Date: Sat, 13 Jan 2024 00:00:29 -0500 Subject: [PATCH 01/17] fix chanset to accept multiple args --- src/mod/channels.mod/channels.c | 28 ++++++++++++++++++++++++++++ src/mod/channels.mod/cmdschan.c | 19 ++++++++++++++----- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/mod/channels.mod/channels.c b/src/mod/channels.mod/channels.c index dc202dc22..08584eb3d 100644 --- a/src/mod/channels.mod/channels.c +++ b/src/mod/channels.mod/channels.c @@ -32,6 +32,8 @@ static Function *global = NULL; static char chanfile[121], glob_chanmode[65]; static char *lastdeletedmask; +static p_tcl_bind_list H_chanset; + static struct udef_struct *udef; static int use_info, chan_hack, quiet_save, global_revenge_mode, @@ -241,6 +243,32 @@ static void get_mode_protect(struct chanset_t *chan, char *s) } } +static int builtin_chanset STDVAR +{ + Function F = (Function) cd; + + BADARGS(4, 4, " chan, setting, type, value"); + + CHECKVALIDITY(builtin_chanset); + F(argv[1], argv[2], argv[3]); + return TCL_OK; +} + +int check_tcl_chanset(const char *chan, const char *setting, const char *type, const char *value) +{ + int x; + + Tcl_SetVar(interp, "_chanset1", (char *) chan, 0); + Tcl_SetVar(interp, "_chanset2", (char *) setting, 0); + Tcl_SetVar(interp, "_chanset3", (char *) type, 0); + Tcl_SetVar(interp, "_chanset4", (char *) value, 0); + + x = check_tcl_bind(H_chanset, chan, 0, " $_chanset1 $_chanset2 $_chanset3 $_chanset4", + BIND_STACKABLE | BIND_WANTRET); + + return 0; +} + /* Returns true if this is one of the channel masks */ static int ismodeline(masklist *m, char *user) diff --git a/src/mod/channels.mod/cmdschan.c b/src/mod/channels.mod/cmdschan.c index a267dfb08..340d1ce7b 100644 --- a/src/mod/channels.mod/cmdschan.c +++ b/src/mod/channels.mod/cmdschan.c @@ -1472,7 +1472,7 @@ static void cmd_chaninfo(struct userrec *u, int idx, char *par) static void cmd_chanset(struct userrec *u, int idx, char *par) { - char *chname = NULL, answers[512], *parcpy; + char *chname = NULL, answers[1024], *parcpy; char *list[2], *bak, *buf; struct chanset_t *chan = NULL; int all = 0; @@ -1555,6 +1555,14 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) return; } list[1] = par; + /* Don't send any follow-on arguments if this is an integer value */ + list[1] = newsplit(&par); +/* + ptr = strchr(list[1], ' '); + if (ptr != NULL) { + *ptr = '\0'; + } +*/ /* Par gets modified in tcl_channel_modify under some * circumstances, so save it now. */ @@ -1563,18 +1571,19 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) irp = Tcl_CreateInterp(); if (tcl_channel_modify(irp, chan, 2, list) == TCL_OK) { int len = strlen(answers); - egg_snprintf(answers + len, (sizeof answers) - len, "%s { %s }", list[0], parcpy); /* Concatenation */ + egg_snprintf(answers + len, (sizeof answers) - len, "%s { %s } ", list[0], list[1]); /* Concatenation */ } else if (!all || !chan->next) dprintf(idx, "Error trying to set %s for %s, %s\n", list[0], all ? "all channels" : chname, Tcl_GetStringResult(irp)); Tcl_ResetResult(irp); Tcl_DeleteInterp(irp); nfree(parcpy); + list[0] = newsplit(&par); + list[1] = '\0'; } - break; } if (!all && answers[0]) { - dprintf(idx, "Successfully set modes { %s } on %s.\n", answers, + dprintf(idx, "Successfully set modes { %s} on %s.\n", answers, chname); putlog(LOG_CMDS, "*", "#%s# chanset %s %s", dcc[idx].nick, chname, answers); @@ -1585,7 +1594,7 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) chan = chan->next; } if (all && answers[0]) { - dprintf(idx, "Successfully set modes { %s } on all channels.\n", + dprintf(idx, "Successfully set modes { %s} on all channels.\n", answers); putlog(LOG_CMDS, "*", "#%s# chanset * %s", dcc[idx].nick, answers); } From c7a4a2f9bc57bd1269abc5a25f188511f324c06f Mon Sep 17 00:00:00 2001 From: Geo Date: Sat, 13 Jan 2024 01:20:44 -0500 Subject: [PATCH 02/17] add chanset bind --- src/mod/channels.mod/channels.c | 16 ++++++++-------- src/mod/channels.mod/channels.h | 2 ++ src/mod/channels.mod/cmdschan.c | 23 +++++++++-------------- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/mod/channels.mod/channels.c b/src/mod/channels.mod/channels.c index 08584eb3d..7594ebf95 100644 --- a/src/mod/channels.mod/channels.c +++ b/src/mod/channels.mod/channels.c @@ -247,26 +247,25 @@ static int builtin_chanset STDVAR { Function F = (Function) cd; - BADARGS(4, 4, " chan, setting, type, value"); + BADARGS(3, 3, " chan, setting, value"); CHECKVALIDITY(builtin_chanset); - F(argv[1], argv[2], argv[3]); + F(argv[1], argv[2]); return TCL_OK; } -int check_tcl_chanset(const char *chan, const char *setting, const char *type, const char *value) +int check_tcl_chanset(const char *chan, const char *setting, const char *value) { int x; Tcl_SetVar(interp, "_chanset1", (char *) chan, 0); Tcl_SetVar(interp, "_chanset2", (char *) setting, 0); - Tcl_SetVar(interp, "_chanset3", (char *) type, 0); - Tcl_SetVar(interp, "_chanset4", (char *) value, 0); + Tcl_SetVar(interp, "_chanset3", (char *) value, 0); - x = check_tcl_bind(H_chanset, chan, 0, " $_chanset1 $_chanset2 $_chanset3 $_chanset4", - BIND_STACKABLE | BIND_WANTRET); + x = check_tcl_bind(H_chanset, setting, 0, " $_chanset1 $_chanset2 $_chanset3", + MATCH_MASK | BIND_STACKABLE); - return 0; + return x; } /* Returns true if this is one of the channel masks @@ -1035,6 +1034,7 @@ char *channels_start(Function *global_funcs) Tcl_TraceVar(interp, "default-chanset", TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS, traced_globchanset, NULL); + H_chanset = add_bind_table("chanset", HT_STACKABLE, builtin_chanset); add_builtins(H_chon, my_chon); add_builtins(H_dcc, C_dcc_irc); add_tcl_commands(channels_cmds); diff --git a/src/mod/channels.mod/channels.h b/src/mod/channels.mod/channels.h index a165e20be..86cab4902 100644 --- a/src/mod/channels.mod/channels.h +++ b/src/mod/channels.mod/channels.h @@ -118,6 +118,8 @@ static void setudef(struct udef_struct *, char *, intptr_t); static void remove_channel(struct chanset_t *); static intptr_t ngetudef(char *, char *); static int expired_mask(struct chanset_t *chan, char *who); +static int check_tcl_chanset(const char *, const char *, const char *); + #else diff --git a/src/mod/channels.mod/cmdschan.c b/src/mod/channels.mod/cmdschan.c index 340d1ce7b..72efc0f6b 100644 --- a/src/mod/channels.mod/cmdschan.c +++ b/src/mod/channels.mod/cmdschan.c @@ -1472,8 +1472,8 @@ static void cmd_chaninfo(struct userrec *u, int idx, char *par) static void cmd_chanset(struct userrec *u, int idx, char *par) { - char *chname = NULL, answers[1024], *parcpy; - char *list[2], *bak, *buf; + char *chname = NULL, answers[1024], *parcpy, *ptr = NULL; + char *list[2], value[2], *bak, *buf; struct chanset_t *chan = NULL; int all = 0; @@ -1534,6 +1534,9 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) return; } if (tcl_channel_modify(0, chan, 1, list) == TCL_OK) { + ptr = list[0]+1; + strlcpy(value, list[0], 2); + check_tcl_chanset(chname, ptr, value); strcat(answers, list[0]); strcat(answers, " "); } else if (!all || !chan->next) @@ -1542,9 +1545,8 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) list[0] = newsplit(&par); continue; } - /* The rest have an unknown amount of args, so assume the rest of the - * line is args. Woops nearly made a nasty little hole here :) we'll - * just ignore any non global +n's trying to set the need-commands. + /* Prior to 1.10 we assumed anything here meant the end of the command. + * Now we process it and continue on until no more args are left */ if (strncmp(list[0], "need-", 5) || (u->flags & USER_OWNER)) { Tcl_Interp *irp = NULL; @@ -1554,15 +1556,7 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) nfree(buf); return; } - list[1] = par; - /* Don't send any follow-on arguments if this is an integer value */ list[1] = newsplit(&par); -/* - ptr = strchr(list[1], ' '); - if (ptr != NULL) { - *ptr = '\0'; - } -*/ /* Par gets modified in tcl_channel_modify under some * circumstances, so save it now. */ @@ -1570,6 +1564,7 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) strcpy(parcpy, par); irp = Tcl_CreateInterp(); if (tcl_channel_modify(irp, chan, 2, list) == TCL_OK) { + check_tcl_chanset(chname, list[0], list[1]); int len = strlen(answers); egg_snprintf(answers + len, (sizeof answers) - len, "%s { %s } ", list[0], list[1]); /* Concatenation */ } else if (!all || !chan->next) @@ -1579,7 +1574,7 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) Tcl_DeleteInterp(irp); nfree(parcpy); list[0] = newsplit(&par); - list[1] = '\0'; + list[1] = 0; } } if (!all && answers[0]) { From 7b5a67a237b385ff789665c735b0e66d94966d0c Mon Sep 17 00:00:00 2001 From: Geo Date: Sat, 13 Jan 2024 01:24:51 -0500 Subject: [PATCH 03/17] add docs --- doc/sphinx_source/using/tcl-commands.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/sphinx_source/using/tcl-commands.rst b/doc/sphinx_source/using/tcl-commands.rst index 9d0f53df1..b67b3cc6e 100644 --- a/doc/sphinx_source/using/tcl-commands.rst +++ b/doc/sphinx_source/using/tcl-commands.rst @@ -3585,6 +3585,14 @@ The following is a list of bind types and how they work. Below each bind type is Description: triggered when a server sends an IRCv3 spec CHGHOST message to change a user's hostmask. The new host is matched against mask in the form of "#channel nick!user\@host" and can contain wildcards. The specified proc will be called with the nick of the user whose hostmask changed; the hostmask the affected user had before the change, the handle of the affected user (or * if no handle is present), the channel the user was on when the bind triggered, and the new hostmask of the affected user. This bind will trigger once for each channel the user is on. +(57) CHANSET + + bind chanset + + procname + + Description: triggered when a channel setting is set via the partyline. flags is ignored, mask is the name of channel setting (not including any +/- prefix) and can contain wildcards. The proc will be called with the channel that the setting was set on, the text name of the setting that was changed, and the value it was set to (a +/-, string, or X:Y formatted value). + ^^^^^^^^^^^^^ Return Values ^^^^^^^^^^^^^ From 2ae6c9e086c49c51e76607de08785a6cd6311aaf Mon Sep 17 00:00:00 2001 From: Geo Date: Sun, 18 Feb 2024 10:14:27 -0500 Subject: [PATCH 04/17] Add bind return value checks --- src/mod/channels.mod/channels.c | 4 ++-- src/mod/channels.mod/cmdschan.c | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/mod/channels.mod/channels.c b/src/mod/channels.mod/channels.c index 7594ebf95..0313be6c0 100644 --- a/src/mod/channels.mod/channels.c +++ b/src/mod/channels.mod/channels.c @@ -262,8 +262,8 @@ int check_tcl_chanset(const char *chan, const char *setting, const char *value) Tcl_SetVar(interp, "_chanset2", (char *) setting, 0); Tcl_SetVar(interp, "_chanset3", (char *) value, 0); - x = check_tcl_bind(H_chanset, setting, 0, " $_chanset1 $_chanset2 $_chanset3", - MATCH_MASK | BIND_STACKABLE); + return BIND_EXEC_LOG == check_tcl_bind(H_chanset, setting, 0, " $_chanset1 $_chanset2 $_chanset3", + MATCH_MASK | BIND_STACKABLE | BIND_STACKRET | BIND_WANTRET); return x; } diff --git a/src/mod/channels.mod/cmdschan.c b/src/mod/channels.mod/cmdschan.c index 72efc0f6b..494d8ca17 100644 --- a/src/mod/channels.mod/cmdschan.c +++ b/src/mod/channels.mod/cmdschan.c @@ -1472,7 +1472,7 @@ static void cmd_chaninfo(struct userrec *u, int idx, char *par) static void cmd_chanset(struct userrec *u, int idx, char *par) { - char *chname = NULL, answers[1024], *parcpy, *ptr = NULL; + char *chname = NULL, answers[1024], *parcpy; char *list[2], value[2], *bak, *buf; struct chanset_t *chan = NULL; int all = 0; @@ -1533,10 +1533,11 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) nfree(buf); return; } + if (check_tcl_chanset(chname, list[0]+1, value)) { + return; + } if (tcl_channel_modify(0, chan, 1, list) == TCL_OK) { - ptr = list[0]+1; strlcpy(value, list[0], 2); - check_tcl_chanset(chname, ptr, value); strcat(answers, list[0]); strcat(answers, " "); } else if (!all || !chan->next) @@ -1563,6 +1564,9 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) parcpy = nmalloc(strlen(par) + 1); strcpy(parcpy, par); irp = Tcl_CreateInterp(); + if (check_tcl_chanset(chname, list[0], list[1])) { + return; + } if (tcl_channel_modify(irp, chan, 2, list) == TCL_OK) { check_tcl_chanset(chname, list[0], list[1]); int len = strlen(answers); From 47c739d4f9b1af91f6ffa6d5f3d03aa07f60b165 Mon Sep 17 00:00:00 2001 From: Geo Date: Sun, 18 Feb 2024 21:54:58 -0500 Subject: [PATCH 05/17] revert processing end chanset args need-* settings can allow multiple strings (such as {putlog op me cuz I'm lame}- the attempted fix did not take this into account and would break things --- src/mod/channels.mod/cmdschan.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/mod/channels.mod/cmdschan.c b/src/mod/channels.mod/cmdschan.c index 494d8ca17..9470bbdb8 100644 --- a/src/mod/channels.mod/cmdschan.c +++ b/src/mod/channels.mod/cmdschan.c @@ -1546,8 +1546,9 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) list[0] = newsplit(&par); continue; } - /* Prior to 1.10 we assumed anything here meant the end of the command. - * Now we process it and continue on until no more args are left + /* The rest have an unknown amount of args, so assume the rest of the + * line is args. Woops nearly made a nasty little hole here :) we'll + * just ignore any non global +n's trying to set the need-commands. */ if (strncmp(list[0], "need-", 5) || (u->flags & USER_OWNER)) { Tcl_Interp *irp = NULL; @@ -1557,7 +1558,7 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) nfree(buf); return; } - list[1] = newsplit(&par); + list[1] = par; /* Par gets modified in tcl_channel_modify under some * circumstances, so save it now. */ @@ -1568,21 +1569,19 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) return; } if (tcl_channel_modify(irp, chan, 2, list) == TCL_OK) { - check_tcl_chanset(chname, list[0], list[1]); int len = strlen(answers); - egg_snprintf(answers + len, (sizeof answers) - len, "%s { %s } ", list[0], list[1]); /* Concatenation */ + egg_snprintf(answers + len, (sizeof answers) - len, "%s { %s }", list[0], parcpy); /* Concatenation */ } else if (!all || !chan->next) dprintf(idx, "Error trying to set %s for %s, %s\n", list[0], all ? "all channels" : chname, Tcl_GetStringResult(irp)); Tcl_ResetResult(irp); Tcl_DeleteInterp(irp); nfree(parcpy); - list[0] = newsplit(&par); - list[1] = 0; } + break; } if (!all && answers[0]) { - dprintf(idx, "Successfully set modes { %s} on %s.\n", answers, + dprintf(idx, "Successfully set modes { %s } on %s.\n", answers, chname); putlog(LOG_CMDS, "*", "#%s# chanset %s %s", dcc[idx].nick, chname, answers); @@ -1593,7 +1592,7 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) chan = chan->next; } if (all && answers[0]) { - dprintf(idx, "Successfully set modes { %s} on all channels.\n", + dprintf(idx, "Successfully set modes { %s } on all channels.\n", answers); putlog(LOG_CMDS, "*", "#%s# chanset * %s", dcc[idx].nick, answers); } From 62ca1bb3676b372dc4c9cb8b8eced02a15e2f9e6 Mon Sep 17 00:00:00 2001 From: Geo Date: Sun, 18 Feb 2024 21:56:04 -0500 Subject: [PATCH 06/17] cleanup --- src/mod/channels.mod/channels.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/mod/channels.mod/channels.c b/src/mod/channels.mod/channels.c index 0313be6c0..252dd5fbe 100644 --- a/src/mod/channels.mod/channels.c +++ b/src/mod/channels.mod/channels.c @@ -247,7 +247,7 @@ static int builtin_chanset STDVAR { Function F = (Function) cd; - BADARGS(3, 3, " chan, setting, value"); + BADARGS(3, 3, " chan setting value"); CHECKVALIDITY(builtin_chanset); F(argv[1], argv[2]); @@ -256,16 +256,12 @@ static int builtin_chanset STDVAR int check_tcl_chanset(const char *chan, const char *setting, const char *value) { - int x; - Tcl_SetVar(interp, "_chanset1", (char *) chan, 0); Tcl_SetVar(interp, "_chanset2", (char *) setting, 0); Tcl_SetVar(interp, "_chanset3", (char *) value, 0); return BIND_EXEC_LOG == check_tcl_bind(H_chanset, setting, 0, " $_chanset1 $_chanset2 $_chanset3", MATCH_MASK | BIND_STACKABLE | BIND_STACKRET | BIND_WANTRET); - - return x; } /* Returns true if this is one of the channel masks From 077de5c4486b280f9dc836040eb7f74d5156fd17 Mon Sep 17 00:00:00 2001 From: Geo Date: Sun, 18 Feb 2024 22:22:17 -0500 Subject: [PATCH 07/17] Handle multiple args against bind --- src/mod/channels.mod/cmdschan.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mod/channels.mod/cmdschan.c b/src/mod/channels.mod/cmdschan.c index 9470bbdb8..986e77b6d 100644 --- a/src/mod/channels.mod/cmdschan.c +++ b/src/mod/channels.mod/cmdschan.c @@ -1534,7 +1534,8 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) return; } if (check_tcl_chanset(chname, list[0]+1, value)) { - return; + list[0] = newsplit(&par); + continue; } if (tcl_channel_modify(0, chan, 1, list) == TCL_OK) { strlcpy(value, list[0], 2); @@ -1566,7 +1567,7 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) strcpy(parcpy, par); irp = Tcl_CreateInterp(); if (check_tcl_chanset(chname, list[0], list[1])) { - return; + continue; } if (tcl_channel_modify(irp, chan, 2, list) == TCL_OK) { int len = strlen(answers); From 524bc510af1020f0c4102b2ce4b7367a5d5316f7 Mon Sep 17 00:00:00 2001 From: Geo Date: Sun, 18 Feb 2024 22:31:05 -0500 Subject: [PATCH 08/17] break don't continue --- src/mod/channels.mod/cmdschan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/channels.mod/cmdschan.c b/src/mod/channels.mod/cmdschan.c index 986e77b6d..7b88122cd 100644 --- a/src/mod/channels.mod/cmdschan.c +++ b/src/mod/channels.mod/cmdschan.c @@ -1567,7 +1567,7 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) strcpy(parcpy, par); irp = Tcl_CreateInterp(); if (check_tcl_chanset(chname, list[0], list[1])) { - continue; + break; } if (tcl_channel_modify(irp, chan, 2, list) == TCL_OK) { int len = strlen(answers); From ad1f66ed405408c77e0ce822e4d1e12faedf112c Mon Sep 17 00:00:00 2001 From: Geo Date: Sun, 18 Feb 2024 23:05:14 -0500 Subject: [PATCH 09/17] cleanup --- src/mod/channels.mod/cmdschan.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/mod/channels.mod/cmdschan.c b/src/mod/channels.mod/cmdschan.c index 7b88122cd..744bb2481 100644 --- a/src/mod/channels.mod/cmdschan.c +++ b/src/mod/channels.mod/cmdschan.c @@ -1475,7 +1475,7 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) char *chname = NULL, answers[1024], *parcpy; char *list[2], value[2], *bak, *buf; struct chanset_t *chan = NULL; - int all = 0; + int len, all = 0; if (!par[0]) dprintf(idx, "Usage: chanset [%schannel] \n", CHANMETA); @@ -1539,8 +1539,9 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) } if (tcl_channel_modify(0, chan, 1, list) == TCL_OK) { strlcpy(value, list[0], 2); - strcat(answers, list[0]); - strcat(answers, " "); + len = strlen(answers); + egg_snprintf(answers + len, (sizeof answers) - len, + (len == 0) ? "%s" : " %s", list[0]); /* Concatenation */ } else if (!all || !chan->next) dprintf(idx, "Error trying to set %s for %s, invalid mode.\n", list[0], all ? "all channels" : chname); @@ -1570,7 +1571,7 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) break; } if (tcl_channel_modify(irp, chan, 2, list) == TCL_OK) { - int len = strlen(answers); + len = strlen(answers); egg_snprintf(answers + len, (sizeof answers) - len, "%s { %s }", list[0], parcpy); /* Concatenation */ } else if (!all || !chan->next) dprintf(idx, "Error trying to set %s for %s, %s\n", From a097d330ef156a418d40e9c2a851cb1233a7ffe2 Mon Sep 17 00:00:00 2001 From: Thomas Sader Date: Sat, 2 Mar 2024 18:49:56 +0100 Subject: [PATCH 10/17] Fix chanset bind for flags --- doc/sphinx_source/using/tcl-commands.rst | 2 +- src/mod/channels.mod/channels.c | 2 +- src/mod/channels.mod/cmdschan.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/sphinx_source/using/tcl-commands.rst b/doc/sphinx_source/using/tcl-commands.rst index b67b3cc6e..1a37f2623 100644 --- a/doc/sphinx_source/using/tcl-commands.rst +++ b/doc/sphinx_source/using/tcl-commands.rst @@ -3591,7 +3591,7 @@ The following is a list of bind types and how they work. Below each bind type is procname - Description: triggered when a channel setting is set via the partyline. flags is ignored, mask is the name of channel setting (not including any +/- prefix) and can contain wildcards. The proc will be called with the channel that the setting was set on, the text name of the setting that was changed, and the value it was set to (a +/-, string, or X:Y formatted value). + Description: triggered when a channel setting is set via the partyline. flags is ignored, mask is the name of channel setting (not including any +/- prefix) and can contain wildcards. The proc will be called with the channel that the setting was set on, the text name of the setting that was changed, and the value it was set to (0/1 for -/+, string, or X:Y formatted value). ^^^^^^^^^^^^^ Return Values diff --git a/src/mod/channels.mod/channels.c b/src/mod/channels.mod/channels.c index 252dd5fbe..885b86539 100644 --- a/src/mod/channels.mod/channels.c +++ b/src/mod/channels.mod/channels.c @@ -250,7 +250,7 @@ static int builtin_chanset STDVAR BADARGS(3, 3, " chan setting value"); CHECKVALIDITY(builtin_chanset); - F(argv[1], argv[2]); + F(argv[1], argv[2], argv[3]); return TCL_OK; } diff --git a/src/mod/channels.mod/cmdschan.c b/src/mod/channels.mod/cmdschan.c index 744bb2481..5020f66b2 100644 --- a/src/mod/channels.mod/cmdschan.c +++ b/src/mod/channels.mod/cmdschan.c @@ -1533,7 +1533,7 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) nfree(buf); return; } - if (check_tcl_chanset(chname, list[0]+1, value)) { + if (check_tcl_chanset(chname, list[0] + 1, list[0] == '+' ? "1" : "0")) { list[0] = newsplit(&par); continue; } From e72810663ac495590ab298de5e2c62eebb3aecc5 Mon Sep 17 00:00:00 2001 From: Geo Date: Sat, 2 Mar 2024 16:32:00 -0500 Subject: [PATCH 11/17] changes... --- src/mod/channels.mod/channels.c | 4 ++++ src/mod/channels.mod/cmdschan.c | 4 ---- src/mod/channels.mod/tclchan.c | 22 +++++++++++++++++++++- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/mod/channels.mod/channels.c b/src/mod/channels.mod/channels.c index 885b86539..07160a83e 100644 --- a/src/mod/channels.mod/channels.c +++ b/src/mod/channels.mod/channels.c @@ -256,9 +256,13 @@ static int builtin_chanset STDVAR int check_tcl_chanset(const char *chan, const char *setting, const char *value) { + setting = strdup(setting); + value = strdup(value); Tcl_SetVar(interp, "_chanset1", (char *) chan, 0); Tcl_SetVar(interp, "_chanset2", (char *) setting, 0); Tcl_SetVar(interp, "_chanset3", (char *) value, 0); + nfree(setting); + nfree(value); return BIND_EXEC_LOG == check_tcl_bind(H_chanset, setting, 0, " $_chanset1 $_chanset2 $_chanset3", MATCH_MASK | BIND_STACKABLE | BIND_STACKRET | BIND_WANTRET); diff --git a/src/mod/channels.mod/cmdschan.c b/src/mod/channels.mod/cmdschan.c index 5020f66b2..5d67dc71f 100644 --- a/src/mod/channels.mod/cmdschan.c +++ b/src/mod/channels.mod/cmdschan.c @@ -1533,10 +1533,6 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) nfree(buf); return; } - if (check_tcl_chanset(chname, list[0] + 1, list[0] == '+' ? "1" : "0")) { - list[0] = newsplit(&par); - continue; - } if (tcl_channel_modify(0, chan, 1, list) == TCL_OK) { strlcpy(value, list[0], 2); len = strlen(answers); diff --git a/src/mod/channels.mod/tclchan.c b/src/mod/channels.mod/tclchan.c index debfb1368..2f65424f1 100644 --- a/src/mod/channels.mod/tclchan.c +++ b/src/mod/channels.mod/tclchan.c @@ -1264,7 +1264,14 @@ static int tcl_channel STDVAR Tcl_AppendResult(irp, "no such channel record", NULL); return TCL_ERROR; } - return tcl_channel_modify(irp, chan, argc - 3, &argv[3]); + for (int i = 3; i < argc; i++) { + argv[i] = strdup(argv[i]); + } + int ret = tcl_channel_modify(irp, chan, argc - 3, &argv[3]); + for (int i = 3; i < argc; i++) { + nfree(argv[i]); + } + return ret; } if (!strcmp(argv[1], "get")) { BADARGS(3, 4, " get channel-name ?setting-name?"); @@ -1318,6 +1325,19 @@ static int tcl_channel_modify(Tcl_Interp *irp, struct chanset_t *chan, module_entry *me; for (i = 0; i < items; i++) { + if (item[i][0] == '+' || item[i][0] == '-') { + if (check_tcl_chanset(chan->dname, item[i]+1, item[i][0] == '+' ? "1" : "0")) { + if (irp) + Tcl_AppendResult(irp, "Channel setting ", item[i], " rejected by Tcl script", NULL); + return TCL_ERROR; + } + } else { + if (check_tcl_chanset(chan->dname, item[i], item[i][0] == '+' ? "1" : "0")) { + if (irp) + Tcl_AppendResult(irp, "Channel setting ", item[i], " rejected by Tcl script", NULL); + return TCL_ERROR; + } + } if (!strcmp(item[i], "need-op")) { i++; if (i >= items) { From 60b5f270afda75d032a2948ce0f72760e302ab87 Mon Sep 17 00:00:00 2001 From: Geo Date: Sat, 2 Mar 2024 18:14:11 -0500 Subject: [PATCH 12/17] Add return 1 function to docs --- doc/sphinx_source/using/tcl-commands.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/sphinx_source/using/tcl-commands.rst b/doc/sphinx_source/using/tcl-commands.rst index 1a37f2623..c284bd871 100644 --- a/doc/sphinx_source/using/tcl-commands.rst +++ b/doc/sphinx_source/using/tcl-commands.rst @@ -3647,6 +3647,8 @@ Here's a list of the bindings that use the return value from procs they trigger: (19) RAWT Return 1 to ask the bot not to process the server text. This can affet the bot's performance by causing it to miss things that it would normally act on -- you have been warned. Again. +(20) CHANSET Return 1 to prevent the channel setting from being changed. + Control Procedures ------------------ From 56fc23244489cb9202d7f2462ee3ac47a80b3ae2 Mon Sep 17 00:00:00 2001 From: Thomas Sader Date: Sun, 3 Mar 2024 14:41:45 +0100 Subject: [PATCH 13/17] Fix memory problems with recursive Tcl stringproc calls --- src/mod/channels.mod/channels.c | 4 ---- src/mod/channels.mod/tclchan.c | 17 +++++++---------- src/tcl.c | 19 ++++++++++++------- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/mod/channels.mod/channels.c b/src/mod/channels.mod/channels.c index 07160a83e..885b86539 100644 --- a/src/mod/channels.mod/channels.c +++ b/src/mod/channels.mod/channels.c @@ -256,13 +256,9 @@ static int builtin_chanset STDVAR int check_tcl_chanset(const char *chan, const char *setting, const char *value) { - setting = strdup(setting); - value = strdup(value); Tcl_SetVar(interp, "_chanset1", (char *) chan, 0); Tcl_SetVar(interp, "_chanset2", (char *) setting, 0); Tcl_SetVar(interp, "_chanset3", (char *) value, 0); - nfree(setting); - nfree(value); return BIND_EXEC_LOG == check_tcl_bind(H_chanset, setting, 0, " $_chanset1 $_chanset2 $_chanset3", MATCH_MASK | BIND_STACKABLE | BIND_STACKRET | BIND_WANTRET); diff --git a/src/mod/channels.mod/tclchan.c b/src/mod/channels.mod/tclchan.c index 2f65424f1..cf08c4e61 100644 --- a/src/mod/channels.mod/tclchan.c +++ b/src/mod/channels.mod/tclchan.c @@ -1264,14 +1264,7 @@ static int tcl_channel STDVAR Tcl_AppendResult(irp, "no such channel record", NULL); return TCL_ERROR; } - for (int i = 3; i < argc; i++) { - argv[i] = strdup(argv[i]); - } - int ret = tcl_channel_modify(irp, chan, argc - 3, &argv[3]); - for (int i = 3; i < argc; i++) { - nfree(argv[i]); - } - return ret; + return tcl_channel_modify(irp, chan, argc - 3, &argv[3]); } if (!strcmp(argv[1], "get")) { BADARGS(3, 4, " get channel-name ?setting-name?"); @@ -1327,14 +1320,18 @@ static int tcl_channel_modify(Tcl_Interp *irp, struct chanset_t *chan, for (i = 0; i < items; i++) { if (item[i][0] == '+' || item[i][0] == '-') { if (check_tcl_chanset(chan->dname, item[i]+1, item[i][0] == '+' ? "1" : "0")) { - if (irp) + if (irp) { + Tcl_ResetResult(irp); Tcl_AppendResult(irp, "Channel setting ", item[i], " rejected by Tcl script", NULL); + } return TCL_ERROR; } } else { if (check_tcl_chanset(chan->dname, item[i], item[i][0] == '+' ? "1" : "0")) { - if (irp) + if (irp) { + Tcl_ResetResult(irp); Tcl_AppendResult(irp, "Channel setting ", item[i], " rejected by Tcl script", NULL); + } return TCL_ERROR; } } diff --git a/src/tcl.c b/src/tcl.c index bbd12fcbc..381148ec3 100644 --- a/src/tcl.c +++ b/src/tcl.c @@ -321,17 +321,22 @@ static void tcl_cleanup_stringinfo(ClientData cd) /* Compatibility wrapper that calls Tcl functions with String API */ static int tcl_call_stringproc_cd(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { - static int max; - static const char **argv; - int i; + const char **argv; + int i, ret; struct tcl_call_stringinfo *info = cd; + /* The string API guarantees argv[argc] == NULL, unlike the obj API */ - if (objc + 1 > max) - argv = nrealloc(argv, (objc + 1) * sizeof *argv); - for (i = 0; i < objc; i++) + argv = nmalloc((objc + 1) * sizeof *argv); + for (i = 0; i < objc; i++) { + Tcl_IncrRefCount(objv[i]); argv[i] = Tcl_GetString(objv[i]); + } argv[objc] = NULL; - return (info->proc)(info->cd, interp, objc, argv); + ret = (info->proc)(info->cd, interp, objc, argv); + for (i = 0; i < objc; i++) { + Tcl_DecrRefCount(objv[i]); + } + return ret; } /* The standard case of no actual cd */ From ba3d89d1c6c7ace7ca0d4080692e053ee2c7b2c7 Mon Sep 17 00:00:00 2001 From: Thomas Sader Date: Sun, 3 Mar 2024 14:43:21 +0100 Subject: [PATCH 14/17] Fix memory leak --- src/tcl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tcl.c b/src/tcl.c index 381148ec3..6fd7802c1 100644 --- a/src/tcl.c +++ b/src/tcl.c @@ -336,6 +336,7 @@ static int tcl_call_stringproc_cd(ClientData cd, Tcl_Interp *interp, int objc, T for (i = 0; i < objc; i++) { Tcl_DecrRefCount(objv[i]); } + nfree(argv); return ret; } From a6a7c8b386c23c66b9706f4ffad2e9a894b5ee72 Mon Sep 17 00:00:00 2001 From: Thomas Sader Date: Sun, 3 Mar 2024 19:01:03 +0100 Subject: [PATCH 15/17] Add comment --- src/tcl.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tcl.c b/src/tcl.c index 6fd7802c1..b7e7fb4d7 100644 --- a/src/tcl.c +++ b/src/tcl.c @@ -319,6 +319,11 @@ static void tcl_cleanup_stringinfo(ClientData cd) } /* Compatibility wrapper that calls Tcl functions with String API */ +/* + * can call itself recursively, so argv is dynamically allocated + * incrrefcount is needed to preserve the strings we get from Tcl_GetString from being cleaned up + * if Tcl is invoked from this + */ static int tcl_call_stringproc_cd(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { const char **argv; From 5fd0b941aadc04c9b243d4987306643ffb702257 Mon Sep 17 00:00:00 2001 From: Thomas Sader Date: Sun, 3 Mar 2024 19:06:08 +0100 Subject: [PATCH 16/17] Fix handling of non-binary settings --- src/mod/channels.mod/tclchan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/channels.mod/tclchan.c b/src/mod/channels.mod/tclchan.c index cf08c4e61..e2b0c56e9 100644 --- a/src/mod/channels.mod/tclchan.c +++ b/src/mod/channels.mod/tclchan.c @@ -1327,7 +1327,7 @@ static int tcl_channel_modify(Tcl_Interp *irp, struct chanset_t *chan, return TCL_ERROR; } } else { - if (check_tcl_chanset(chan->dname, item[i], item[i][0] == '+' ? "1" : "0")) { + if (i < items - 1 && check_tcl_chanset(chan->dname, item[i], item[i + 1])) { if (irp) { Tcl_ResetResult(irp); Tcl_AppendResult(irp, "Channel setting ", item[i], " rejected by Tcl script", NULL); From 49b9c7c98b072577a2899cfd011c81cf67cb3b9c Mon Sep 17 00:00:00 2001 From: Thomas Sader Date: Sun, 3 Mar 2024 19:53:46 +0100 Subject: [PATCH 17/17] Handle need- settings and other settings by splitting up to space --- src/mod/channels.mod/cmdschan.c | 3 --- src/mod/channels.mod/tclchan.c | 37 +++++++++++++++++++++++++++------ 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/mod/channels.mod/cmdschan.c b/src/mod/channels.mod/cmdschan.c index 5d67dc71f..670a1e6d6 100644 --- a/src/mod/channels.mod/cmdschan.c +++ b/src/mod/channels.mod/cmdschan.c @@ -1563,9 +1563,6 @@ static void cmd_chanset(struct userrec *u, int idx, char *par) parcpy = nmalloc(strlen(par) + 1); strcpy(parcpy, par); irp = Tcl_CreateInterp(); - if (check_tcl_chanset(chname, list[0], list[1])) { - break; - } if (tcl_channel_modify(irp, chan, 2, list) == TCL_OK) { len = strlen(answers); egg_snprintf(answers + len, (sizeof answers) - len, "%s { %s }", list[0], parcpy); /* Concatenation */ diff --git a/src/mod/channels.mod/tclchan.c b/src/mod/channels.mod/tclchan.c index e2b0c56e9..af91753cd 100644 --- a/src/mod/channels.mod/tclchan.c +++ b/src/mod/channels.mod/tclchan.c @@ -1319,7 +1319,7 @@ static int tcl_channel_modify(Tcl_Interp *irp, struct chanset_t *chan, for (i = 0; i < items; i++) { if (item[i][0] == '+' || item[i][0] == '-') { - if (check_tcl_chanset(chan->dname, item[i]+1, item[i][0] == '+' ? "1" : "0")) { + if (check_tcl_chanset(chan->dname, item[i] + 1, item[i][0] == '+' ? "1" : "0")) { if (irp) { Tcl_ResetResult(irp); Tcl_AppendResult(irp, "Channel setting ", item[i], " rejected by Tcl script", NULL); @@ -1327,12 +1327,37 @@ static int tcl_channel_modify(Tcl_Interp *irp, struct chanset_t *chan, return TCL_ERROR; } } else { - if (i < items - 1 && check_tcl_chanset(chan->dname, item[i], item[i + 1])) { - if (irp) { - Tcl_ResetResult(irp); - Tcl_AppendResult(irp, "Channel setting ", item[i], " rejected by Tcl script", NULL); + // otherwise invalid later, missing value + if (i < items - 1) { + int free_value = 0; + char *value; + + if (!strncmp("need-", item[i], 5)) { + value = item[i + 1]; + } else { + char *sep = strchr(item[i + 1], ' '); + + if (sep) { + value = nmalloc(sep - item[i + 1] + 1); + strlcpy(value, item[i + 1], sep - item[i + 1] + 1); + free_value = 1; + } else { + value = item[i + 1]; + } + } + if (check_tcl_chanset(chan->dname, item[i], value)) { + if (free_value) { + nfree(value); + } + if (irp) { + Tcl_ResetResult(irp); + Tcl_AppendResult(irp, "Channel setting ", item[i], " to ", value, " rejected by Tcl script", NULL); + } + return TCL_ERROR; + } + if (free_value) { + nfree(value); } - return TCL_ERROR; } } if (!strcmp(item[i], "need-op")) {