diff options
-rw-r--r-- | Changes | 5 | ||||
-rw-r--r-- | include/extern.h | 2 | ||||
-rw-r--r-- | src/channels.c | 305 | ||||
-rw-r--r-- | src/misc.c | 2 | ||||
-rw-r--r-- | src/nickserv.c | 2 | ||||
-rw-r--r-- | version.log | 6 |
6 files changed, 202 insertions, 120 deletions
@@ -10,6 +10,11 @@ Provided by Anope Dev. <dev@anope.org> - 2005 02/13 A Internal Event support, see EVENTS in the doc folder for help [ #00] 02/05 A Support for Unreal 3.2 +I channel mode. [ #00] 02/03 A Merged anope-win32 branch into the main, now Win32 ready. [ #00] +03/28 F Segfault on joining unregistered channels in some cases. [#327] +03/28 F Adding and removing modes are now merged per user while syncing. [#329] +03/28 F Modes not being correctly removed from all users. [#335] +03/28 F Topics not always set on channel creation. [#334] +03/26 F stripModePrefix not checking if it was stripping a mode prefix. [ #00] 03/26 F Memleak in nickIsServices() [ #00] 03/22 F Wrong behaviour of /ns update for channel founders. [#323] 03/21 F Topics being set too often during bursts. [ #00] diff --git a/include/extern.h b/include/extern.h index 6df37b26a..3dff3dea0 100644 --- a/include/extern.h +++ b/include/extern.h @@ -128,7 +128,7 @@ E void do_sjoin(const char *source, int ac, char **av); E void do_topic(const char *source, int ac, char **av); E void do_mass_mode(char *modes); -E void chan_set_correct_modes(User * user, Channel * c); +E void chan_set_correct_modes(User * user, Channel * c, int give_modes); E void restore_unsycned_topics(void); #define whosends(ci) ((!(ci) || !((ci)->botflags & BS_SYMBIOSIS) || !(ci)->bi || !(ci)->c || (ci)->c->usercount < BSMinUsers) ? s_ChanServ : (ci)->bi->nick) diff --git a/src/channels.c b/src/channels.c index 3c4f2683e..ed2e907e6 100644 --- a/src/channels.c +++ b/src/channels.c @@ -161,6 +161,10 @@ void chan_remove_user_status(Channel * chan, User * user, int16 status) { struct u_chanlist *uc; + if (debug >= 2) + alog("debug: removing user status (%d) from %s for %s", status, + user->nick, chan->name); + for (uc = user->chans; uc; uc = uc->next) { if (uc->chan == chan) { uc->status &= ~status; @@ -175,14 +179,15 @@ void chan_set_modes(const char *source, Channel * chan, int ac, char **av, int check) { int add = 1; - int servermode = !!strchr(source, '.'); char *modes = av[0], mode; CBMode *cbm; CMMode *cmm; CUMode *cum; unsigned char botmode = 0; BotInfo *bi; - char *lastnick = NULL; + User *user; + int i, real_ac = ac; + char **real_av = av; if (debug) alog("debug: Changing modes for %s to %s", chan->name, @@ -208,8 +213,6 @@ void chan_set_modes(const char *source, Channel * chan, int ac, char **av, } if ((cum = &cumodes[(int) mode])->status != 0) { - User *user; - if (ac == 0) { alog("channel: mode %c%c with no parameter (?) for channel %s", add ? '+' : '-', mode, chan->name); continue; @@ -247,53 +250,11 @@ void chan_set_modes(const char *source, Channel * chan, int ac, char **av, alog("debug: Setting %c%c on %s for %s", (add ? '+' : '-'), mode, chan->name, user->nick); - if (add) { - /* - if they are in the uline server list we assume they can - have the mode - yes woke up in the middle of the night to - add this.. - TSL - */ - if (is_ulined(user->server->name)) { - chan_set_user_status(chan, user, cum->status); - continue; - } - - /* Fixes bug #68 - - might be a bit ugly but it works, the idea is that since the - is_valid function strips out all of the modes there is no point - in sending it over and over again - */ - if (check) { - if (check == 2 && cum->is_valid) { - if (debug) { - alog("debug: Modes already removed, calling remove_user_status() to clean up"); - } - chan_remove_user_status(chan, user, cum->status); - continue; - } else if (cum->is_valid - && !cum->is_valid(user, chan, servermode)) { - if (debug) { - alog("debug: Modes already sent calling remove_user_status() to clean up"); - } - chan_remove_user_status(chan, user, cum->status); - if (!lastnick) { - check = 2; - lastnick = sstrdup(user->nick); - } else { - if (stricmp(user->nick, lastnick)) { - check = 1; - } - } - continue; - } else { - chan_set_user_status(chan, user, cum->status); - } - } else { - chan_set_user_status(chan, user, cum->status); - } - } else { + if (add) + chan_set_user_status(chan, user, cum->status); + else chan_remove_user_status(chan, user, cum->status); - } + } else if ((cbm = &cbmodes[(int) mode])->flag != 0) { if (add) chan->mode |= cbm->flag; @@ -319,12 +280,28 @@ void chan_set_modes(const char *source, Channel * chan, int ac, char **av, ac--; av++; - add ? cmm->addmask(chan, *av) : cmm->delmask(chan, *av); + if (add) + cmm->addmask(chan, *av); + else + cmm->delmask(chan, *av); } } - if (check) + if (check) { check_modes(chan); + + if (check < 2) { + /* Walk through all users we've set modes for and see if they are + * valid. Invalid modes (like +o with SECUREOPS on) will be removed + */ + real_ac--; + real_av++; + for (i = 0; i < real_ac; i++) { + if ((user = finduser(*av)) && is_on_chan(chan, user)) + chan_set_correct_modes(user, chan, 0); + } + } + } } /*************************************************************************/ @@ -335,6 +312,10 @@ void chan_set_user_status(Channel * chan, User * user, int16 status) { struct u_chanlist *uc; + if (debug >= 2) + alog("debug: setting user status (%d) on %s for %s", status, + user->nick, chan->name); + if (HelpChannel && ircd->supporthelper && (status == CUS_OP || status == (CUS_PROTECT | CUS_OP) || status == (CUS_OWNER | CUS_OP)) @@ -573,8 +554,8 @@ void do_join(const char *source, int ac, char **av) continue; chan = findchan(s); - join_user_update(user, chan, s); - chan_set_correct_modes(user, chan); + chan = join_user_update(user, chan, s); + chan_set_correct_modes(user, chan, 1); } } @@ -739,6 +720,7 @@ void do_sjoin(const char *source, int ac, char **av) char *s, *end, cubuf[7], *end2, *cumodes[6]; int is_sqlined = 0; int ts = 0; + int is_created = 0; serv = findserver(servlist, source); @@ -837,13 +819,13 @@ void do_sjoin(const char *source, int ac, char **av) for (i = 1; i < end2 - cubuf; i++) cumodes[i] = user->nick; chan_set_modes(source, c, 1 + (end2 - cubuf - 1), - cumodes, 1); + cumodes, 2); } if (c->ci && (!serv || is_sync(serv)) && !c->topic_sync) restore_topic(c->name); - chan_set_correct_modes(user, c); + chan_set_correct_modes(user, c, 1); } } @@ -856,7 +838,7 @@ void do_sjoin(const char *source, int ac, char **av) /* Set the timestamp */ c->creation_time = ts; /* We now update the channel mode. */ - chan_set_modes(source, c, ac - 3, &av[2], 1); + chan_set_modes(source, c, ac - 3, &av[2], 2); } /* Unreal just had to be different */ @@ -913,10 +895,10 @@ void do_sjoin(const char *source, int ac, char **av) for (i = 1; i < end2 - cubuf; i++) cumodes[i] = user->nick; chan_set_modes(source, c, 1 + (end2 - cubuf - 1), - cumodes, 1); + cumodes, 2); } - chan_set_correct_modes(user, c); + chan_set_correct_modes(user, c, 1); } } @@ -984,10 +966,10 @@ void do_sjoin(const char *source, int ac, char **av) for (i = 1; i < end2 - cubuf; i++) cumodes[i] = user->nick; chan_set_modes(source, c, 1 + (end2 - cubuf - 1), - cumodes, 1); + cumodes, 2); } - chan_set_correct_modes(user, c); + chan_set_correct_modes(user, c, 1); } } @@ -1018,6 +1000,8 @@ void do_sjoin(const char *source, int ac, char **av) return; c = findchan(av[1]); + if (!c) + is_created = 1; if (ircd->chansqline) { if (!c) is_sqlined = check_chan_sqline(av[1]); @@ -1028,7 +1012,9 @@ void do_sjoin(const char *source, int ac, char **av) } else { c = join_user_update(user, c, av[1]); c->creation_time = ts; - chan_set_correct_modes(user, c); + if (is_created && c->ci) + restore_topic(c->name); + chan_set_correct_modes(user, c, 1); } } } @@ -1229,80 +1215,167 @@ void add_invite(Channel * chan, char *mask) /*************************************************************************/ -/* Set the correct modes for the given user on the given channel. Ignored - * users won't get any modes set this way. -GD - */ - -void chan_set_correct_modes(User * user, Channel * c) +/** + * Set the correct modes, or remove the ones granted without permission, + * for the specified user on ths specified channel. This doesn't give + * modes to ignored users, but does remove them if needed. + * @param user The user to give/remove modes to/from + * @param c The channel to give/remove modes on + * @param give_modes Set to 1 to give modes, 0 to not give modes + * @return void + **/ +void chan_set_correct_modes(User * user, Channel * c, int give_modes) { char *tmp; + char modebuf[BUFSIZE]; + char userbuf[BUFSIZE]; int status; + int add_modes = 0; + int rem_modes = 0; ChannelInfo *ci; - ci = c->ci; + if (!c || !(ci = c->ci)) + return; - if (!ci || (ci->flags & CI_VERBOTEN) || (*(c->name) == '+')) + if ((ci->flags & CI_VERBOTEN) || (*(c->name) == '+')) return; if ((ci->flags & CI_SECURE) && !nick_identified(user)) return; - if (get_ignore(user->nick) != NULL) + status = chan_get_user_status(c, user); + + if (debug >= 2) + alog("debug: Setting correct user modes for %s on %s (current status: %d, %sgiving modes)", user->nick, c->name, status, (give_modes ? "" : "not ")); + + if (give_modes && (get_ignore(user->nick) == NULL)) { + if (ircd->owner + && + (((ci->flags & CI_SECUREFOUNDER) && is_real_founder(user, ci)) + || (!(ci->flags & CI_SECUREFOUNDER) && is_founder(user, ci)))) + add_modes |= (CUS_OWNER | CUS_OP); + else if ((ircd->protect || ircd->admin) + && check_access(user, ci, CA_AUTOPROTECT)) + add_modes |= (CUS_PROTECT | CUS_OP); + else if (check_access(user, ci, CA_AUTOOP)) + add_modes |= CUS_OP; + else if (ircd->halfop && check_access(user, ci, CA_AUTOHALFOP)) + add_modes |= CUS_HALFOP; + else if (check_access(user, ci, CA_AUTOVOICE)) + add_modes |= CUS_VOICE; + } + + /* We check if every mode they have is legally acquired here, and remove + * the modes that they're not allowed to have. But only if SECUREOPS is + * on, because else every mode is legal. -GD + */ + if (ci->flags & CI_SECUREOPS) { + if (ircd->owner && (status & CUS_OWNER) + && + !(((ci->flags & CI_SECUREFOUNDER) && is_real_founder(user, ci)) + || (!(ci->flags & CI_SECUREFOUNDER) + && is_founder(user, ci)))) + rem_modes |= CUS_OWNER; + if ((ircd->protect || ircd->admin) && (status & CUS_PROTECT) + && !check_access(user, ci, CA_AUTOPROTECT)) + rem_modes |= CUS_PROTECT; + if ((status & CUS_OP) && !check_access(user, ci, CA_AUTOOP)) + rem_modes |= CUS_OP; + if (ircd->halfop && (status & CUS_HALFOP) + && !check_access(user, ci, CA_AUTOHALFOP)) + rem_modes |= CUS_HALFOP; + } + + /* No modes to add or remove, exit function -GD */ + if (!add_modes && !rem_modes) return; - status = chan_get_user_status(c, user); - if (ircd->owner - && (((ci->flags & CI_SECUREFOUNDER) && is_real_founder(user, ci)) - || (!(ci->flags & CI_SECUREFOUNDER) - && is_founder(user, ci)))) { - if (!(status & CUS_OWNER)) { + /* No need for strn* functions for modebuf, as every possible string + * will always fit in. -GD + */ + strcpy(modebuf, ""); + strcpy(userbuf, ""); + if (add_modes > 0) { + strcat(modebuf, "+"); + if ((add_modes & CUS_OWNER) && !(status & CUS_OWNER)) { tmp = stripModePrefix(ircd->ownerset); - if (!(status & CUS_OP)) { - anope_cmd_mode(whosends(ci), c->name, "+o%s %s %s", tmp, - user->nick, user->nick); - chan_set_user_status(c, user, CUS_OWNER | CUS_OP); - } else { - anope_cmd_mode(whosends(ci), c->name, "+%s %s", tmp, - user->nick); - chan_set_user_status(c, user, CUS_OWNER); - } - } else if (!(status & CUS_OP)) { - anope_cmd_mode(whosends(ci), c->name, "+o %s", user->nick); - chan_set_user_status(c, user, CUS_OP); + strcat(modebuf, tmp); + free(tmp); + strcat(userbuf, " "); + strcat(userbuf, user->nick); + } else { + add_modes &= ~CUS_OWNER; } - } else if ((ircd->protect || ircd->admin) - && check_access(user, ci, CA_AUTOPROTECT)) { - tmp = stripModePrefix(ircd->adminset); - if (!(status & CUS_PROTECT)) { - if (!(status & CUS_OP)) { - anope_cmd_mode(whosends(ci), c->name, "+o%s %s %s", tmp, - user->nick, user->nick); - chan_set_user_status(c, user, CUS_PROTECT | CUS_OP); - } else { - anope_cmd_mode(whosends(ci), c->name, "+%s %s", tmp, - user->nick); - chan_set_user_status(c, user, CUS_PROTECT); - } - } else if (!(status & CUS_OP)) { - anope_cmd_mode(whosends(ci), c->name, "+o %s", user->nick); - chan_set_user_status(c, user, CUS_OP); + if ((add_modes & CUS_PROTECT) && !(status & CUS_PROTECT)) { + tmp = stripModePrefix(ircd->adminset); + strcat(modebuf, tmp); + free(tmp); + strcat(userbuf, " "); + strcat(userbuf, user->nick); + } else { + add_modes &= ~CUS_PROTECT; + } + if ((add_modes & CUS_OP) && !(status & CUS_OP)) { + strcat(modebuf, "o"); + strcat(userbuf, " "); + strcat(userbuf, user->nick); + } else { + add_modes &= ~CUS_OP; + } + if ((add_modes & CUS_HALFOP) && !(status & CUS_HALFOP)) { + strcat(modebuf, "h"); + strcat(userbuf, " "); + strcat(userbuf, user->nick); + } else { + add_modes &= ~CUS_HALFOP; + } + if ((add_modes & CUS_VOICE) && !(status & CUS_VOICE)) { + strcat(modebuf, "v"); + strcat(userbuf, " "); + strcat(userbuf, user->nick); + } else { + add_modes &= ~CUS_VOICE; } - } else if (check_access(user, ci, CA_AUTOOP)) { - if (!(status & CUS_OP)) { - anope_cmd_mode(whosends(ci), c->name, "+o %s", user->nick); - chan_set_user_status(c, user, CUS_OP); + } + if (rem_modes > 0) { + strcat(modebuf, "-"); + if (rem_modes & CUS_OWNER) { + tmp = stripModePrefix(ircd->ownerset); + strcat(modebuf, tmp); + free(tmp); + strcat(userbuf, " "); + strcat(userbuf, user->nick); } - } else if (ircd->halfop && check_access(user, ci, CA_AUTOHALFOP)) { - if (!(status & CUS_HALFOP)) { - anope_cmd_mode(whosends(ci), c->name, "+h %s", user->nick); - chan_set_user_status(c, user, CUS_HALFOP); + if (rem_modes & CUS_PROTECT) { + tmp = stripModePrefix(ircd->adminset); + strcat(modebuf, tmp); + free(tmp); + strcat(userbuf, " "); + strcat(userbuf, user->nick); } - } else if (check_access(user, ci, CA_AUTOVOICE)) { - if (!(status & CUS_VOICE)) { - anope_cmd_mode(whosends(ci), c->name, "+v %s", user->nick); - chan_set_user_status(c, user, CUS_VOICE); + if (rem_modes & CUS_OP) { + strcat(modebuf, "o"); + strcat(userbuf, " "); + strcat(userbuf, user->nick); + } + if (rem_modes & CUS_HALFOP) { + strcat(modebuf, "h"); + strcat(userbuf, " "); + strcat(userbuf, user->nick); } } + + /* Here, both can be empty again due to the "isn't it set already?" + * checks above. -GD + */ + if (!add_modes && !rem_modes) + return; + + anope_cmd_mode(whosends(ci), c->name, "%s%s", modebuf, userbuf); + if (add_modes > 0) + chan_set_user_status(c, user, add_modes); + if (rem_modes > 0) + chan_remove_user_status(c, user, rem_modes); } /*************************************************************************/ diff --git a/src/misc.c b/src/misc.c index 7afd17e8f..d12351ba0 100644 --- a/src/misc.c +++ b/src/misc.c @@ -1144,7 +1144,7 @@ char *str_signed(unsigned char *str) **/ char *stripModePrefix(const char *str) { - if(str) { + if (str && ((*str == '+') || (*str == '-'))) { return sstrdup(str+1); } return NULL; diff --git a/src/nickserv.c b/src/nickserv.c index db4c23840..54629c9d7 100644 --- a/src/nickserv.c +++ b/src/nickserv.c @@ -2683,7 +2683,7 @@ static int do_setmodes(User * u) /* Walk users current channels */ for (uc = u->chans; uc; uc = uc->next) { if ((c = uc->chan)) - chan_set_correct_modes(u, c); + chan_set_correct_modes(u, c, 1); } return MOD_CONT; } diff --git a/version.log b/version.log index 1920f4405..00e2e54c4 100644 --- a/version.log +++ b/version.log @@ -8,10 +8,14 @@ VERSION_MAJOR="1" VERSION_MINOR="7" VERSION_PATCH="8" -VERSION_BUILD="645" +VERSION_BUILD="646" # $Log$ # +# BUILD : 1.7.8 (646) +# BUGS : 327 329 334 335 +# NOTES : Fixed quite a few bugs with mode handling (not always removed correctly, added/removed modes were split), fixed a segfault in chan_set_correct_modes, fixed some issues with topic setting, made stripModePrefix really stripping the mode prefix instead of the first char +# # BUILD : 1.7.8 (645) # BUGS : none # NOTES : Language file normalization. |