Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions doc/sphinx_source/using/tcl-commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2900,6 +2900,13 @@ language

Module: core

^^^^^^^^^^^^^^
account-extban
^^^^^^^^^^^^^^
Value: a string containing the value of ACCOUNTEXTBAN provided by the 005 connection message. This is the raw value and, depending on the IRC server formatting, could be in multiple formats such as "a", or "a,account".

Module: channel

Binds
-----

Expand Down
3 changes: 2 additions & 1 deletion eggdrop.conf
Original file line number Diff line number Diff line change
Expand Up @@ -1266,7 +1266,8 @@ set stack-limit 4
# Sets the default RPL_ISUPPORT (raw 005) information to use as a fallback.
# These MUST be compatible to all IRCds you might want to connect to.
# The actual server settings overwrite these, so you shouldn't need to modify.
#set isupport-default "CASEMAPPING=rfc1459 CHANNELLEN=80 NICKLEN=9 CHANTYPES=#& PREFIX=(ov)@+ CHANMODES=b,k,l,imnpst MODES=3 MAXCHANNELS=10 TOPICLEN=250 KICKLEN=250 STATUSMSG=@+"
#set isupport-default "CASEMAPPING=rfc1459 CHANNELLEN=80 NICKLEN=9 CHANTYPES=#& PREFIX=(ov)@+ CHANMODES=b,k,l,imnpst MODES=3 MAXCHANNELS=10 TOPICLEN=250 KICKLEN=250 STATUSMSG=@+ EXTBAN=~,ar ACCOUNTEXTBAN=a"


### SERVER MODULE - OTHER NETWORKS (net-type "Other") ###

Expand Down
96 changes: 95 additions & 1 deletion src/mod/channels.mod/channels.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@
#include "src/mod/module.h"

static Function *global = NULL;

static void get_extban_prefix(char *prefix);
static int is_extban_mask(const char *mask);
static char chanfile[121], glob_chanmode[65];
static char *lastdeletedmask;
const char *isupport_get(const char *name, size_t len);

static p_tcl_bind_list H_chanset;

Expand All @@ -55,6 +57,73 @@ static int gfld_chan_thr, gfld_chan_time, gfld_deop_thr, gfld_deop_time,
#include "userchan.c"
#include "udefchan.c"

/* Parse extban mask into type and arg pointers.
* Supports both prefixed (<prefix><type>:<arg>) and non-prefixed (<type>:<arg>) forms.
*/
int extban_parse(const char *mask, char *type, const char **arg)
{
if (!mask || !mask[0])
return 0;

/* Break out no-prefix mask */
if (isalnum((unsigned char) mask[0]) && mask[1] == ':') {
if (type)
*type = mask[0];
if (arg)
*arg = mask + 2;
return 1;
}

/* Break out prefix mask */
if (mask[0] && isalnum((unsigned char) mask[1]) && mask[2] == ':') {
if (type)
*type = mask[1];
if (arg)
*arg = mask + 3;
return 1;
}

return 0;
}

/* Return 1 if mask uses extban syntax. */
static int is_extban_mask(const char *mask)
{
return extban_parse(mask, NULL, NULL);
}

/* Extban prefix from ISUPPORT EXTBAN, if present.
* EXTBAN grammar is [prefix],<types>
*/
static void get_extban_prefix(char *prefix)
{
const char *value, *comma;

/* Clear out old value */
if (prefix) {
*prefix = '\0';
}
value = isupport_get("EXTBAN", strlen("EXTBAN"));
if (!value || !value[0]) {
//TO DO: log issue to partyline
return;
}
comma = strchr(value, ',');
if (comma) {
if (comma == value) {
// No prefix
return;
}
if ((comma - value) == 1) {
if (prefix)
*prefix = value[0];
// Prefix
return;
}
}
// What do we do if no , present?
return;
}

