summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Changes5
-rw-r--r--include/extern.h2
-rw-r--r--src/channels.c305
-rw-r--r--src/misc.c2
-rw-r--r--src/nickserv.c2
-rw-r--r--version.log6
6 files changed, 202 insertions, 120 deletions
diff --git a/Changes b/Changes
index ccf587690..4087a4d95 100644
--- a/Changes
+++ b/Changes
@@ -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.