From c2f6f79e77b402b5637e269a5b9d5f81172cf992 Mon Sep 17 00:00:00 2001 From: Geo Date: Sun, 22 Feb 2026 16:09:31 -0500 Subject: [PATCH 01/11] First attempt at extban support --- eggdrop.conf | 3 +- src/mod/channels.mod/channels.c | 95 ++++++++++++++++++ src/mod/channels.mod/channels.h | 23 +++++ src/mod/channels.mod/cmdschan.c | 126 +++++++++++++++++++----- src/mod/channels.mod/help/channels.help | 14 ++- src/mod/channels.mod/userchan.c | 62 +++++++++++- src/mod/irc.mod/chan.c | 103 ++++++++++++++++++- src/mod/modvals.h | 1 + src/mod/server.mod/isupport.c | 2 +- 9 files changed, 389 insertions(+), 40 deletions(-) diff --git a/eggdrop.conf b/eggdrop.conf index bed1f7a5c2..49dfe648ae 100755 --- a/eggdrop.conf +++ b/eggdrop.conf @@ -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") ### diff --git a/src/mod/channels.mod/channels.c b/src/mod/channels.mod/channels.c index cb099f6cae..f85a3485fc 100644 --- a/src/mod/channels.mod/channels.c +++ b/src/mod/channels.mod/channels.c @@ -28,6 +28,10 @@ #include "src/mod/module.h" static Function *global = NULL; +static void get_extban_prefix(char *prefix); +static int is_extban_mask(const char *mask); +const char *isupport_get(const char *name, size_t len); + static char chanfile[121], glob_chanmode[65]; static char *lastdeletedmask; @@ -55,6 +59,96 @@ 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 (:) and non-prefixed (:) 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], + */ +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")); + 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 int extban_flag_is_supported(char flag) +{ + const char *value, *comma, *types; + + value = isupport_get("EXTBAN", strlen("EXTBAN")); + if (!value || !value[0]) { + return 0; + } + + comma = strchr(value, ','); + if (comma) { + types = comma + 1; + } + else { + types = value; + } + + for (; *types; types++) { + if (*types == flag) { + return 1; + } + } + return 0; +} +*/ static void *channel_malloc(int size, char *file, int line) { @@ -950,6 +1044,7 @@ static Function channels_table[] = { (Function) & global_exempt_time, /* 48 - 51 */ (Function) & global_invite_time, + (Function) extban_parse }; char *channels_start(Function *global_funcs) diff --git a/src/mod/channels.mod/channels.h b/src/mod/channels.mod/channels.h index 86f19a08bb..f1605e0b44 100644 --- a/src/mod/channels.mod/channels.h +++ b/src/mod/channels.mod/channels.h @@ -65,6 +65,28 @@ struct udef_struct { * structures. */ }; +/* List of extban flags that are non-enforceable by Eggdrop- + * In other words, extbans that need to be set and forgotten about because + * Eggdrop can't do things like block notices or CTCPs, etc + */ +static int extban_sticky_flags(char flag) +{ + switch (flag) { + case 'p': + case 'q': + case 'Q': + case 'A': + case 'B': + case 'c': + case 'N': + case 'T': + return 1; + default: + return 0; + } +} + + 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); @@ -180,6 +202,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 */ diff --git a/src/mod/channels.mod/cmdschan.c b/src/mod/channels.mod/cmdschan.c index 4eb05aa0c1..3c18b06b50 100644 --- a/src/mod/channels.mod/cmdschan.c +++ b/src/mod/channels.mod/cmdschan.c @@ -23,6 +23,8 @@ 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 }; +static 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) { @@ -41,6 +43,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; + int extban_enabled = 1; + int extban_force_sticky = 0; + const char *value, *comma, *types, *extbanargs; long expire_foo; unsigned long expire_time = 0; int sticky = 0; @@ -121,32 +127,51 @@ 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)) { + extban_force_sticky = extban_sticky_flags(extbanflag); + if (extban_force_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_force_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); @@ -158,14 +183,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_force_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_force_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); @@ -174,13 +204,56 @@ 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_force_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) +{ + 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 [channel] [%%] [reason]\n"); + return; + } + + flag = flagstr[0]; + arg = newsplit(&par); + if (!arg[0]) { + dprintf(idx, "Usage: +extban [channel] [%%] [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); + + if (!extban[0]) + egg_snprintf(extban, sizeof extban, "%c:%s", flag, arg); + + if (!forwarded[0] && extban[0]) + strlcpy(forwarded, extban, sizeof forwarded); + + 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; @@ -1624,6 +1697,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}, diff --git a/src/mod/channels.mod/help/channels.help b/src/mod/channels.mod/help/channels.help index 627bb0c487..c966967670 100644 --- a/src/mod/channels.mod/help/channels.help +++ b/src/mod/channels.mod/help/channels.help @@ -8,6 +8,16 @@ has to be expressed in years, days, hours and/or minutes. See also: bans, -ban, stick, unstick +%{help=+extban}%{+lo|lo} +### %b+extban%b [channel] [%%] [comment] + Adds a channel extban using allowed ISUPPORT EXTBAN values. + Extban masks are created as [prefix]: + + Not all extbans (notice blocking, ctcp blocking, etc) can be enforced by + Eggdrop. These non-enforceable extbans are set on-channel and managed as + sticky bans. + +See also: +ban, -ban, bans %{help=+exempt}%{+lo|lo} ### %b+exempt%b [channel] [%%] [comment] Adds an exempt to the list of exempts stored on the bot, with optional @@ -264,7 +274,7 @@ See also: stick, bans, exempts, invites, -ban, -exempt, -invite %binfo%b For channel ops or halfops: - %b+ban -ban bans stick%b + %b+ban +extban -ban bans stick%b %b+exempt -exempt exempts unstick%b %b+invite -invite invites%b @@ -286,7 +296,7 @@ See also: stick, bans, exempts, invites, -ban, -exempt, -invite %binfo%b %{+ol|ol} For channel ops or halfops: - %b+ban -ban bans stick%b + %b+ban +extban -ban bans stick%b %b+exempt -exempt exempts unstick%b %b+invite -invite invites%b %{+m|m} diff --git a/src/mod/channels.mod/userchan.c b/src/mod/channels.mod/userchan.c index f97c005698..3c5ece1d24 100644 --- a/src/mod/channels.mod/userchan.c +++ b/src/mod/channels.mod/userchan.c @@ -220,9 +220,52 @@ static int u_equals_mask(maskrec *u, char *mask) static int u_match_mask(maskrec *rec, char *mask) { - for (; rec; rec = rec->next) + char nick[NICKLEN]; + char *bang; + memberlist *m = NULL; + + if (mask && mask[0]) { + bang = strchr(mask, '!'); + if (bang) { + size_t nicklen = (size_t)(bang - mask); + + if (nicklen >= sizeof nick) { + nicklen = sizeof nick - 1; + } + memcpy(nick, mask, nicklen); + nick[nicklen] = 0; + if (nick[0]) { + m = find_member_from_nick(nick); + } + } + } + + for (; rec; rec = rec->next) { + char type; + const char *arg, *accountflag; + + if (extban_parse(rec->mask, &type, &arg)) { + accountflag = isupport_get("ACCOUNTEXTBAN", strlen("ACCOUNTEXTBAN")); + + /* unknown account state never matches extbans */ + if (!m || !m->account[0]) { + continue; + } + if (accountflag && (type == accountflag[0])) { + if (!rfc_casecmp(m->account, arg)) { + return 1; + } + } else if (type == 'U') { + if (!strcmp(m->account, "*") && match_addr((char *) arg, mask)) { + return 1; + } + } + continue; + } + if (match_addr(rec->mask, mask)) return 1; + } return 0; } @@ -418,14 +461,23 @@ static void fix_broken_mask(char *newmask, const char *oldmask, size_t len) static int u_addban(struct chanset_t *chan, char *ban, char *from, char *note, time_t expire_time, int flags) { - char host[1024], s[1024]; + char host[1024], s[1024], extbantype; + const char *extbanarg; maskrec *p = NULL, *l, **u = chan ? &chan->bans : &global_bans; module_entry *me; - /* Choke check: fix broken bans (must have '!' and '@') */ - fix_broken_mask(host, ban, sizeof host); + if (is_extban_mask(ban)) + strlcpy(host, ban, sizeof host); + else + /* Choke check: fix broken bans (must have '!' and '@') */ + fix_broken_mask(host, ban, sizeof host); + + if (is_extban_mask(host)) { + if (extban_parse(host, &extbantype, &extbanarg) && extban_sticky_flags(extbantype)) + flags |= MASKREC_STICKY; + } - if ((me = module_find("server", 0, 0)) && me->funcs) { + if (!is_extban_mask(host) && (me = module_find("server", 0, 0)) && me->funcs) { simple_sprintf(s, "%s!%s", me->funcs[SERVER_BOTNAME], me->funcs[SERVER_BOTUSERHOST]); if (match_addr(host, s)) { diff --git a/src/mod/irc.mod/chan.c b/src/mod/irc.mod/chan.c index cadce03d13..9cdd244378 100644 --- a/src/mod/irc.mod/chan.c +++ b/src/mod/irc.mod/chan.c @@ -35,6 +35,7 @@ static char last_invchan[CHANNELLEN + 1] = ""; static char botflag005; static int got315(char *from, char *msg); +static void refresh_ban_kick(struct chanset_t *chan, char *user, char *nick); /* ID length for !channels. */ @@ -92,6 +93,81 @@ static void update_idle(char *chname, char *nick) } } +/* Parse a banmask and determine if it is an extban. This duplicates channel.mod for the momemnt b/c lazy. */ +static int extban_parse_local(const char *mask, char *type, const char **arg) +{ + if (!mask || !mask[0]) + return 0; + + if (isalnum((unsigned char) mask[0]) && mask[1] == ':') { + if (type) + *type = mask[0]; + if (arg) + *arg = mask + 2; + return 1; + } + + if (mask[0] && isalnum((unsigned char) mask[1]) && mask[2] == ':') { + if (type) + *type = mask[1]; + if (arg) + *arg = mask + 3; + return 1; + } + + return 0; +} + +static int extban_flag_supported_local(char flag) +{ + module_entry *me; + const char *value, *comma, *types; + + me = module_find("server", 0, 0); + if (me && me->funcs && me->funcs[SERVER_GET_ISUPPORT]) { + value = (const char *)isupport_get("EXTBAN", strlen("EXTBAN")); + } + if (!value || !value[0]) + return 0; + + comma = strchr(value, ','); + types = comma ? comma + 1 : value; + for (; *types; types++) + if (*types == flag) + return 1; + return 0; +} + +/* XXXXXXX Document this little clusterf */ +static int banmask_matches_member(const char *banmask, const char *user, memberlist *m) +{ + module_entry *me; + char type; + const char *v = '\0', *arg = '\0'; + + if (!extban_parse(banmask, &type, &arg)) { + return match_addr((char *) banmask, (char *) user); + } + + if (!m || !m->account[0]) { + return 0; + } + + + me = module_find("server", 0, 0); + if (me && me->funcs && me->funcs[SERVER_GET_ISUPPORT]) { + v = (const char *)isupport_get("ACCOUNTEXTBAN", strlen("ACCOUNTEXTBAN")); + } + if (type == v[0]) { + return !rfc_casecmp(m->account, arg); + } + + if (type == 'U') { + return !strcmp(m->account, "*") && match_addr((char *) arg, (char *) user); + } + return 0; +} + /* set user account on all members on all channels, * trigger account bind if account state was not "unknown" (empty string) */ @@ -103,6 +179,8 @@ static void setaccount(char *nick, char *account) for (chan = chanset; chan; chan = chan->next) { if ((m = ismember(chan, nick))) { if (rfc_casecmp(m->account, account)) { + char user[UHOSTLEN]; + /* account was known */ if (m->account[0]) { if (!strcmp(account, "*")) { @@ -113,6 +191,11 @@ static void setaccount(char *nick, char *account) check_tcl_account(m->nick, m->userhost, get_user_from_member(m), chan->dname, account); } strlcpy(m->account, account, sizeof m->account); + + egg_snprintf(user, sizeof user, "%s!%s", m->nick, m->userhost); + if (u_match_mask(global_bans, user) || u_match_mask(chan->bans, user)) { + refresh_ban_kick(chan, user, m->nick); + } } } } @@ -475,7 +558,7 @@ static void refresh_ban_kick(struct chanset_t *chan, char *user, char *nick) /* Check global bans in first cycle and channel bans in second cycle. */ for (cycle = 0; cycle < 2; cycle++) { for (b = cycle ? chan->bans : global_bans; b; b = b->next) { - if (match_addr(b->mask, user)) { + if (banmask_matches_member(b->mask, user, m)) { struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 }; char c[512]; /* The ban comment. */ get_user_flagrec(get_user_from_member(m), &fr, @@ -574,10 +657,16 @@ static void recheck_bans(struct chanset_t *chan) /* Check global bans in first cycle and channel bans in second cycle. */ for (cycle = 0; cycle < 2; cycle++) { - for (u = cycle ? chan->bans : global_bans; u; u = u->next) + for (u = cycle ? chan->bans : global_bans; u; u = u->next) { + char extflag; + const char *extarg; + + if (extban_parse_local(u->mask, &extflag, &extarg) && !extban_flag_supported_local(extflag)) + continue; if (!isbanned(chan, u->mask) && (!channel_dynamicbans(chan) || (u->flags & MASKREC_STICKY))) add_mode(chan, '+', 'b', u->mask); + } } } @@ -668,14 +757,18 @@ static void resetmasks(struct chanset_t *chan, masklist *m, maskrec *mrec, static void check_this_ban(struct chanset_t *chan, char *banmask, int sticky) { memberlist *m; - char user[NICKMAX+UHOSTLEN+1]; + char user[NICKMAX+UHOSTLEN+1], extflag; + const char *extarg; if (HALFOP_CANTDOMODE('b')) return; + if (extban_parse_local(banmask, &extflag, &extarg) && !extban_flag_supported_local(extflag)) + return; + for (m = chan->channel.member; m && m->nick[0]; m = m->next) { sprintf(user, "%s!%s", m->nick, m->userhost); - if (match_addr(banmask, user) && + if (banmask_matches_member(banmask, user, m) && !(use_exempts && (u_match_mask(global_exempts, user) || u_match_mask(chan->exempts, user)))) @@ -2169,7 +2262,7 @@ static int gotjoin(char *from, char *channame) (!use_exempts || !isexempted(chan, from)) && (me_op(chan) || (me_halfop(chan) && !chan_hasop(m)))) { for (b = chan->channel.ban; b->mask[0]; b = b->next) { - if (match_addr(b->mask, from)) { + if (banmask_matches_member(b->mask, from, m)) { dprintf(DP_SERVER, "KICK %s %s :%s\n", chname, m->nick, IRC_YOUREBANNED); m->flags |= SENTKICK; diff --git a/src/mod/modvals.h b/src/mod/modvals.h index 30314f19ba..c2f9b2a4ae 100644 --- a/src/mod/modvals.h +++ b/src/mod/modvals.h @@ -80,6 +80,7 @@ #define SERVER_BOTNAME 4 #define SERVER_BOTUSERHOST 5 #define SERVER_NICKLEN 37 +#define SERVER_GET_ISUPPORT 47 /* IRC */ #define IRC_RECHECK_CHANNEL 15 #define IRC_RECHECK_CHANNEL_MODES 17 diff --git a/src/mod/server.mod/isupport.c b/src/mod/server.mod/isupport.c index 321178a9bc..84b56ba615 100644 --- a/src/mod/server.mod/isupport.c +++ b/src/mod/server.mod/isupport.c @@ -31,7 +31,7 @@ typedef struct isupport { static isupport_t *isupport_list; static p_tcl_bind_list H_isupport; -static const char isupport_default[4096] = "CASEMAPPING=rfc1459 CHANNELLEN=80 NICKLEN=9 CHANTYPES=#& PREFIX=(ov)@+ CHANMODES=b,k,l,imnpst MODES=3 MAXCHANNELS=10 TOPICLEN=250 KICKLEN=250 STATUSMSG=@+"; +static const char isupport_default[4096] = "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"; static int hexdigit2dec[128] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0 - 9 */ From e8e496848444b746a447bc332685a6dbde81fa2c Mon Sep 17 00:00:00 2001 From: Geo Date: Mon, 23 Feb 2026 14:01:05 -0500 Subject: [PATCH 02/11] crash fixes --- src/mod/channels.mod/channels.c | 3 +++ src/mod/channels.mod/userchan.c | 7 +++++-- src/mod/irc.mod/chan.c | 6 +++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/mod/channels.mod/channels.c b/src/mod/channels.mod/channels.c index f85a3485fc..a81124f88a 100644 --- a/src/mod/channels.mod/channels.c +++ b/src/mod/channels.mod/channels.c @@ -106,6 +106,9 @@ static void get_extban_prefix(char *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) { diff --git a/src/mod/channels.mod/userchan.c b/src/mod/channels.mod/userchan.c index 3c5ece1d24..dd72109d3a 100644 --- a/src/mod/channels.mod/userchan.c +++ b/src/mod/channels.mod/userchan.c @@ -245,8 +245,11 @@ static int u_match_mask(maskrec *rec, char *mask) const char *arg, *accountflag; if (extban_parse(rec->mask, &type, &arg)) { - accountflag = isupport_get("ACCOUNTEXTBAN", strlen("ACCOUNTEXTBAN")); - + me = module_find("server", 0, 0); + if (me && me->funcs && me->funcs[SERVER_GET_ISUPPORT]) { + accountflag = isupport_get("ACCOUNTEXTBAN", strlen("ACCOUNTEXTBAN")); + return 0; + } /* unknown account state never matches extbans */ if (!m || !m->account[0]) { continue; diff --git a/src/mod/irc.mod/chan.c b/src/mod/irc.mod/chan.c index 9cdd244378..4986561e50 100644 --- a/src/mod/irc.mod/chan.c +++ b/src/mod/irc.mod/chan.c @@ -121,7 +121,7 @@ static int extban_parse_local(const char *mask, char *type, const char **arg) static int extban_flag_supported_local(char flag) { module_entry *me; - const char *value, *comma, *types; + const char *value = NULL, *comma, *types; me = module_find("server", 0, 0); if (me && me->funcs && me->funcs[SERVER_GET_ISUPPORT]) { @@ -143,7 +143,7 @@ static int banmask_matches_member(const char *banmask, const char *user, memberl { module_entry *me; char type; - const char *v = '\0', *arg = '\0'; + const char *v = NULL, *arg = NULL; if (!extban_parse(banmask, &type, &arg)) { return match_addr((char *) banmask, (char *) user); @@ -158,7 +158,7 @@ static int banmask_matches_member(const char *banmask, const char *user, memberl if (me && me->funcs && me->funcs[SERVER_GET_ISUPPORT]) { v = (const char *)isupport_get("ACCOUNTEXTBAN", strlen("ACCOUNTEXTBAN")); } - if (type == v[0]) { + if (type && (type == v[0])) { return !rfc_casecmp(m->account, arg); } From 61e2e4b94dd4b723dc36a388bc4ef7770bdfd430 Mon Sep 17 00:00:00 2001 From: Geo Date: Mon, 23 Feb 2026 17:20:11 -0500 Subject: [PATCH 03/11] Compiling is for winners --- src/mod/channels.mod/channels.c | 7 +++---- src/mod/channels.mod/cmdschan.c | 11 +---------- src/mod/channels.mod/userchan.c | 21 +++++++++++---------- src/mod/irc.mod/chan.c | 29 ++--------------------------- 4 files changed, 17 insertions(+), 51 deletions(-) diff --git a/src/mod/channels.mod/channels.c b/src/mod/channels.mod/channels.c index a81124f88a..883413336b 100644 --- a/src/mod/channels.mod/channels.c +++ b/src/mod/channels.mod/channels.c @@ -30,11 +30,9 @@ static Function *global = NULL; static void get_extban_prefix(char *prefix); static int is_extban_mask(const char *mask); -const char *isupport_get(const char *name, size_t len); - - 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; @@ -109,6 +107,7 @@ static void get_extban_prefix(char *prefix) if (!value || !value[0]) { //TO DO: log issue to partyline return; + } comma = strchr(value, ','); if (comma) { if (comma == value) { @@ -1047,7 +1046,7 @@ static Function channels_table[] = { (Function) & global_exempt_time, /* 48 - 51 */ (Function) & global_invite_time, - (Function) extban_parse + (Function) extban_parse, }; char *channels_start(Function *global_funcs) diff --git a/src/mod/channels.mod/cmdschan.c b/src/mod/channels.mod/cmdschan.c index 3c18b06b50..63e83444a8 100644 --- a/src/mod/channels.mod/cmdschan.c +++ b/src/mod/channels.mod/cmdschan.c @@ -22,9 +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 }; - -static int extban_parse(const char *mask, char *type, const char **arg); - +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) { @@ -244,13 +242,6 @@ static void cmd_pls_extban(struct userrec *u, int idx, char *par) egg_snprintf(forwarded, sizeof forwarded, "%s%s%s", extban, par[0] ? " " : "", par); - - if (!extban[0]) - egg_snprintf(extban, sizeof extban, "%c:%s", flag, arg); - - if (!forwarded[0] && extban[0]) - strlcpy(forwarded, extban, sizeof forwarded); - cmd_pls_ban(u, idx, forwarded); } diff --git a/src/mod/channels.mod/userchan.c b/src/mod/channels.mod/userchan.c index dd72109d3a..0818bfb581 100644 --- a/src/mod/channels.mod/userchan.c +++ b/src/mod/channels.mod/userchan.c @@ -220,9 +220,12 @@ static int u_equals_mask(maskrec *u, char *mask) static int u_match_mask(maskrec *rec, char *mask) { - char nick[NICKLEN]; + + char type, nick[NICKLEN]; char *bang; + const char *arg, *accountflag = NULL; memberlist *m = NULL; + module_entry *me; if (mask && mask[0]) { bang = strchr(mask, '!'); @@ -241,9 +244,6 @@ static int u_match_mask(maskrec *rec, char *mask) } for (; rec; rec = rec->next) { - char type; - const char *arg, *accountflag; - if (extban_parse(rec->mask, &type, &arg)) { me = module_find("server", 0, 0); if (me && me->funcs && me->funcs[SERVER_GET_ISUPPORT]) { @@ -464,23 +464,24 @@ static void fix_broken_mask(char *newmask, const char *oldmask, size_t len) static int u_addban(struct chanset_t *chan, char *ban, char *from, char *note, time_t expire_time, int flags) { - char host[1024], s[1024], extbantype; + char host[1024], s[1024], extbantype, isextban; const char *extbanarg; maskrec *p = NULL, *l, **u = chan ? &chan->bans : &global_bans; module_entry *me; - if (is_extban_mask(ban)) + isextban = is_extban_mask(ban); + if (isextban) { strlcpy(host, ban, sizeof host); - else + } else { /* Choke check: fix broken bans (must have '!' and '@') */ fix_broken_mask(host, ban, sizeof host); - - if (is_extban_mask(host)) { + } + if (isextban) { if (extban_parse(host, &extbantype, &extbanarg) && extban_sticky_flags(extbantype)) flags |= MASKREC_STICKY; } - if (!is_extban_mask(host) && (me = module_find("server", 0, 0)) && me->funcs) { + if (!isextban && (me = module_find("server", 0, 0)) && me->funcs) { simple_sprintf(s, "%s!%s", me->funcs[SERVER_BOTNAME], me->funcs[SERVER_BOTUSERHOST]); if (match_addr(host, s)) { diff --git a/src/mod/irc.mod/chan.c b/src/mod/irc.mod/chan.c index 4986561e50..37bc8afd95 100644 --- a/src/mod/irc.mod/chan.c +++ b/src/mod/irc.mod/chan.c @@ -93,31 +93,6 @@ static void update_idle(char *chname, char *nick) } } -/* Parse a banmask and determine if it is an extban. This duplicates channel.mod for the momemnt b/c lazy. */ -static int extban_parse_local(const char *mask, char *type, const char **arg) -{ - if (!mask || !mask[0]) - return 0; - - if (isalnum((unsigned char) mask[0]) && mask[1] == ':') { - if (type) - *type = mask[0]; - if (arg) - *arg = mask + 2; - return 1; - } - - if (mask[0] && isalnum((unsigned char) mask[1]) && mask[2] == ':') { - if (type) - *type = mask[1]; - if (arg) - *arg = mask + 3; - return 1; - } - - return 0; -} - static int extban_flag_supported_local(char flag) { module_entry *me; @@ -661,7 +636,7 @@ static void recheck_bans(struct chanset_t *chan) char extflag; const char *extarg; - if (extban_parse_local(u->mask, &extflag, &extarg) && !extban_flag_supported_local(extflag)) + if (extban_parse(u->mask, &extflag, &extarg) && !extban_flag_supported_local(extflag)) continue; if (!isbanned(chan, u->mask) && (!channel_dynamicbans(chan) || (u->flags & MASKREC_STICKY))) @@ -763,7 +738,7 @@ static void check_this_ban(struct chanset_t *chan, char *banmask, int sticky) if (HALFOP_CANTDOMODE('b')) return; - if (extban_parse_local(banmask, &extflag, &extarg) && !extban_flag_supported_local(extflag)) + if (extban_parse(banmask, &extflag, &extarg) && !extban_flag_supported_local(extflag)) return; for (m = chan->channel.member; m && m->nick[0]; m = m->next) { From 7551ae211dd9871c4d1dd085105c39b6f5badc38 Mon Sep 17 00:00:00 2001 From: Geo Date: Sat, 14 Mar 2026 10:14:31 -0400 Subject: [PATCH 04/11] Swap to sticky bans by default --- src/mod/channels.mod/channels.h | 21 ++++----------------- src/mod/channels.mod/cmdschan.c | 21 +++++++++++++-------- src/mod/channels.mod/userchan.c | 10 +++++++--- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/mod/channels.mod/channels.h b/src/mod/channels.mod/channels.h index f1605e0b44..84d01cbce6 100644 --- a/src/mod/channels.mod/channels.h +++ b/src/mod/channels.mod/channels.h @@ -65,25 +65,12 @@ struct udef_struct { * structures. */ }; -/* List of extban flags that are non-enforceable by Eggdrop- - * In other words, extbans that need to be set and forgotten about because - * Eggdrop can't do things like block notices or CTCPs, etc +/* 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_sticky_flags(char flag) +static int extban_is_enforceable_flag(char flag, char account_extban_flag) { - switch (flag) { - case 'p': - case 'q': - case 'Q': - case 'A': - case 'B': - case 'c': - case 'N': - case 'T': - return 1; - default: - return 0; - } + return (flag == 'U' || (account_extban_flag && flag == account_extban_flag)); } diff --git a/src/mod/channels.mod/cmdschan.c b/src/mod/channels.mod/cmdschan.c index 63e83444a8..1c81c39453 100644 --- a/src/mod/channels.mod/cmdschan.c +++ b/src/mod/channels.mod/cmdschan.c @@ -41,9 +41,9 @@ 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; + char extbanflag = 0, account_extban_flag = 0; int extban_enabled = 1; - int extban_force_sticky = 0; + int extban_default_sticky = 0; const char *value, *comma, *types, *extbanargs; long expire_foo; unsigned long expire_time = 0; @@ -129,8 +129,13 @@ static void cmd_pls_ban(struct userrec *u, int idx, char *par) strlcpy(s, who, sizeof s); /* If its an extban, check if it needs to be set as a sticky ban */ if (extban_parse(s, &extbanflag, &extbanargs)) { - extban_force_sticky = extban_sticky_flags(extbanflag); - if (extban_force_sticky) + 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]) { @@ -166,7 +171,7 @@ static void cmd_pls_ban(struct userrec *u, int idx, char *par) if (chan) { u_addban(chan, s, dcc[idx].nick, par, expire_time ? now + expire_time : 0, 0); - if (par[0] == '*' || extban_force_sticky) { + if (par[0] == '*' || extban_default_sticky) { sticky = 1; if (par[0] == '*') par++; @@ -183,14 +188,14 @@ static void cmd_pls_ban(struct userrec *u, int idx, char *par) */ if ((me = module_find("irc", 0, 0))) { if (!extbanflag || extban_enabled) - (me->funcs[IRC_CHECK_THIS_BAN]) (chan, s, sticky || extban_force_sticky); + (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] == '*' || extban_force_sticky) { + if (par[0] == '*' || extban_default_sticky) { sticky = 1; if (par[0] == '*') par++; @@ -205,7 +210,7 @@ static void cmd_pls_ban(struct userrec *u, int idx, char *par) 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_force_sticky); + (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); } diff --git a/src/mod/channels.mod/userchan.c b/src/mod/channels.mod/userchan.c index 0818bfb581..59b61e2e85 100644 --- a/src/mod/channels.mod/userchan.c +++ b/src/mod/channels.mod/userchan.c @@ -464,8 +464,8 @@ static void fix_broken_mask(char *newmask, const char *oldmask, size_t len) static int u_addban(struct chanset_t *chan, char *ban, char *from, char *note, time_t expire_time, int flags) { - char host[1024], s[1024], extbantype, isextban; - const char *extbanarg; + char host[1024], s[1024], extbantype, account_extban_flag = 0, isextban; + const char *extbanarg, *account_extban; maskrec *p = NULL, *l, **u = chan ? &chan->bans : &global_bans; module_entry *me; @@ -477,7 +477,11 @@ static int u_addban(struct chanset_t *chan, char *ban, char *from, char *note, fix_broken_mask(host, ban, sizeof host); } if (isextban) { - if (extban_parse(host, &extbantype, &extbanarg) && extban_sticky_flags(extbantype)) + account_extban = isupport_get("ACCOUNTEXTBAN", strlen("ACCOUNTEXTBAN")); + if (account_extban && account_extban[0]) + account_extban_flag = account_extban[0]; + if (extban_parse(host, &extbantype, &extbanarg) && + !extban_is_enforceable_flag(extbantype, account_extban_flag)) flags |= MASKREC_STICKY; } From 41f6111f3967d78f7f8fafcac193b17aa8abec4e Mon Sep 17 00:00:00 2001 From: Geo Date: Sat, 14 Mar 2026 10:38:53 -0400 Subject: [PATCH 05/11] Add Tcl variable account-extban --- src/mod/channels.mod/channels.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/mod/channels.mod/channels.c b/src/mod/channels.mod/channels.c index 883413336b..04fc417e8e 100644 --- a/src/mod/channels.mod/channels.c +++ b/src/mod/channels.mod/channels.c @@ -897,6 +897,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}, @@ -975,6 +992,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); @@ -1127,6 +1148,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); From 91d20a848b318a7a66c44d1d39d4ee8481feb57a Mon Sep 17 00:00:00 2001 From: Geo Date: Sat, 14 Mar 2026 15:11:10 -0400 Subject: [PATCH 06/11] add globar var to Tcl doc --- doc/sphinx_source/using/tcl-commands.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/sphinx_source/using/tcl-commands.rst b/doc/sphinx_source/using/tcl-commands.rst index 0fbbecb6df..6e2f3b460a 100644 --- a/doc/sphinx_source/using/tcl-commands.rst +++ b/doc/sphinx_source/using/tcl-commands.rst @@ -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 ----- From a96f4bdd68126166db0d506cb5741077ab3cfc34 Mon Sep 17 00:00:00 2001 From: Geo Date: Sat, 14 Mar 2026 19:31:23 -0400 Subject: [PATCH 07/11] round one of fixes --- src/mod/channels.mod/channels.c | 27 --------------------------- src/mod/channels.mod/userchan.c | 5 ++++- src/mod/irc.mod/chan.c | 2 +- 3 files changed, 5 insertions(+), 29 deletions(-) diff --git a/src/mod/channels.mod/channels.c b/src/mod/channels.mod/channels.c index 04fc417e8e..78db2587d3 100644 --- a/src/mod/channels.mod/channels.c +++ b/src/mod/channels.mod/channels.c @@ -125,33 +125,6 @@ static void get_extban_prefix(char *prefix) return; } -/* -static int extban_flag_is_supported(char flag) -{ - const char *value, *comma, *types; - - value = isupport_get("EXTBAN", strlen("EXTBAN")); - if (!value || !value[0]) { - return 0; - } - - comma = strchr(value, ','); - if (comma) { - types = comma + 1; - } - else { - types = value; - } - - for (; *types; types++) { - if (*types == flag) { - return 1; - } - } - return 0; -} -*/ - static void *channel_malloc(int size, char *file, int line) { char *p; diff --git a/src/mod/channels.mod/userchan.c b/src/mod/channels.mod/userchan.c index 59b61e2e85..65261cf968 100644 --- a/src/mod/channels.mod/userchan.c +++ b/src/mod/channels.mod/userchan.c @@ -243,12 +243,15 @@ static int u_match_mask(maskrec *rec, char *mask) } } +/* Loop through all ban records, see if user matches based on mask or + * flag (extban). + */ for (; rec; rec = rec->next) { + /* Am I an extban? */ if (extban_parse(rec->mask, &type, &arg)) { me = module_find("server", 0, 0); if (me && me->funcs && me->funcs[SERVER_GET_ISUPPORT]) { accountflag = isupport_get("ACCOUNTEXTBAN", strlen("ACCOUNTEXTBAN")); - return 0; } /* unknown account state never matches extbans */ if (!m || !m->account[0]) { diff --git a/src/mod/irc.mod/chan.c b/src/mod/irc.mod/chan.c index 37bc8afd95..4fc794e7ee 100644 --- a/src/mod/irc.mod/chan.c +++ b/src/mod/irc.mod/chan.c @@ -133,7 +133,7 @@ static int banmask_matches_member(const char *banmask, const char *user, memberl if (me && me->funcs && me->funcs[SERVER_GET_ISUPPORT]) { v = (const char *)isupport_get("ACCOUNTEXTBAN", strlen("ACCOUNTEXTBAN")); } - if (type && (type == v[0])) { + if (type && v && type == v[0]) { return !rfc_casecmp(m->account, arg); } From b9d2c564733bbf41e31ed2d050635915aa770e61 Mon Sep 17 00:00:00 2001 From: Geo Date: Sat, 14 Mar 2026 19:43:35 -0400 Subject: [PATCH 08/11] next round of fixes --- src/mod/irc.mod/chan.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/mod/irc.mod/chan.c b/src/mod/irc.mod/chan.c index 4fc794e7ee..e28780fdb0 100644 --- a/src/mod/irc.mod/chan.c +++ b/src/mod/irc.mod/chan.c @@ -93,7 +93,7 @@ static void update_idle(char *chname, char *nick) } } -static int extban_flag_supported_local(char flag) +static int extban_flag_supported(char flag) { module_entry *me; const char *value = NULL, *comma, *types; @@ -113,30 +113,31 @@ static int extban_flag_supported_local(char flag) return 0; } -/* XXXXXXX Document this little clusterf */ +/* Document whether a ban matches a specific channel member + * banmask can be normal or extban, user is the traditional userhost. + * Returns 1 if the ban mask matches the member, 0 if not + */ static int banmask_matches_member(const char *banmask, const char *user, memberlist *m) { module_entry *me; char type; const char *v = NULL, *arg = NULL; + /* Am I an extban? */ if (!extban_parse(banmask, &type, &arg)) { return match_addr((char *) banmask, (char *) user); } - if (!m || !m->account[0]) { - return 0; - } - - me = module_find("server", 0, 0); if (me && me->funcs && me->funcs[SERVER_GET_ISUPPORT]) { v = (const char *)isupport_get("ACCOUNTEXTBAN", strlen("ACCOUNTEXTBAN")); } + /* Try account extban matching */ if (type && v && type == v[0]) { return !rfc_casecmp(m->account, arg); } + /* Try U (unregistered) extban matching */ if (type == 'U') { return !strcmp(m->account, "*") && match_addr((char *) arg, (char *) user); } @@ -636,7 +637,7 @@ static void recheck_bans(struct chanset_t *chan) char extflag; const char *extarg; - if (extban_parse(u->mask, &extflag, &extarg) && !extban_flag_supported_local(extflag)) + if (extban_parse(u->mask, &extflag, &extarg) && !extban_flag_supported(extflag)) continue; if (!isbanned(chan, u->mask) && (!channel_dynamicbans(chan) || (u->flags & MASKREC_STICKY))) @@ -738,7 +739,7 @@ static void check_this_ban(struct chanset_t *chan, char *banmask, int sticky) if (HALFOP_CANTDOMODE('b')) return; - if (extban_parse(banmask, &extflag, &extarg) && !extban_flag_supported_local(extflag)) + if (extban_parse(banmask, &extflag, &extarg) && !extban_flag_supported(extflag)) return; for (m = chan->channel.member; m && m->nick[0]; m = m->next) { From cc3371398d120a204d3166a15b8b75af3267837c Mon Sep 17 00:00:00 2001 From: Geo Date: Sat, 14 Mar 2026 19:56:32 -0400 Subject: [PATCH 09/11] check dynamic ban status before setting ban --- src/mod/irc.mod/chan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mod/irc.mod/chan.c b/src/mod/irc.mod/chan.c index e28780fdb0..00d324b627 100644 --- a/src/mod/irc.mod/chan.c +++ b/src/mod/irc.mod/chan.c @@ -170,6 +170,7 @@ static void setaccount(char *nick, char *account) egg_snprintf(user, sizeof user, "%s!%s", m->nick, m->userhost); if (u_match_mask(global_bans, user) || u_match_mask(chan->bans, user)) { + check_this_ban(chan, user, m->nick); refresh_ban_kick(chan, user, m->nick); } } From 014349b177fb368e8a838d260173e86dba5c53e0 Mon Sep 17 00:00:00 2001 From: Geo Date: Sat, 14 Mar 2026 20:08:21 -0400 Subject: [PATCH 10/11] remove old code --- src/mod/channels.mod/userchan.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/mod/channels.mod/userchan.c b/src/mod/channels.mod/userchan.c index 65261cf968..84b385837e 100644 --- a/src/mod/channels.mod/userchan.c +++ b/src/mod/channels.mod/userchan.c @@ -243,9 +243,9 @@ static int u_match_mask(maskrec *rec, char *mask) } } -/* Loop through all ban records, see if user matches based on mask or - * flag (extban). - */ + /* Loop through all ban records, see if user matches based on mask or + * flag (extban). + */ for (; rec; rec = rec->next) { /* Am I an extban? */ if (extban_parse(rec->mask, &type, &arg)) { @@ -253,10 +253,7 @@ static int u_match_mask(maskrec *rec, char *mask) if (me && me->funcs && me->funcs[SERVER_GET_ISUPPORT]) { accountflag = isupport_get("ACCOUNTEXTBAN", strlen("ACCOUNTEXTBAN")); } - /* unknown account state never matches extbans */ - if (!m || !m->account[0]) { - continue; - } + if (accountflag && (type == accountflag[0])) { if (!rfc_casecmp(m->account, arg)) { return 1; From 4c8d3246f2359c9494c5bee60d0891c866fe430f Mon Sep 17 00:00:00 2001 From: Geo Date: Sat, 14 Mar 2026 20:14:10 -0400 Subject: [PATCH 11/11] fix copypasta --- src/mod/irc.mod/chan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mod/irc.mod/chan.c b/src/mod/irc.mod/chan.c index 00d324b627..b0085ff03f 100644 --- a/src/mod/irc.mod/chan.c +++ b/src/mod/irc.mod/chan.c @@ -36,6 +36,7 @@ static char botflag005; static int got315(char *from, char *msg); static void refresh_ban_kick(struct chanset_t *chan, char *user, char *nick); +static void check_this_ban(struct chanset_t *chan, char *banmask, int sticky); /* ID length for !channels. */ @@ -170,7 +171,7 @@ static void setaccount(char *nick, char *account) egg_snprintf(user, sizeof user, "%s!%s", m->nick, m->userhost); if (u_match_mask(global_bans, user) || u_match_mask(chan->bans, user)) { - check_this_ban(chan, user, m->nick); + check_this_ban(chan, user, 0); refresh_ban_kick(chan, user, m->nick); } }