static void *channel_malloc(int size, char *file, int line)
{
Expand Down Expand Up @@ -801,6 +870,23 @@ static char *traced_globchanset(ClientData cdata, Tcl_Interp *irp,
return NULL;
}

static char *traced_account_extban(ClientData cdata, Tcl_Interp *irp,
EGG_CONST char *name1,
EGG_CONST char *name2, int flags)
{
const char *account_extban = isupport_get("ACCOUNTEXTBAN", strlen("ACCOUNTEXTBAN"));

Tcl_SetVar2(interp, name1, name2,
(account_extban && account_extban[0]) ? account_extban : "",
TCL_GLOBAL_ONLY);
if (flags & TCL_TRACE_UNSETS) {
Tcl_TraceVar(interp, "account-extban",
TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
traced_account_extban, NULL);
}
return NULL;
}

static tcl_ints my_tcl_ints[] = {
{"use-info", &use_info, 0},
{"quiet-save", &quiet_save, 0},
Expand Down Expand Up @@ -879,6 +965,10 @@ static char *channels_close()
Tcl_UntraceVar(interp, "default-chanset",
TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
traced_globchanset, NULL);
Tcl_UntraceVar(interp, "account-extban",
TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
traced_account_extban, NULL);
traced_account_extban(NULL, interp, "account-extban", NULL, TCL_TRACE_READS);
rem_help_reference("channels.help");
rem_help_reference("chaninfo.help");
module_undepend(MODULE_NAME);
Expand Down Expand Up @@ -950,6 +1040,7 @@ static Function channels_table[] = {
(Function) & global_exempt_time,
/* 48 - 51 */
(Function) & global_invite_time,
(Function) extban_parse,
};

char *channels_start(Function *global_funcs)
Expand Down Expand Up @@ -1030,6 +1121,9 @@ char *channels_start(Function *global_funcs)
Tcl_TraceVar(interp, "default-chanset",
TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
traced_globchanset, NULL);
Tcl_TraceVar(interp, "account-extban",
TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
traced_account_extban, NULL);
H_chanset = add_bind_table("chanset", HT_STACKABLE, builtin_chanset);
add_builtins(H_chon, my_chon);
add_builtins(H_dcc, C_dcc_irc);
Expand Down
10 changes: 10 additions & 0 deletions src/mod/channels.mod/channels.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ struct udef_struct {
* structures. */
};

/* List of extban flags that Eggdrop can actively enforce.
* Any extban flag not in this list is treated as sticky by default.
*/
static int extban_is_enforceable_flag(char flag, char account_extban_flag)
{
return (flag == 'U' || (account_extban_flag && flag == account_extban_flag));
}


static void del_chanrec(struct userrec *u, char *);
static struct chanuserrec *get_chanrec(struct userrec *u, char *chname);
static struct chanuserrec *add_chanrec(struct userrec *u, char *chname);
Expand Down Expand Up @@ -180,6 +189,7 @@ static int check_tcl_chanset(const char *, const char *, const char *);
#define global_exempt_time (*(int *)(channels_funcs[47]))
/* 48 - 51 */
#define global_invite_time (*(int *)(channels_funcs[48]))
#define extban_parse ((int (*)(const char *, char *, const char **))channels_funcs[49])

#endif /* MAKING_CHANNELS */

Expand Down
124 changes: 97 additions & 27 deletions src/mod/channels.mod/cmdschan.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

static struct flag_record user = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
static struct flag_record victim = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };

int extban_parse(const char *mask, char *type, const char **arg);

/* RFC 1035/2812- hostmasks can't be longer than 63 characters */
static void truncate_mask_hostname(char *s) {
Expand All @@ -41,6 +41,10 @@ static void truncate_mask_hostname(char *s) {
static void cmd_pls_ban(struct userrec *u, int idx, char *par)
{
char *chname, *who, s[UHOSTLEN], s1[UHOSTLEN], *p, *p_expire;
char extbanflag = 0, account_extban_flag = 0;
int extban_enabled = 1;
int extban_default_sticky = 0;
const char *value, *comma, *types, *extbanargs;
long expire_foo;
unsigned long expire_time = 0;
int sticky = 0;
Expand Down Expand Up @@ -121,32 +125,56 @@ static void cmd_pls_ban(struct userrec *u, int idx, char *par)
par[MASKREASON_MAX] = 0;
if (strlen(who) > UHOSTMAX - 4)
who[UHOSTMAX - 4] = 0;
/* Fix missing ! or @ BEFORE checking against myself */
if (!strchr(who, '!')) {
if (!strchr(who, '@'))
egg_snprintf(s, sizeof s, "%s!*@*", who); /* Lame nick ban */
else
egg_snprintf(s, sizeof s, "*!%s", who);
} else if (!strchr(who, '@'))
egg_snprintf(s, sizeof s, "%s@*", who); /* brain-dead? */
else
if (is_extban_mask(who)) {
strlcpy(s, who, sizeof s);
if ((me = module_find("server", 0, 0)) && me->funcs) {
egg_snprintf(s1, sizeof s1, "%s!%s", me->funcs[SERVER_BOTNAME],
me->funcs[SERVER_BOTUSERHOST]);
if (match_addr(s, s1)) {
dprintf(idx, "I'm not going to ban myself.\n");
putlog(LOG_CMDS, "*", "#%s# attempted +ban %s", dcc[idx].nick, s);
return;
/* If its an extban, check if it needs to be set as a sticky ban */
if (extban_parse(s, &extbanflag, &extbanargs)) {
const char *account_extban;

account_extban = isupport_get("ACCOUNTEXTBAN", strlen("ACCOUNTEXTBAN"));
if (account_extban && account_extban[0])
account_extban_flag = account_extban[0];
extban_default_sticky = !extban_is_enforceable_flag(extbanflag, account_extban_flag);
if (extban_default_sticky)
sticky = 1;
value = isupport_get("EXTBAN", strlen("EXTBAN"));
if (value && value[0]) {
comma = strchr(value, ',');
types = comma ? comma + 1 : value;
extban_enabled = strchr(types, extbanflag) ? 1 : 0;
} else {
extban_enabled = 0;
}
}
} else {
/* Fix missing ! or @ BEFORE checking against myself */
if (!strchr(who, '!')) {
if (!strchr(who, '@'))
egg_snprintf(s, sizeof s, "%s!*@*", who); /* Lame nick ban */
else
egg_snprintf(s, sizeof s, "*!%s", who);
} else if (!strchr(who, '@'))
egg_snprintf(s, sizeof s, "%s@*", who); /* brain-dead? */
else
strlcpy(s, who, sizeof s);
if ((me = module_find("server", 0, 0)) && me->funcs) {
egg_snprintf(s1, sizeof s1, "%s!%s", me->funcs[SERVER_BOTNAME],
me->funcs[SERVER_BOTUSERHOST]);
if (match_addr(s, s1)) {
dprintf(idx, "I'm not going to ban myself.\n");
putlog(LOG_CMDS, "*", "#%s# attempted +ban %s", dcc[idx].nick, s);
return;
}
}
truncate_mask_hostname(s);
}
truncate_mask_hostname(s);
if (chan) {
u_addban(chan, s, dcc[idx].nick, par,
expire_time ? now + expire_time : 0, 0);
if (par[0] == '*') {
if (par[0] == '*' || extban_default_sticky) {
sticky = 1;
par++;
if (par[0] == '*')
par++;
putlog(LOG_CMDS, "*", "#%s# (%s) +ban %s %s (%s) (sticky)",
dcc[idx].nick, dcc[idx].u.chat->con_chan, s, chan->dname, par);
dprintf(idx, "New %s sticky ban: %s (%s)\n", chan->dname, s, par);
Expand All @@ -158,14 +186,19 @@ static void cmd_pls_ban(struct userrec *u, int idx, char *par)
/* Avoid unnesessary modes if you got +dynamicbans, and there is
* no reason to set mode if irc.mod aint loaded. (dw 001120)
*/
if ((me = module_find("irc", 0, 0)))
(me->funcs[IRC_CHECK_THIS_BAN]) (chan, s, sticky);
if ((me = module_find("irc", 0, 0))) {
if (!extbanflag || extban_enabled)
(me->funcs[IRC_CHECK_THIS_BAN]) (chan, s, sticky || extban_default_sticky);
else
dprintf(idx, "The %c extban is not enabled on this server. Eggdrop will save this ban but only set it when on a server that has the %c flag enabled.\n", extbanflag, extbanflag);
}
} else {
u_addban(NULL, s, dcc[idx].nick, par,
expire_time ? now + expire_time : 0, 0);
if (par[0] == '*') {
if (par[0] == '*' || extban_default_sticky) {
sticky = 1;
par++;
if (par[0] == '*')
par++;
putlog(LOG_CMDS, "*", "#%s# (GLOBAL) +ban %s (%s) (sticky)",
dcc[idx].nick, s, par);
dprintf(idx, "New sticky ban: %s (%s)\n", s, par);
Expand All @@ -174,13 +207,49 @@ static void cmd_pls_ban(struct userrec *u, int idx, char *par)
s, par);
dprintf(idx, "New ban: %s (%s)\n", s, par);
}
if ((me = module_find("irc", 0, 0)))
for (chan = chanset; chan != NULL; chan = chan->next)
(me->funcs[IRC_CHECK_THIS_BAN]) (chan, s, sticky);
if ((me = module_find("irc", 0, 0))) {
if (!extbanflag || extban_enabled) {
for (chan = chanset; chan != NULL; chan = chan->next)
(me->funcs[IRC_CHECK_THIS_BAN]) (chan, s, sticky || extban_default_sticky);
} else
dprintf(idx, "The %c extban is not enabled on this server. Eggdrop will save this ban but only set it when on a server that has the %c flag enabled.\n", extbanflag, extbanflag);
}
}
}
}


static void cmd_pls_extban(struct userrec *u, int idx, char *par)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to keep this then?
If yes, I would consider adding cmd_mns_extban as well to correspond.

Copy link
Copy Markdown
Member Author

@vanosg vanosg Mar 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.-ban works for all bans, extban or not. I acknowledge it is confusing, but I don't know what this command would add since you're either using a number or a mask, both of which work via .-ban

I'll add that I implemented cmd_pls_extban because it takes needing to know the extban prefix and specific banmask formatting out of the users hands... I thought this is a bonus. .+ban should still work if you want to do it manually as well.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.+ban also works on all bans, no? Maybe you can explain more why .+extban adds value but .-extban would not

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For sure- as a novice user, I might not know that $ and ~ are different on different servers, or what flags are allowed and not allowed on a server. .+extban checks all this; .+ban will just set what you set. In short, there is optionality in how to build a ban, but deleting a ban is still pretty simple and intuitive.

{
char *flagstr, *arg;
char extban[UHOSTLEN], forwarded[LOGLINEMAX];
char flag;
char prefix = '\0';

flagstr = newsplit(&par);
if (!flagstr[0] || flagstr[1]) {
dprintf(idx, "Usage: +extban <flag> <value> [channel] [%%<XyXdXhXm>] [reason]\n");
return;
}

flag = flagstr[0];
arg = newsplit(&par);
if (!arg[0]) {
dprintf(idx, "Usage: +extban <flag> <value> [channel] [%%<XyXdXhXm>] [reason]\n");
return;
}

get_extban_prefix(&prefix);
if (prefix)
egg_snprintf(extban, sizeof extban, "%c%c:%s", prefix, flag, arg);
else
egg_snprintf(extban, sizeof extban, "%c:%s", flag, arg);

egg_snprintf(forwarded, sizeof forwarded, "%s%s%s", extban,
par[0] ? " " : "", par);
cmd_pls_ban(u, idx, forwarded);
}

static void cmd_pls_exempt(struct userrec *u, int idx, char *par)
{
char *chname, *who, s[UHOSTLEN], *p, *p_expire;
Expand Down Expand Up @@ -1624,6 +1693,7 @@ static void cmd_chanload(struct userrec *u, int idx, char *par)
*/
static cmd_t C_dcc_irc[] = {
{"+ban", "ol|ol", (IntFunc) cmd_pls_ban, NULL},
{"+extban", "ol|ol", (IntFunc) cmd_pls_extban, NULL},
{"+exempt", "ol|ol", (IntFunc) cmd_pls_exempt, NULL},
{"+invite", "ol|ol", (IntFunc) cmd_pls_invite, NULL},
{"+chan", "n", (IntFunc) cmd_pls_chan, NULL},
Expand Down
Loading
Loading