From 711787b54d8b57ace877b09587f368cee5553b70 Mon Sep 17 00:00:00 2001 From: Adam- Date: Thu, 14 Jan 2010 21:29:08 +0000 Subject: Burned do_sjoin and rewrote it to be sane. This changes how Anope handles new channel creations drasitcally as we now truely track it all instead of hack around it by not initially tracking user joins to new channels git-svn-id: http://anope.svn.sourceforge.net/svnroot/anope/trunk@2755 5417fbe8-f217-4b02-8779-1006273d7864 --- src/protocol/bahamut.c | 166 ++++++++++++++++++++++++++++++++++++++-- src/protocol/inspircd11.c | 157 ++++++++++++++++++++++++++------------ src/protocol/inspircd12.cpp | 181 +++++++++++++++++++++++++++----------------- src/protocol/ratbox.c | 130 +++++++++++++++++++++++++++++-- src/protocol/unreal32.c | 158 ++++++++++++++++++++++++++++++++++++-- 5 files changed, 655 insertions(+), 137 deletions(-) (limited to 'src/protocol') diff --git a/src/protocol/bahamut.c b/src/protocol/bahamut.c index bd5a323dc..ac0647d41 100644 --- a/src/protocol/bahamut.c +++ b/src/protocol/bahamut.c @@ -48,9 +48,6 @@ IRCDVar myIrcd[] = { NULL, /* CAPAB Chan Modes */ 0, /* We support TOKENS */ 0, /* TIME STAMPS are BASE64 */ - 0, /* SJOIN ban char */ - 0, /* SJOIN except char */ - 0, /* SJOIN invite char */ 0, /* Can remove User Channel Modes with SVSMODE */ 0, /* Sglines are not enforced until user reconnects */ 0, /* ts6 */ @@ -350,7 +347,163 @@ class BahamutIRCdProto : public IRCDProto /* EVENT: SJOIN */ int anope_event_sjoin(const char *source, int ac, const char **av) { - do_sjoin(source, ac, av); + Channel *c = findchan(av[1]); + time_t ts = atol(av[0]); + bool was_created = false; + bool keep_their_modes = false; + + if (!c) + { + c = new Channel(av[1], ts); + was_created = true; + } + /* Our creation time is newer than what the server gave us */ + else if (c->creation_time > ts) + { + c->creation_time = ts; + + /* Remove status from all of our users */ + for (struct c_userlist *cu = c->users; cu; cu = cu->next) + { + c->RemoveMode(NULL, CMODE_OWNER, cu->user->nick); + c->RemoveMode(NULL, CMODE_PROTECT, cu->user->nick); + c->RemoveMode(NULL, CMODE_OP, cu->user->nick); + c->RemoveMode(NULL, CMODE_HALFOP, cu->user->nick); + c->RemoveMode(NULL, CMODE_VOICE, cu->user->nick); + } + if (c->ci) + { + /* Rejoin the bot to fix the TS */ + if (c->ci->bi) + { + ircdproto->SendPart(c->ci->bi, c, "TS reop"); + bot_join(c->ci); + } + /* Reset mlock */ + check_modes(c); + } + } + /* Their TS is newer than ours, our modes > theirs, unset their modes if need be */ + else + keep_their_modes = false; + + /* Mark the channel as syncing */ + if (was_created) + c->SetFlag(CH_SYNCING); + + /* If we need to keep their modes, and this SJOIN string contains modes */ + if (keep_their_modes && ac >= 4) + { + /* Set the modes internally */ + ChanSetInternalModes(c, ac - 3, av + 2); + } + + /* For a reason unknown to me, bahamut will send a SJOIN from the user joining a channel + * if the channel already existed + */ + if (!was_created && ac == 2) + { + User *u = finduser(source); + if (!u) + { + if (debug) + alog("debug: SJOIN for nonexistant user %s on %s", source, c->name.c_str()); + } + else + { + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnPreJoinChannel, OnPreJoinChannel(u, c)); + + /* Add the user to the channel */ + c->JoinUser(u); + + /* Now set whatever modes this user is allowed to have on the channel */ + chan_set_correct_modes(u, c, 1); + + /* Check to see if modules want the user to join, if they do + * check to see if they are allowed to join (CheckKick will kick/ban them) + * Don't trigger OnJoinChannel event then as the user will be destroyed + */ + if (MOD_RESULT == EVENT_STOP && (!c->ci || !c->ci->CheckKick(u))) + { + FOREACH_MOD(I_OnJoinChannel, OnJoinChannel(u, c)); + } + } + } + else + { + spacesepstream sep(av[ac - 1]); + std::string buf; + while (sep.GetToken(buf)) + { + std::list Status; + Status.clear(); + char ch; + + /* Get prefixes from the nick */ + while ((ch = ModeManager::GetStatusChar(buf[0]))) + { + buf.erase(buf.begin()); + ChannelMode *cm = ModeManager::FindChannelModeByChar(ch); + if (!cm) + { + alog("Recieved unknown mode prefix %c in SJOIN string", buf[0]); + continue; + } + + Status.push_back(cm); + } + + User *u = finduser(buf); + if (!u) + { + if (debug) + alog("debug: SJOIN for nonexistant user %s on %s", buf.c_str(), c->name.c_str()); + continue; + } + + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnPreJoinChannel, OnPreJoinChannel(u, c)); + + /* Add the user to the channel */ + c->JoinUser(u); + + /* Update their status internally on the channel + * This will enforce secureops etc on the user + */ + for (std::list::iterator it = Status.begin(); it != Status.end(); ++it) + { + c->SetModeInternal(*it, buf); + } + + /* Now set whatever modes this user is allowed to have on the channel */ + chan_set_correct_modes(u, c, 1); + + /* Check to see if modules want the user to join, if they do + * check to see if they are allowed to join (CheckKick will kick/ban them) + * Don't trigger OnJoinChannel event then as the user will be destroyed + */ + if (MOD_RESULT != EVENT_STOP && c->ci && c->ci->CheckKick(u)) + continue; + + FOREACH_MOD(I_OnJoinChannel, OnJoinChannel(u, c)); + } + } + + /* Channel is done syncing */ + if (was_created) + { + /* Unset the syncing flag */ + c->UnsetFlag(CH_SYNCING); + + /* If there are users in the channel they are allowed to be, set topic mlock etc. */ + if (c->usercount) + c->Sync(); + /* If there are no users in the channel, there is a ChanServ timer set to part the service bot + * and destroy the channel soon + */ + } + return MOD_CONT; } @@ -614,11 +767,11 @@ int anope_event_burst(const char *source, int ac, const char **av) return MOD_CONT; } -bool ChannelModeFlood::IsValid(const char *value) +bool ChannelModeFlood::IsValid(const std::string &value) { char *dp, *end; - if (value && *value != ':' && strtoul((*value == '*' ? value + 1 : value), &dp, 10) > 0 && *dp == ':' && *(++dp) && strtoul(dp, &end, 10) > 0 && !*end) + if (!value.empty() && value[0] != ':' && strtoul((value[0] == '*' ? value.c_str() + 1 : value.c_str()), &dp, 10) > 0 && *dp == ':' && *(++dp) && strtoul(dp, &end, 10) > 0 && !*end) return true; return false; @@ -707,6 +860,7 @@ class ProtoBahamut : public Module pmodule_ircd_var(myIrcd); pmodule_ircd_useTSMode(0); + moduleAddIRCDMsgs(); moduleAddModes(); pmodule_ircd_proto(&ircd_proto); diff --git a/src/protocol/inspircd11.c b/src/protocol/inspircd11.c index 55f0858e3..91b400e9b 100644 --- a/src/protocol/inspircd11.c +++ b/src/protocol/inspircd11.c @@ -65,9 +65,6 @@ IRCDVar myIrcd[] = { NULL, /* CAPAB Chan Modes */ 0, /* We support inspircd TOKENS */ 0, /* TIME STAMPS are BASE64 */ - 0, /* SJOIN ban char */ - 0, /* SJOIN except char */ - 0, /* SJOIN invite char */ 0, /* Can remove User Channel Modes with SVSMODE */ 0, /* Sglines are not enforced until user reconnects */ 0, /* ts6 */ @@ -460,59 +457,121 @@ int anope_event_fmode(const char *source, int ac, const char **av) int anope_event_fjoin(const char *source, int ac, const char **av) { - const char *newav[10]; - - /* storing the current nick */ - char *curnick; - - /* these are used to generate the final string that is passed to ircservices' core */ - int nlen = 0; - char nicklist[514]; + Channel *c = findchan(av[0]); + time_t ts = atol(av[1]); + bool was_created = false; + bool keep_their_modes = true; - /* temporary buffer */ - char prefixandnick[60]; + if (!c) + { + c = new Channel(av[0], ts); + was_created = true; + } + /* Our creation time is newer than what the server gave us */ + else if (c->creation_time > ts) + { + c->creation_time = ts; - *nicklist = '\0'; - *prefixandnick = '\0'; + /* Remove status from all of our users */ + for (struct c_userlist *cu = c->users; cu; cu = cu->next) + { + c->RemoveMode(NULL, CMODE_OWNER, cu->user->nick); + c->RemoveMode(NULL, CMODE_PROTECT, cu->user->nick); + c->RemoveMode(NULL, CMODE_OP, cu->user->nick); + c->RemoveMode(NULL, CMODE_HALFOP, cu->user->nick); + c->RemoveMode(NULL, CMODE_VOICE, cu->user->nick); + } + if (c->ci) + { + /* Rejoin the bot to fix the TS */ + if (c->ci->bi) + { + ircdproto->SendPart(c->ci->bi, c, "TS reop"); + bot_join(c->ci); + } + /* Reset mlock */ + check_modes(c); + } + } + /* Their TS is newer than ours, our modes > theirs, unset their modes if need be */ + else + keep_their_modes = false; + + /* Mark the channel as syncing */ + if (was_created) + c->SetFlag(CH_SYNCING); + + spacesepstream sep(av[ac - 1]); + std::string buf; + while (sep.GetToken(buf)) + { + std::list Status; + Status.clear(); + char ch; - if (ac < 3) - return MOD_CONT; + /* Loop through prefixes */ + while ((ch = ModeManager::GetStatusChar(buf[0]))) + { + buf.erase(buf.begin()); + ChannelMode *cm = ModeManager::FindChannelModeByChar(ch); - spacesepstream nicks(av[2]); - std::string nick; - - while (nicks.GetToken(nick)) { - curnick = sstrdup(nick.c_str()); - char *curnick_real = curnick; - for (; *curnick; curnick++) { - /* I bet theres a better way to do this... */ - if ((*curnick == '&') || - (*curnick == '~') || (*curnick == '@') || (*curnick == '%') - || (*curnick == '+')) { - prefixandnick[nlen++] = *curnick; + if (cm) + { + alog("Recieved unknown mode prefix %c in FJOIN string", buf[0]); continue; - } else { - if (*curnick == ',') { - curnick++; - strncpy(prefixandnick + nlen, curnick, - sizeof(prefixandnick) - nlen); - break; - } else { - alog("fjoin: unrecognised prefix: %c", *curnick); - } } + + Status.push_back(cm); } - strlcat(nicklist, prefixandnick, sizeof(nicklist)); - strlcat(nicklist, " ", sizeof(nicklist)); - delete [] curnick_real; - nlen = 0; + + User *u = finduser(buf); + if (!u) + { + if (debug) + alog("debug: FJOIN for nonexistant user %s on %s", buf.c_str(), c->name.c_str()); + continue; + } + + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnPreJoinChannel, OnPreJoinChannel(u, c)); + + /* Add the user to the channel */ + c->JoinUser(u); + + /* Update their status internally on the channel + * This will enforce secureops etc on the user + */ + for (std::list::iterator it = Status.begin(); it != Status.end(); ++it) + { + c->SetModeInternal(*it, buf); + } + + /* Now set whatever modes this user is allowed to have on the channel */ + chan_set_correct_modes(u, c, 1); + + /* Check to see if modules want the user to join, if they do + * check to see if they are allowed to join (CheckKick will kick/ban them) + * Don't trigger OnJoinChannel event then as the user will be destroyed + */ + if (MOD_RESULT != EVENT_STOP && c->ci && c->ci->CheckKick(u)) + continue; + + FOREACH_MOD(I_OnJoinChannel, OnJoinChannel(u, c)); } - newav[0] = av[1]; /* timestamp */ - newav[1] = av[0]; /* channel name */ - newav[2] = "+"; /* channel modes */ - newav[3] = nicklist; - do_sjoin(source, 4, newav); + /* Channel is done syncing */ + if (was_created) + { + /* Unset the syncing flag */ + c->UnsetFlag(CH_SYNCING); + + /* If there are users in the channel they are allowed to be, set topic mlock etc. */ + if (c->usercount) + c->Sync(); + /* If there are no users in the channel, there is a ChanServ timer set to part the service bot + * and destroy the channel soon + */ + } return MOD_CONT; } @@ -972,11 +1031,11 @@ void moduleAddIRCDMsgs() { m = createMessage("IDLE", anope_event_idle); addCoreMessage(IRCD,m); } -bool ChannelModeFlood::IsValid(const char *value) +bool ChannelModeFlood::IsValid(const std::string &value) { char *dp, *end; - if (value && *value != ':' && strtoul((*value == '*' ? value + 1 : value), &dp, 10) > 0 && *dp == ':' && *(++dp) && strtoul(dp, &end, 10) > 0 && !*end) + if (!value.empty() && value[0] != ':' && strtoul((value[0] == '*' ? value.c_str() + 1 : value.c_str()), &dp, 10) > 0 && *dp == ':' && *(++dp) && strtoul(dp, &end, 10) > 0 && !*end) return true; return false; diff --git a/src/protocol/inspircd12.cpp b/src/protocol/inspircd12.cpp index 9587f57a7..78d3a88fc 100644 --- a/src/protocol/inspircd12.cpp +++ b/src/protocol/inspircd12.cpp @@ -65,9 +65,6 @@ IRCDVar myIrcd[] = { NULL, /* CAPAB Chan Modes */ 0, /* We support inspircd TOKENS */ 0, /* TIME STAMPS are BASE64 */ - 0, /* SJOIN ban char */ - 0, /* SJOIN except char */ - 0, /* SJOIN invite char */ 0, /* Can remove User Channel Modes with SVSMODE */ 0, /* Sglines are not enforced until user reconnects */ 1, /* ts6 */ @@ -513,91 +510,133 @@ int anope_event_fmode(const char *source, int ac, const char **av) * * 0: name * 1: channel ts (when it was created, see protocol docs for more info) - * 2: channel modes + params (NOTEL this may definitely be more than one param!) + * 2: channel modes + params (NOTE: this may definitely be more than one param!) * last: users */ int anope_event_fjoin(const char *source, int ac, const char **av) { - const char *newav[30]; // hopefully 30 will do until the stupid ac/av stuff goes away. - - /* storing the current nick */ - char *curnick; - - /* these are used to generate the final string that is passed to ircservices' core */ - int nlen = 0; - char nicklist[514]; - - /* temporary buffer */ - char prefixandnick[60]; - - *nicklist = '\0'; - *prefixandnick = '\0'; + Channel *c = findchan(av[0]); + time_t ts = atol(av[1]); + bool was_created = false; + bool keep_their_modes = true; - if (ac <= 3) - return MOD_CONT; + if (!c) + { + c = new Channel(av[0], ts); + was_created = true; + } + /* Our creation time is newer than what the server gave us */ + else if (c->creation_time > ts) + { + c->creation_time = ts; - spacesepstream nicks(av[ac - 1]); - std::string nick; + /* Remove status from all of our users */ + for (struct c_userlist *cu = c->users; cu; cu = cu->next) + { + c->RemoveMode(NULL, CMODE_OWNER, cu->user->nick); + c->RemoveMode(NULL, CMODE_PROTECT, cu->user->nick); + c->RemoveMode(NULL, CMODE_OP, cu->user->nick); + c->RemoveMode(NULL, CMODE_HALFOP, cu->user->nick); + c->RemoveMode(NULL, CMODE_VOICE, cu->user->nick); + } + if (c->ci) + { + /* Rejoin the bot to fix the TS */ + if (c->ci->bi) + { + ircdproto->SendPart(c->ci->bi, c, "TS reop"); + bot_join(c->ci); + } + /* Reset mlock */ + check_modes(c); + } + } + /* Their TS is newer than ours, our modes > theirs, unset their modes if need be */ + else + keep_their_modes = false; + + /* Mark the channel as syncing */ + if (was_created) + c->SetFlag(CH_SYNCING); + + /* If we need to keep their modes, and this FJOIN string contains modes */ + if (keep_their_modes && ac >= 4) + { + /* Set the modes internally */ + ChanSetInternalModes(c, ac - 3, av + 2); + } - while (nicks.GetToken(nick)) + spacesepstream sep(av[ac - 1]); + std::string buf; + while (sep.GetToken(buf)) { - curnick = sstrdup(nick.c_str()); - char *curnick_real = curnick; - for (; *curnick; curnick++) + std::list Status; + Status.clear(); + + /* Loop through prefixes and find modes for them */ + while (buf[0] != ',') { - /* XXX: bleagh! -- w00t */ - switch (*curnick) + ChannelMode *cm = ModeManager::FindChannelModeByChar(buf[0]); + buf.erase(buf.begin()); + if (!cm) { - case 'q': - prefixandnick[nlen++] = '~'; - break; - case 'a': - prefixandnick[nlen++] = '&'; - break; - case 'o': - prefixandnick[nlen++] = '@'; - break; - case 'h': - prefixandnick[nlen++] = '%'; - break; - case 'v': - prefixandnick[nlen++] = '+'; - break; - case ',': - curnick++; - strncpy(prefixandnick + nlen, curnick, sizeof(prefixandnick) - nlen); - goto endnick; - break; - default: - alog("fjoin: unrecognised prefix: %c", *curnick); - break; + alog("Recieved unknown mode prefix %c in FJOIN string", buf[0]); + continue; } + + Status.push_back(cm); } + buf.erase(buf.begin()); -// Much as I hate goto.. I can't `break 2' to get here.. XXX ugly -endnick: - strlcat(nicklist, prefixandnick, sizeof(nicklist)); - strlcat(nicklist, " ", sizeof(nicklist)); - delete [] curnick_real; - nlen = 0; - } + User *u = find_byuid(buf); + if (!u) + { + if (debug) + alog("debug: FJOIN for nonexistant user %s on %s", buf.c_str(), c->name.c_str()); + continue; + } - newav[0] = av[1]; /* timestamp */ - newav[1] = av[0]; /* channel name */ + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnPreJoinChannel, OnPreJoinChannel(u, c)); - int i; + /* Add the user to the channel */ + c->JoinUser(u); - /* We want to replace the last string with our newly formatted user string */ - for (i = 2; i != ac - 1; i++) - { - newav[i] = av[i]; - } + /* Update their status internally on the channel + * This will enforce secureops etc on the user + */ + for (std::list::iterator it = Status.begin(); it != Status.end(); ++it) + { + c->SetModeInternal(*it, buf); + } - newav[i] = nicklist; - i++; + /* Now set whatever modes this user is allowed to have on the channel */ + chan_set_correct_modes(u, c, 1); - do_sjoin(source, i, newav); + /* Check to see if modules want the user to join, if they do + * check to see if they are allowed to join (CheckKick will kick/ban them) + * Don't trigger OnJoinChannel event then as the user will be destroyed + */ + if (MOD_RESULT != EVENT_STOP && c->ci && c->ci->CheckKick(u)) + continue; + FOREACH_MOD(I_OnJoinChannel, OnJoinChannel(u, c)); + } + + /* Channel is done syncing */ + if (was_created) + { + /* Unset the syncing flag */ + c->UnsetFlag(CH_SYNCING); + + /* If there are users in the channel they are allowed to be, set topic mlock etc */ + if (c->usercount) + c->Sync(); + /* If there are no users in the channel, there is a ChanServ timer set to part the service bot + * and destroy the channel soon + */ + } + return MOD_CONT; } @@ -1177,10 +1216,10 @@ void moduleAddIRCDMsgs() { m = createMessage("METADATA", anope_event_metadata); addCoreMessage(IRCD,m); } -bool ChannelModeFlood::IsValid(const char *value) +bool ChannelModeFlood::IsValid(const std::string &value) { char *dp, *end; - if (value && *value != ':' && strtoul((*value == '*' ? value + 1 : value), &dp, 10) > 0 && *dp == ':' && *(++dp) && strtoul(dp, &end, 10) > 0 && !*end) return 1; + if (!value.empty() && value[0] != ':' && strtoul((value[0] == '*' ? value.c_str() + 1 : value.c_str()), &dp, 10) > 0 && *dp == ':' && *(++dp) && strtoul(dp, &end, 10) > 0 && !*end) return 1; else return 0; } diff --git a/src/protocol/ratbox.c b/src/protocol/ratbox.c index e605a922a..40ed33634 100644 --- a/src/protocol/ratbox.c +++ b/src/protocol/ratbox.c @@ -46,9 +46,6 @@ IRCDVar myIrcd[] = { NULL, /* CAPAB Chan Modes */ 0, /* We support TOKENS */ 0, /* TIME STAMPS are BASE64 */ - 0, /* SJOIN ban char */ - 0, /* SJOIN except char */ - 0, /* SJOIN invite char */ 0, /* Can remove User Channel Modes with SVSMODE */ 0, /* Sglines are not enforced until user reconnects */ 1, /* ts6 */ @@ -335,7 +332,7 @@ class RatboxProto : public IRCDTS6Proto snprintf(svidbuf, sizeof(svidbuf), "%ld", static_cast(u->timestamp)); u->nc->Shrink("authenticationtoken"); - u->nc->Extend("authenticationtoken", new ExtensibleItemPointerArray(svidbuf)); + u->nc->Extend("authenticationtoken", new ExtensibleItemPointerArray(sstrdup(svidbuf))); } } ircd_proto; @@ -345,7 +342,128 @@ class RatboxProto : public IRCDTS6Proto int anope_event_sjoin(const char *source, int ac, const char **av) { - do_sjoin(source, ac, av); + Channel *c = findchan(av[1]); + time_t ts = atol(av[0]); + bool was_created = false; + bool keep_their_modes = true; + + if (!c) + { + c = new Channel(av[1], ts); + was_created = true; + } + /* Our creation time is newer than what the server gave us */ + else if (c->creation_time > ts) + { + c->creation_time = ts; + + /* Remove status from all of our users */ + for (struct c_userlist *cu = c->users; cu; cu = cu->next) + { + c->RemoveMode(NULL, CMODE_OWNER, cu->user->nick); + c->RemoveMode(NULL, CMODE_PROTECT, cu->user->nick); + c->RemoveMode(NULL, CMODE_OP, cu->user->nick); + c->RemoveMode(NULL, CMODE_HALFOP, cu->user->nick); + c->RemoveMode(NULL, CMODE_VOICE, cu->user->nick); + } + if (c->ci) + { + /* Rejoin the bot to fix the TS */ + if (c->ci->bi) + { + ircdproto->SendPart(c->ci->bi, c, "TS reop"); + bot_join(c->ci); + } + /* Reset mlock */ + check_modes(c); + } + } + /* Their TS is newer than ours, our modes > theirs, unset their modes if need be */ + else + keep_their_modes = false; + + /* Mark the channel as syncing */ + if (was_created) + c->SetFlag(CH_SYNCING); + + /* If we need to keep their modes, and this SJOIN string contains modes */ + if (keep_their_modes && ac >= 4) + { + /* Set the modes internally */ + ChanSetInternalModes(c, ac - 3, av + 2); + } + + spacesepstream sep(av[ac - 1]); + std::string buf; + while (sep.GetToken(buf)) + { + std::list Status; + Status.clear(); + char ch; + + /* Get prefixes from the nick */ + while ((ch = ModeManager::GetStatusChar(buf[0]))) + { + buf.erase(buf.begin()); + ChannelMode *cm = ModeManager::FindChannelModeByChar(ch); + if (!cm) + { + alog("Recieved unknown mode prefix %c in SJOIN string", buf[0]); + continue; + } + + Status.push_back(cm); + } + + User *u = find_byuid(buf); + if (!u) + { + if (debug) + alog("debug: SJOIN for nonexistant user %s on %s", buf.c_str(), c->name.c_str()); + continue; + } + + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnPreJoinChannel, OnPreJoinChannel(u, c)); + + /* Add the user to the channel */ + c->JoinUser(u); + + /* Update their status internally on the channel + * This will enforce secureops etc on the user + */ + for (std::list::iterator it = Status.begin(); it != Status.end(); ++it) + { + c->SetModeInternal(*it, buf); + } + + /* Now set whatever modes this user is allowed to have on the channel */ + chan_set_correct_modes(u, c, 1); + + /* Check to see if modules want the user to join, if they do + * check to see if they are allowed to join (CheckKick will kick/ban them) + * Don't trigger OnJoinChannel event then as the user will be destroyed + */ + if (MOD_RESULT != EVENT_STOP && c->ci && c->ci->CheckKick(u)) + continue; + + FOREACH_MOD(I_OnJoinChannel, OnJoinChannel(u, c)); + } + + /* Channel is done syncing */ + if (was_created) + { + /* Unset the syncing flag */ + c->UnsetFlag(CH_SYNCING); + + /* If there are users in the channel they are allowed to be, set topic mlock etc. */ + if (c->usercount) + c->Sync(); + /* If there are no users in the channel, there is a ChanServ timer set to part the service bot + * and destroy the channel soon + */ + } + return MOD_CONT; } @@ -541,7 +659,7 @@ int anope_event_kick(const char *source, int ac, const char **av) int anope_event_join(const char *source, int ac, const char **av) { if (ac != 1) { - do_sjoin(source, ac, av); + anope_event_sjoin(source, ac, av); return MOD_CONT; } else { do_join(source, ac, av); diff --git a/src/protocol/unreal32.c b/src/protocol/unreal32.c index 8f5d70fe8..7b924ff52 100644 --- a/src/protocol/unreal32.c +++ b/src/protocol/unreal32.c @@ -48,9 +48,6 @@ IRCDVar myIrcd[] = { NULL, /* CAPAB Chan Modes */ 1, /* We support Unreal TOKENS */ 1, /* TIME STAMPS are BASE64 */ - '&', /* SJOIN ban char */ - '\"', /* SJOIN except char */ - '\'', /* SJOIN invite char */ 1, /* Can remove User Channel Modes with SVSMODE */ 0, /* Sglines are not enforced until user reconnects */ 0, /* ts6 */ @@ -970,7 +967,157 @@ int anope_event_userhost(const char *source, int ac, const char **av) int anope_event_sjoin(const char *source, int ac, const char **av) { - do_sjoin(source, ac, av); + Channel *c = findchan(av[1]); + time_t ts = base64dects(av[0]); + bool keep_their_modes = true; + bool was_created = false; + + if (!c) + { + c = new Channel(av[1], ts); + was_created = true; + } + /* Our creation time is newer than what the server gave us */ + else if (c->creation_time > ts) + { + c->creation_time = ts; + + /* Remove status from all of our users */ + for (struct c_userlist *cu = c->users; cu; cu = cu->next) + { + c->RemoveMode(NULL, CMODE_OWNER, cu->user->nick); + c->RemoveMode(NULL, CMODE_PROTECT, cu->user->nick); + c->RemoveMode(NULL, CMODE_OP, cu->user->nick); + c->RemoveMode(NULL, CMODE_HALFOP, cu->user->nick); + c->RemoveMode(NULL, CMODE_VOICE, cu->user->nick); + } + if (c->ci) + { + /* Rejoin the bot to fix the TS */ + if (c->ci->bi) + { + ircdproto->SendPart(c->ci->bi, c, "TS reop"); + bot_join(c->ci); + } + /* Reset mlock */ + check_modes(c); + } + } + /* Their TS is newer than ours, our modes > theirs, unset their modes if need be */ + else + keep_their_modes = false; + + /* Mark the channel as syncing */ + if (was_created) + c->SetFlag(CH_SYNCING); + + /* If we need to keep their modes, and this SJOIN string contains modes */ + if (keep_their_modes && ac >= 4) + { + /* Set the modes internally */ + ChanSetInternalModes(c, ac - 3, av + 2); + } + + spacesepstream sep(av[ac - 1]); + std::string buf; + while (sep.GetToken(buf)) + { + /* Ban */ + if (keep_their_modes && buf[0] == '&') + { + buf.erase(buf.begin()); + ChannelModeList *cml = dynamic_cast(ModeManager::FindChannelModeByName(CMODE_BAN)); + if (cml->IsValid(buf)) + cml->AddMask(c, buf.c_str()); + } + /* Except */ + else if (keep_their_modes && buf[0] == '"') + { + buf.erase(buf.begin()); + ChannelModeList *cml = dynamic_cast(ModeManager::FindChannelModeByName(CMODE_EXCEPT)); + + if (cml->IsValid(buf)) + cml->AddMask(c, buf.c_str()); + } + /* Invex */ + else if (keep_their_modes && buf[0] == '\'') + { + buf.erase(buf.begin()); + ChannelModeList *cml = dynamic_cast(ModeManager::FindChannelModeByName(CMODE_INVITEOVERRIDE)); + + if (cml->IsValid(buf)) + cml->AddMask(c, buf.c_str()); + } + else + { + std::list Status; + Status.clear(); + char ch; + + /* Get prefixes from the nick */ + while ((ch = ModeManager::GetStatusChar(buf[0]))) + { + buf.erase(buf.begin()); + ChannelMode *cm = ModeManager::FindChannelModeByChar(ch); + if (!cm) + { + alog("Recieved unknown mode prefix %c in SJOIN string", buf[0]); + continue; + } + + Status.push_back(cm); + } + + User *u = finduser(buf); + if (!u) + { + if (debug) + alog("debug: SJOIN for nonexistant user %s on %s", buf.c_str(), c->name.c_str()); + continue; + } + + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnPreJoinChannel, OnPreJoinChannel(u, c)); + + /* Add the user to the channel */ + c->JoinUser(u); + + /* Update their status internally on the channel + * This will enforce secureops etc on the user + */ + for (std::list::iterator it = Status.begin(); it != Status.end(); ++it) + { + c->SetModeInternal(*it, buf); + } + + /* Now set whatever modes this user is allowed to have on the channel */ + chan_set_correct_modes(u, c, 1); + + /* Check to see if modules want the user to join, if they do + * check to see if they are allowed to join (CheckKick will kick/ban them) + * Don't trigger OnJoinChannel event then as the user will be destroyed + */ + if (MOD_RESULT != EVENT_STOP && c->ci && c->ci->CheckKick(u)) + continue; + + FOREACH_MOD(I_OnJoinChannel, OnJoinChannel(u, c)); + } + } + + /* Channel is done syncing */ + if (was_created) + { + /* Unset the syncing flag */ + c->UnsetFlag(CH_SYNCING); + + /* If there are users in the channel they are allowed to be, set topic mlock etc. */ + if (c->usercount) + c->Sync(); + /* If there are no users in the channel, there is a ChanServ timer set to part the service bot + * and destroy the channel soon + */ + } + return MOD_CONT; } @@ -1047,8 +1194,9 @@ void moduleAddIRCDMsgs() { } /* Borrowed part of this check from UnrealIRCd */ -bool ChannelModeFlood::IsValid(const char *value) +bool ChannelModeFlood::IsValid(const std::string &value2) { + const char *value = value2.c_str(); char *dp, *end; /* NEW +F */ char xbuf[256], *p, *p2, *x = xbuf + 1; -- cgit