diff options
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | include/extern.h | 6 | ||||
-rw-r--r-- | include/modes.h | 10 | ||||
-rw-r--r-- | include/modules.h | 5 | ||||
-rw-r--r-- | include/services.h | 23 | ||||
-rw-r--r-- | src/channels.c | 827 | ||||
-rw-r--r-- | src/chanserv.c | 79 | ||||
-rw-r--r-- | src/core/cs_forbid.c | 1 | ||||
-rw-r--r-- | src/core/cs_set.c | 2 | ||||
-rw-r--r-- | src/core/os_defcon.c | 2 | ||||
-rw-r--r-- | src/modes.cpp | 4 | ||||
-rw-r--r-- | src/protocol/bahamut.c | 166 | ||||
-rw-r--r-- | src/protocol/inspircd11.c | 157 | ||||
-rw-r--r-- | src/protocol/inspircd12.cpp | 181 | ||||
-rw-r--r-- | src/protocol/ratbox.c | 130 | ||||
-rw-r--r-- | src/protocol/unreal32.c | 158 | ||||
-rw-r--r-- | src/users.c | 2 |
17 files changed, 944 insertions, 811 deletions
@@ -30,6 +30,7 @@ Legend: [x] CS SET INHABIT to keep pseudoclient in a channel after it empties to maintain banlists and such [x] Set forbidden channels +s [x] SendAkill should just take a pointer to the Akill class instead of millions of fields (same for some other stuff) +[x] burn do_sjoin with fire Future ------ @@ -37,7 +38,6 @@ Future [?] Remote identification (1.9.1? will this break stuff?) [ ] Language charset stuff, including collation (1.9.1? phoenix?) [ ] Add support for +k, +q, etc type umodes -[ ] burn do_sjoin with fire [ ] fantasy: allow replies/notifications to fantasy commands to go to the channel via notice [?] a way for a module to queue itself (or even another module) for unloading [ ] add overridden form of SendGlobops accepting BotInfo diff --git a/include/extern.h b/include/extern.h index 75ad3eb28..a46894fa2 100644 --- a/include/extern.h +++ b/include/extern.h @@ -68,9 +68,6 @@ E void bot_raw_mode(User * requester, ChannelInfo * ci, const char *mode, char * E Channel *chanlist[1024]; -E void chan_adduser2(User * user, Channel * c); -E Channel *join_user_update(User * user, Channel * chan, const char *name, time_t chants); - E void get_channel_stats(long *nrec, long *memuse); E Channel *findchan(const char *chan); E Channel *firstchan(); @@ -78,8 +75,6 @@ E Channel *nextchan(); E void ChanSetInternalModes(Channel *c, int ac, const char **av); -E void chan_deluser(User * user, Channel * c); - E int is_on_chan(Channel * c, User * u); E User *nc_on_chan(Channel * c, NickCore * nc); @@ -97,7 +92,6 @@ E void do_cmode(const char *source, int ac, const char **av); E void do_join(const char *source, int ac, const char **av); E void do_kick(const std::string &source, int ac, const char **av); E void do_part(const char *source, int ac, const char **av); -E void do_sjoin(const char *source, int ac, const char **av); E void do_topic(const char *source, int ac, const char **av); E void MassChannelModes(BotInfo *bi, const std::string &modes); diff --git a/include/modes.h b/include/modes.h index 5a1fd60b5..64532bb6d 100644 --- a/include/modes.h +++ b/include/modes.h @@ -99,7 +99,7 @@ class UserModeParam : public UserMode * @param value The param * @return true or false */ - virtual bool IsValid(const char *value) { return true; } + virtual bool IsValid(const std::string &value) { return true; } }; /** This class is a channel mode, all channel modes use this/inherit from this @@ -151,7 +151,7 @@ class CoreExport ChannelModeList : public ChannelMode * @param mask The mask * @return true for yes, false for no */ - virtual bool IsValid(const char *mask) { return true; } + virtual bool IsValid(const std::string &mask) { return true; } /** Add the mask to the channel, this should be overridden * @param chan The channel @@ -190,7 +190,7 @@ class CoreExport ChannelModeParam : public ChannelMode * @param value The param * @return true for yes, false for no */ - virtual bool IsValid(const char *value) { return true; } + virtual bool IsValid(const std::string &value) { return true; } }; /** This is a mode that is a channel status, eg +v/h/o/a/q. @@ -264,7 +264,7 @@ class CoreExport ChannelModeKey : public ChannelModeParam public: ChannelModeKey() : ChannelModeParam(CMODE_KEY) { } - bool IsValid(const char *value); + bool IsValid(const std::string &value); }; /** Channel mode +f (flood) @@ -274,7 +274,7 @@ class ChannelModeFlood : public ChannelModeParam public: ChannelModeFlood() : ChannelModeParam(CMODE_FLOOD) { } - bool IsValid(const char *value); + bool IsValid(const std::string &value); }; /** This class is used for channel mode +A (Admin only) diff --git a/include/modules.h b/include/modules.h index 8903eb9c2..45541c28c 100644 --- a/include/modules.h +++ b/include/modules.h @@ -644,9 +644,10 @@ class CoreExport Module /** Called before a user joins a channel * @param u The user - * @param channel The channel + * @param c The channel + * @return EVENT_STOP to allow the user to join the channel through restrictions, EVENT_CONTINUE to let other modules decide */ - virtual void OnPreJoinChannel(User *u, const char *channel) { } + virtual EventReturn OnPreJoinChannel(User *u, Channel *c) { return EVENT_CONTINUE; } /** Called when a user joins a channel * @param u The user diff --git a/include/services.h b/include/services.h index de6972fc2..244377419 100644 --- a/include/services.h +++ b/include/services.h @@ -410,10 +410,7 @@ struct ircdvars_ { int knock_needs_i; /* Check if we needed +i when setting NOKNOCK */ char *chanmodes; /* If the ircd sends CHANMODE in CAPAB this is where we store it */ int token; /* Does Anope support the tokens for the ircd */ - int sjb64; /* Base 64 encode TIMESTAMP */ - int sjoinbanchar; /* use single quotes to define it */ - int sjoinexchar; /* use single quotes to define it */ - int sjoininvchar; /* use single quotes to define it */ + int sjb64; int svsmode_ucmode; /* Can remove User Channel Modes with SVSMODE */ int sglineenforce; int ts6; /* ircd is TS6 */ @@ -832,7 +829,9 @@ struct c_userlist { enum ChannelFlags { /* Channel still exists when emptied */ - CH_PERSIST + CH_PERSIST, + /* If set the channel is syncing users (eg, multi user SJOIN) and it should not be deleted */ + CH_SYNCING }; class CoreExport Channel : public Extensible, public Flags<ChannelFlags> @@ -878,6 +877,20 @@ class CoreExport Channel : public Extensible, public Flags<ChannelFlags> int16 bouncy_modes; /* Did we fail to set modes here? */ int16 topic_sync; /* Is the topic in sync? */ + /** Restore the channel topic, set mlock (key), set stickied bans, etc + */ + void Sync(); + + /** Join a user internally to the channel + * @param u The user + */ + void JoinUser(User *u); + + /** Remove a user internally from the channel + * @param u The user + */ + void DeleteUser(User *u); + /** * See if a channel has a mode * @param Name The mode name diff --git a/src/channels.c b/src/channels.c index 6c6c399c6..e82da8dc2 100644 --- a/src/channels.c +++ b/src/channels.c @@ -52,16 +52,8 @@ Channel::Channel(const std::string &name, time_t ts) this->ci = cs_findchan(this->name); if (this->ci) - { this->ci->c = this; - check_modes(this); - stick_all(this->ci); - } - - if (serv_uplink && is_sync(serv_uplink) && (!(this->topic_sync))) - restore_topic(name.c_str()); - FOREACH_MOD(I_OnChannelCreate, OnChannelCreate(this)); } @@ -122,6 +114,141 @@ Channel::~Channel() chanlist[HASH(this->name)] = this->next; } +void Channel::Sync() +{ + if (this->ci) + { + check_modes(this); + stick_all(this->ci); + } + + if (serv_uplink && is_sync(serv_uplink) && !this->topic_sync) + restore_topic(name.c_str()); +} + +void Channel::JoinUser(User *user) +{ + struct u_chanlist *c; + + if (debug) + alog("debug: %s joins %s", user->nick.c_str(), this->name.c_str()); + + c = new u_chanlist; + c->prev = NULL; + c->next = user->chans; + if (user->chans) + user->chans->prev = c; + user->chans = c; + c->chan = this; + c->status = 0; + + struct c_userlist *u; + + u = new c_userlist; + u->prev = NULL; + u->next = this->users; + if (this->users) + this->users->prev = u; + this->users = u; + u->user = user; + u->ud = NULL; + this->usercount++; + + if (get_ignore(user->nick.c_str()) == NULL) + { + if (this->ci && (check_access(user, this->ci, CA_MEMO)) && (this->ci->memos.memos.size() > 0)) + { + if (this->ci->memos.memos.size() == 1) + notice_lang(Config.s_MemoServ, user, MEMO_X_ONE_NOTICE, this->ci->memos.memos.size(), this->ci->name.c_str()); + else + notice_lang(Config.s_MemoServ, user, MEMO_X_MANY_NOTICE, this->ci->memos.memos.size(), this->ci->name.c_str()); + } + /* Added channelname to entrymsg - 30.03.2004, Certus */ + /* Also, don't send the entrymsg when bursting -GD */ + if (this->ci && this->ci->entry_message && is_sync(user->server)) + user->SendMessage(whosends(this->ci)->nick, "[%s] %s", this->name.c_str(), this->ci->entry_message); + } + + /** + * We let the bot join even if it was an ignored user, as if we don't, + * and the ignored user doesnt just leave, the bot will never + * make it into the channel, leaving the channel botless even for + * legit users - Rob + * But don't join the bot if the channel is persistant - Adam + **/ + if (Config.s_BotServ && this->ci && this->ci->bi && !this->ci->HasFlag(CI_PERSIST)) + { + if (this->usercount == Config.BSMinUsers) + bot_join(this->ci); + } + if (Config.s_BotServ && this->ci && this->ci->bi) + { + if (this->usercount >= Config.BSMinUsers && (this->ci->botflags.HasFlag(BS_GREET)) + && user->nc && user->nc->greet && check_access(user, this->ci, CA_GREET)) + { + /* Only display the greet if the main uplink we're connected + * to has synced, or we'll get greet-floods when the net + * recovers from a netsplit. -GD + */ + if (is_sync(user->server)) + { + ircdproto->SendPrivmsg(this->ci->bi, this->name.c_str(), "[%s] %s", user->nc->display, user->nc->greet); + this->ci->bi->lastmsg = time(NULL); + } + } + } +} + +/** Remove a user internally from the channel + * @param u The user + */ +void Channel::DeleteUser(User *user) +{ + if (this->ci) + update_cs_lastseen(user, this->ci); + + struct c_userlist *u; + for (u = this->users; u && u->user != user; u = u->next); + if (!u) + return; + + if (u->ud) + { + if (u->ud->lastline) + delete [] u->ud->lastline; + delete u->ud; + } + + if (u->next) + u->next->prev = u->prev; + if (u->prev) + u->prev->next = u->next; + else + this->users = u->next; + delete u; + this->usercount--; + + /* Channel is persistant, it shouldn't be deleted and the service bot should stay */ + if (this->HasFlag(CH_PERSIST) || (this->ci && this->ci->HasFlag(CI_PERSIST))) + return; + + /* Channel is syncing from a netburst, don't destroy it as more users are probably wanting to join immediatly + * We also don't part the bot here either, if necessary we will part it after the sync + */ + if (this->HasFlag(CH_SYNCING)) + return; + + /* Additionally, do not delete this channel if ChanServ/a BotServ bot is inhabiting it */ + if (this->ci && this->ci->HasFlag(CI_INHABIT)) + return; + + if (Config.s_BotServ && this->ci && this->ci->bi && this->usercount <= Config.BSMinUsers - 1) + ircdproto->SendPart(this->ci->bi, this, NULL); + + if (!this->users) + delete this; +} + /** * See if a channel has a mode * @param Name The mode name @@ -290,12 +417,12 @@ void Channel::RemoveModeInternal(ChannelMode *cm, const std::string ¶m, bool User *u = finduser(param); if (!u) { - alog("Channel::RemoveModeInternal() MODE %s +%c for nonexistant user %s", this->name.c_str(), cm->ModeChar, param.c_str()); + alog("Channel::RemoveModeInternal() MODE %s -%c for nonexistant user %s", this->name.c_str(), cm->ModeChar, param.c_str()); return; } if (debug) - alog("debug: Setting +%c on %s for %s", cm->ModeChar, this->name.c_str(), u->nick.c_str()); + alog("debug: Setting -%c on %s for %s", cm->ModeChar, this->name.c_str(), u->nick.c_str()); ChannelModeStatus *cms = dynamic_cast<ChannelModeStatus *>(cm); chan_remove_user_status(this, u, cms->Status); @@ -381,9 +508,24 @@ void Channel::RemoveModeInternal(ChannelMode *cm, const std::string ¶m, bool */ void Channel::SetMode(BotInfo *bi, ChannelMode *cm, const std::string ¶m, bool EnforceMLock) { - if (!cm || HasMode(cm->Name)) + if (!cm) + return; + /* Don't set modes already set */ + if ((cm->Type == MODE_REGULAR || cm->Type == MODE_PARAM) && HasMode(cm->Name)) return; + else if (cm->Type == MODE_STATUS) + { + User *u = finduser(param); + if (u) + { + if (chan_has_user_status(this, u, dynamic_cast<ChannelModeStatus *>(cm)->Status)) + { + return; + } + } + } + ModeManager::StackerAdd(bi, this, cm, true, param); SetModeInternal(cm, param, EnforceMLock); } @@ -420,8 +562,23 @@ void Channel::SetMode(BotInfo *bi, char Mode, const std::string ¶m, bool Enf */ void Channel::RemoveMode(BotInfo *bi, ChannelMode *cm, const std::string ¶m, bool EnforceMLock) { - if (!cm || !HasMode(cm->Name)) + if (!cm) + return; + /* Don't unset modes that arent set */ + if ((cm->Type == MODE_REGULAR || cm->Type == MODE_PARAM) && !HasMode(cm->Name)) return; + /* Don't unset status that aren't set */ + else if (cm->Type == MODE_STATUS) + { + User *u = finduser(param); + if (u) + { + if (!chan_has_user_status(this, u, dynamic_cast<ChannelModeStatus *>(cm)->Status)) + { + return; + } + } + } ModeManager::StackerAdd(bi, this, cm, false, param); RemoveModeInternal(cm, param, EnforceMLock); @@ -748,7 +905,7 @@ void Channel::KickInternal(const std::string &source, const std::string &nick, c if (c) { FOREACH_MOD(I_OnUserKicked, OnUserKicked(c->chan, user, source, reason)); - chan_deluser(user, c->chan); + c->chan->DeleteUser(user); if (c->next) c->next->prev = c->prev; if (c->prev) @@ -784,47 +941,6 @@ bool Channel::Kick(BotInfo *bi, User *u, const char *reason, ...) return true; } -/*************************************************************************/ - -void chan_deluser(User * user, Channel * c) -{ - struct c_userlist *u; - - if (c->ci) - update_cs_lastseen(user, c->ci); - - for (u = c->users; u && u->user != user; u = u->next); - if (!u) - return; - - if (u->ud) { - if (u->ud->lastline) - delete [] u->ud->lastline; - delete u->ud; - } - - if (u->next) - u->next->prev = u->prev; - if (u->prev) - u->prev->next = u->next; - else - c->users = u->next; - delete u; - c->usercount--; - - /* Channel is persistant, it shouldn't be deleted and the service bot should stay */ - if (c->HasFlag(CH_PERSIST) || (c->ci && c->ci->HasFlag(CI_PERSIST))) - return; - - if (Config.s_BotServ && c->ci && c->ci->bi && c->usercount == Config.BSMinUsers - 1) - ircdproto->SendPart(c->ci->bi, c, NULL); - - if (!c->users) - delete c; -} - -/*************************************************************************/ - /* Returns a fully featured binary modes string. If complete is 0, the * eventual parameters won't be added to the string. */ @@ -1134,6 +1250,7 @@ void do_join(const char *source, int ac, const char **av) char *s, *t; struct u_chanlist *c, *nextc; char *channame; + time_t ctime = time(NULL); if (ircd->ts6) { user = find_byuid(source); @@ -1162,7 +1279,7 @@ void do_join(const char *source, int ac, const char **av) nextc = c->next; channame = sstrdup(c->chan->name.c_str()); FOREACH_MOD(I_OnPrePartChannel, OnPrePartChannel(user, c->chan)); - chan_deluser(user, c->chan); + c->chan->DeleteUser(user); FOREACH_MOD(I_OnPartChannel, OnPartChannel(user, findchan(channame), channame, "")); delete [] channame; delete c; @@ -1174,28 +1291,52 @@ void do_join(const char *source, int ac, const char **av) chan = findchan(s); - /* how about not triggering the JOIN event on an actual /part :) -certus */ - FOREACH_MOD(I_OnPreJoinChannel, OnPreJoinChannel(user, s)); + /* Channel doesn't exist, create it */ + if (!chan) + { + chan = new Channel(av[0], ctime); + } - /* Make sure check_kick comes before chan_adduser, so banned users - * don't get to see things like channel keys. */ - /* If channel already exists, check_kick() will use correct TS. - * Otherwise, we lose. */ - if (chan && chan->ci && chan->ci->CheckKick(user)) - continue; + /* Join came with a TS */ + if (ac == 2) + { + time_t ts = atol(av[1]); - time_t ts = time(NULL); - if (ac == 2) { - ts = strtoul(av[1], NULL, 10); - if (debug) { - alog("debug: recieved a new TS for JOIN: %ld", - static_cast<long>(ts)); + /* Their time is older, we lose */ + if (chan->creation_time > ts) + { + if (debug) + alog("debug: recieved a new TS for JOIN: %ld", ts); + + if (chan->ci) + { + /* Cycle the bot to fix ts */ + if (chan->ci->bi) + { + ircdproto->SendPart(chan->ci->bi, chan, "TS reop"); + bot_join(chan->ci); + } + /* Be sure to set mlock again, else we could be -r etc.. */ + check_modes(chan); + } } } - chan = join_user_update(user, chan, s, ts); + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnPreJoinChannel, OnPreJoinChannel(user, chan)); + + /* Join the user to the channel */ + chan->JoinUser(user); + /* Set the propre modes on the user */ chan_set_correct_modes(user, chan, 1); + /* Modules may want to allow this user in the channel, check. + * If not, CheckKick will kick/ban them, don't call OnJoinChannel after this as the user will have + * been destroyed + */ + if (MOD_RESULT != EVENT_STOP && chan && chan->ci && chan->ci->CheckKick(user)) + continue; + FOREACH_MOD(I_OnJoinChannel, OnJoinChannel(user, chan)); } } @@ -1269,7 +1410,7 @@ void do_part(const char *source, int ac, const char **av) FOREACH_MOD(I_OnPrePartChannel, OnPrePartChannel(user, c->chan)); std::string ChannelName = c->chan->name; - chan_deluser(user, c->chan); + c->chan->DeleteUser(user); FOREACH_MOD(I_OnPartChannel, OnPartChannel(user, findchan(ChannelName.c_str()), ChannelName, av[1] ? av[1] : "")); @@ -1286,424 +1427,6 @@ void do_part(const char *source, int ac, const char **av) /*************************************************************************/ -/* Handle a SJOIN command. - - On channel creation, syntax is: - - av[0] = timestamp - av[1] = channel name - av[2|3|4] = modes \ depends of whether the modes k and l - av[3|4|5] = users / are set or not. - - When a single user joins an (existing) channel, it is: - - av[0] = timestamp - av[1] = user - - ============================================================ - - Unreal SJOIN - - On Services connect there is - SJOIN !11LkOb #ircops +nt :@Trystan &*!*@*.aol.com "*@*.home.com - - av[0] = time stamp (base64) - av[1] = channel - av[2] = modes - av[3] = users + bans + exceptions - - On Channel Creation or a User joins an existing - Luna.NomadIrc.Net SJOIN !11LkW9 #akill :@Trystan - Luna.NomadIrc.Net SJOIN !11LkW9 #akill :Trystan` - - av[0] = time stamp (base64) - av[1] = channel - av[2] = users - -*/ - -void do_sjoin(const char *source, int ac, const char **av) -{ - Channel *c; - User *user; - Server *serv; - struct c_userlist *cu; - const char *s = NULL; - char *buf, *end, cubuf[7], *end2, value; - const char *modes[6]; - int is_sqlined = 0; - int ts = 0; - int is_created = 0; - int keep_their_modes = 1; - ChannelModeList *cml; - - serv = findserver(servlist, source); - - if (ircd->sjb64) { - ts = base64dects(av[0]); - } else { - ts = strtoul(av[0], NULL, 10); - } - c = findchan(av[1]); - if (c != NULL) { - if (c->creation_time == 0 || ts == 0) - c->creation_time = 0; - else if (c->creation_time > ts) { - c->creation_time = ts; - for (cu = c->users; cu; cu = cu->next) { - c->RemoveMode(NULL, CMODE_OP, cu->user->nick); - c->RemoveMode(NULL, CMODE_VOICE, cu->user->nick); - } - if (c->ci) - { - if (c->ci->bi) - { - /* This is ugly, but it always works */ - ircdproto->SendPart(c->ci->bi, c, "TS reop"); - bot_join(c->ci); - } - /* Make sure +r is set */ - if (ModeManager::FindChannelModeByName(CMODE_REGISTERED)) - { - c->SetMode(NULL, CMODE_REGISTERED); - } - } - /* XXX simple modes and bans */ - } else if (c->creation_time < ts) - keep_their_modes = 0; - } else - is_created = 1; - - /* Double check to avoid unknown modes that need parameters */ - if (ac >= 4) { - if (ircd->chansqline) { - if (!c) - is_sqlined = check_chan_sqline(av[1]); - } - - cubuf[0] = '+'; - modes[0] = cubuf; - - /* We make all the users join */ - s = av[ac - 1]; /* Users are always the last element */ - - while (*s) { - end = const_cast<char *>(strchr(s, ' ')); - if (end) - *end = 0; - - end2 = cubuf + 1; - - - if (ircd->sjoinbanchar) { - if (*s == ircd->sjoinbanchar && keep_their_modes) { - buf = myStrGetToken(s, ircd->sjoinbanchar, 1); - - cml = dynamic_cast<ChannelModeList *>(ModeManager::FindChannelModeByName(CMODE_BAN)); - if (cml->IsValid(buf)) - cml->AddMask(c, buf); - - delete [] buf; - if (!end) - break; - s = end + 1; - continue; - } - } - if (ircd->sjoinexchar) { - if (*s == ircd->sjoinexchar && keep_their_modes) { - buf = myStrGetToken(s, ircd->sjoinexchar, 1); - - cml = dynamic_cast<ChannelModeList *>(ModeManager::FindChannelModeByName(CMODE_EXCEPT)); - if (cml->IsValid(buf)) - cml->AddMask(c, buf); - - delete [] buf; - if (!end) - break; - s = end + 1; - continue; - } - } - - if (ircd->sjoininvchar) { - if (*s == ircd->sjoininvchar && keep_their_modes) { - buf = myStrGetToken(s, ircd->sjoininvchar, 1); - - cml = dynamic_cast<ChannelModeList *>(ModeManager::FindChannelModeByName(CMODE_INVITEOVERRIDE)); - if (cml->IsValid(buf)) - cml->AddMask(c, buf); - - delete [] buf; - if (!end) - break; - s = end + 1; - continue; - } - } - - while ((value = ModeManager::GetStatusChar(*s))) - { - *end2++ = value; - *s++; - } - *end2 = 0; - - if (ircd->ts6) { - user = find_byuid(s); - if (!user) - user = finduser(s); - } else { - user = finduser(s); - } - - if (!user) { - if (debug) { - alog("debug: SJOIN for nonexistent user %s on %s", s, - av[1]); - } - return; - } - - if (is_sqlined && !is_oper(user)) { - ircdproto->SendKick(findbot(Config.s_OperServ), c, user, "Q-Lined"); - } else { - if (!c || !c->ci || !c->ci->CheckKick(user)) - { - FOREACH_MOD(I_OnPreJoinChannel, OnPreJoinChannel(user, av[1])); - - /* Make the user join; if the channel does not exist it - * will be created there. This ensures that the channel - * is not created to be immediately destroyed, and - * that the locked key or topic is not shown to anyone - * who joins the channel when empty. - */ - c = join_user_update(user, c, av[1], ts); - - /* We update user mode on the channel */ - if (end2 - cubuf > 1 && keep_their_modes) { - int i; - - for (i = 1; i < end2 - cubuf; i++) - modes[i] = user->nick.c_str(); - - ChanSetInternalModes(c, 1 + (end2 - cubuf - 1), modes); - } - - if (c->ci && (!serv || is_sync(serv)) - && !c->topic_sync) - restore_topic(c->name.c_str()); - chan_set_correct_modes(user, c, 1); - - FOREACH_MOD(I_OnJoinChannel, OnJoinChannel(user, c)); - } - } - - if (!end) - break; - s = end + 1; - } - - if (c && keep_their_modes) { - /* We now update the channel mode. */ - ChanSetInternalModes(c, ac - 3, &av[2]); - } - - /* Unreal just had to be different */ - } else if (ac == 3 && !ircd->ts6) { - if (ircd->chansqline) { - if (!c) - is_sqlined = check_chan_sqline(av[1]); - } - - cubuf[0] = '+'; - modes[0] = cubuf; - - /* We make all the users join */ - s = av[2]; /* Users are always the last element */ - - while (*s) { - end = const_cast<char *>(strchr(s, ' ')); - if (end) - *end = 0; - - end2 = cubuf + 1; - - while ((value = ModeManager::GetStatusChar(*s))) - { - *end2++ = value; - *s++; - } - *end2 = 0; - - if (ircd->ts6) { - user = find_byuid(s); - if (!user) - user = finduser(s); - } else { - user = finduser(s); - } - - if (!user) { - if (debug) { - alog("debug: SJOIN for nonexistent user %s on %s", s, - av[1]); - } - return; - } - - if (is_sqlined && !is_oper(user)) { - ircdproto->SendKick(findbot(Config.s_OperServ), c, user, "Q-Lined"); - } else { - if (!c || !c->ci || !c->ci->CheckKick(user)) - { - FOREACH_MOD(I_OnPreJoinChannel, OnPreJoinChannel(user, av[1])); - - /* Make the user join; if the channel does not exist it - * will be created there. This ensures that the channel - * is not created to be immediately destroyed, and - * that the locked key or topic is not shown to anyone - * who joins the channel when empty. - */ - c = join_user_update(user, c, av[1], ts); - - /* We update user mode on the channel */ - if (end2 - cubuf > 1 && keep_their_modes) { - int i; - - for (i = 1; i < end2 - cubuf; i++) - modes[i] = user->nick.c_str(); - ChanSetInternalModes(c, 1 + (end2 - cubuf - 1), modes); - } - - chan_set_correct_modes(user, c, 1); - - FOREACH_MOD(I_OnJoinChannel, OnJoinChannel(user, c)); - } - } - - if (!end) - break; - s = end + 1; - } - } else if (ac == 3 && ircd->ts6) { - if (ircd->chansqline) { - if (!c) - is_sqlined = check_chan_sqline(av[1]); - } - - cubuf[0] = '+'; - modes[0] = cubuf; - - /* We make all the users join */ - s = sstrdup(source); /* Users are always the last element */ - - while (*s) { - end = const_cast<char *>(strchr(s, ' ')); - if (end) - *end = 0; - - end2 = cubuf + 1; - - while ((value = ModeManager::GetStatusChar(*s))) - { - *end2++ = value; - *s++; - } - *end2 = 0; - - if (ircd->ts6) { - user = find_byuid(s); - if (!user) - user = finduser(s); - } else { - user = finduser(s); - } - if (!user) { - if (debug) { - alog("debug: SJOIN for nonexistent user %s on %s", s, - av[1]); - } - delete [] s; - return; - } - - if (is_sqlined && !is_oper(user)) { - ircdproto->SendKick(findbot(Config.s_OperServ), c, user, "Q-Lined"); - } else { - if (!c || !c->ci || !c->ci->CheckKick(user)) - { - FOREACH_MOD(I_OnPreJoinChannel, OnPreJoinChannel(user, av[1])); - - /* Make the user join; if the channel does not exist it - * will be created there. This ensures that the channel - * is not created to be immediately destroyed, and - * that the locked key or topic is not shown to anyone - * who joins the channel when empty. - */ - c = join_user_update(user, c, av[1], ts); - - /* We update user mode on the channel */ - if (end2 - cubuf > 1 && keep_their_modes) { - int i; - - for (i = 1; i < end2 - cubuf; i++) - modes[i] = user->nick.c_str(); - ChanSetInternalModes(c, 1 + (end2 - cubuf - 1), modes); - } - - chan_set_correct_modes(user, c, 1); - - FOREACH_MOD(I_OnJoinChannel, OnJoinChannel(user, c)); - } - } - - if (!end) - break; - s = end + 1; - } - delete [] s; - } else if (ac == 2) { - if (ircd->ts6) { - user = find_byuid(source); - if (!user) - user = finduser(source); - } else { - user = finduser(source); - } - if (!user) { - if (debug) { - alog("debug: SJOIN for nonexistent user %s on %s", source, - av[1]); - } - return; - } - - if (c && c->ci && c->ci->CheckKick(user)) - return; - - if (ircd->chansqline) { - if (!c) - is_sqlined = check_chan_sqline(av[1]); - } - - if (is_sqlined && !is_oper(user)) { - ircdproto->SendKick(findbot(Config.s_OperServ), c, user, "Q-Lined"); - } else { - FOREACH_MOD(I_OnPreJoinChannel, OnPreJoinChannel(user, av[1])); - - c = join_user_update(user, c, av[1], ts); - if (is_created && c->ci) - restore_topic(c->name.c_str()); - chan_set_correct_modes(user, c, 1); - - FOREACH_MOD(I_OnJoinChannel, OnJoinChannel(user, c)); - } - } -} - -/*************************************************************************/ - /** Process a MODE command from the server, and set the modes on the user/channel * it was sent for * @param source The source of the command @@ -1997,132 +1720,6 @@ void chan_set_correct_modes(User * user, Channel * c, int give_modes) /*************************************************************************/ -/* Add/remove a user to/from a channel, creating or deleting the channel as - * necessary. If creating the channel, restore mode lock and topic as - * necessary. Also check for auto-opping and auto-voicing. - */ - -void chan_adduser2(User * user, Channel * c) -{ - struct c_userlist *u; - - u = new c_userlist; - u->prev = NULL; - u->next = c->users; - if (c->users) - c->users->prev = u; - c->users = u; - u->user = user; - u->ud = NULL; - c->usercount++; - - if (get_ignore(user->nick.c_str()) == NULL) { - if (c->ci && (check_access(user, c->ci, CA_MEMO)) - && (c->ci->memos.memos.size() > 0)) { - if (c->ci->memos.memos.size() == 1) { - notice_lang(Config.s_MemoServ, user, MEMO_X_ONE_NOTICE, - c->ci->memos.memos.size(), c->ci->name.c_str()); - } else { - notice_lang(Config.s_MemoServ, user, MEMO_X_MANY_NOTICE, - c->ci->memos.memos.size(), c->ci->name.c_str()); - } - } - /* Added channelname to entrymsg - 30.03.2004, Certus */ - /* Also, don't send the entrymsg when bursting -GD */ - if (c->ci && c->ci->entry_message && is_sync(user->server)) - user->SendMessage(whosends(c->ci)->nick, "[%s] %s", c->name.c_str(), c->ci->entry_message); - } - - /** - * We let the bot join even if it was an ignored user, as if we don't, - * and the ignored user dosnt just leave, the bot will never - * make it into the channel, leaving the channel botless even for - * legit users - Rob - * But don't join the bot if the channel is persistant - Adam - **/ - if (Config.s_BotServ && c->ci && c->ci->bi && !c->ci->HasFlag(CI_PERSIST)) - { - if (c->usercount == Config.BSMinUsers) - bot_join(c->ci); - } - if (Config.s_BotServ && c->ci && c->ci->bi) - { - if (c->usercount >= Config.BSMinUsers && (c->ci->botflags.HasFlag(BS_GREET)) - && user->nc && user->nc->greet - && check_access(user, c->ci, CA_GREET)) { - /* Only display the greet if the main uplink we're connected - * to has synced, or we'll get greet-floods when the net - * recovers from a netsplit. -GD - */ - if (is_sync(user->server)) { - ircdproto->SendPrivmsg(c->ci->bi, c->name.c_str(), "[%s] %s", - user->nc->display, user->nc->greet); - c->ci->bi->lastmsg = time(NULL); - } - } - } -} - -/*************************************************************************/ - -Channel *join_user_update(User * user, Channel * chan, const char *name, - time_t chants) -{ - struct u_chanlist *c; - - /* If it's a new channel, so we need to create it first. */ - if (!chan) - chan = new Channel(name, chants); - else - { - // Check chants against 0, as not every ircd sends JOIN with a TS. - if (chan->creation_time > chants && chants != 0) - { - struct c_userlist *cu; - - chan->creation_time = chants; - for (cu = chan->users; cu; cu = cu->next) - { - chan->RemoveMode(NULL, CMODE_OP, cu->user->nick); - chan->RemoveMode(NULL, CMODE_VOICE, cu->user->nick); - } - if (chan->ci) - { - if (chan->ci->bi) - { - /* This is ugly, but it always works */ - ircdproto->SendPart(chan->ci->bi, chan, "TS reop"); - bot_join(chan->ci); - } - /* Make sure +r is set */ - if (ModeManager::FindChannelModeByName(CMODE_REGISTERED)) - { - chan->SetMode(NULL, CMODE_REGISTERED); - } - } - /* XXX simple modes and bans */ - } - } - - if (debug) - alog("debug: %s joins %s", user->nick.c_str(), chan->name.c_str()); - - c = new u_chanlist; - c->prev = NULL; - c->next = user->chans; - if (user->chans) - user->chans->prev = c; - user->chans = c; - c->chan = chan; - c->status = 0; - - chan_adduser2(user, chan); - - return chan; -} - -/*************************************************************************/ - /** Set modes on every channel * This overrides mlock on channels * @param bi The bot to send the modes from diff --git a/src/chanserv.c b/src/chanserv.c index 8c74d1c6a..af458e42b 100644 --- a/src/chanserv.c +++ b/src/chanserv.c @@ -119,26 +119,41 @@ void moduleAddChanServCmds() { /* *INDENT-ON* */ /*************************************************************************/ +/** A timer used to keep the BotServ bot/ChanServ in the channel + * after kicking the last user in a channel + */ class ChanServTimer : public Timer { - private: - std::string channel; + private: + Channel *c; - public: - ChanServTimer(long delay, const std::string &chan) : Timer(delay), channel(chan) - { - } + public: + ChanServTimer(Channel *chan) : Timer(Config.CSInhabit), c(chan) + { + if (c->ci) + c->ci->SetFlag(CI_INHABIT); + } - void Tick(time_t ctime) - { - ChannelInfo *ci = cs_findchan(channel); + void Tick(time_t) + { + if (!c->ci) + return; - if (ci) - ci->UnsetFlag(CI_INHABIT); + c->ci->UnsetFlag(CI_INHABIT); - if (ci->c) - ircdproto->SendPart(findbot(Config.s_ChanServ), ci->c, NULL); - } + /* If the channel has users again, don't part it and halt */ + if (c->usercount) + return; + + if (c->ci->bi) + ircdproto->SendPart(c->ci->bi, c, NULL); + else + ircdproto->SendPart(findbot(Config.s_ChanServ), c, NULL); + + /* Now delete the channel as it is empty */ + if (!c->HasFlag(CH_PERSIST) && !c->ci->HasFlag(CI_PERSIST)) + delete c; + } }; /*************************************************************************/ @@ -611,9 +626,8 @@ bool ChannelInfo::CheckKick(User *user) NickCore *nc; char mask[BUFSIZE]; const char *reason; - ChanServTimer *t; - if (!user) + if (!user || !this->c) return false; if (user->isSuperAdmin == 1) @@ -680,18 +694,12 @@ bool ChannelInfo::CheckKick(User *user) alog("debug: channel: AutoKicking %s!%s@%s from %s", user->nick.c_str(), user->GetIdent().c_str(), user->host, this->name.c_str()); - /* Remember that the user has not been added to our channel user list - * yet, so we check whether the channel does not exist OR has no user - * on it (before SJOIN would have created the channel structure, while - * JOIN would not). */ - /* Don't check for CI_INHABIT before for the Channel record cos else - * c may be NULL even if it exists */ - if ((!this->c || this->c->usercount == 0) && !this->HasFlag(CI_INHABIT)) + /* If the channel doesnt have any users and if a bot isn't already in the channel, join it + * NOTE: we use usercount == 1 here as there is one user, but they are about to be destroyed + */ + if (this->c->usercount == 1 && !this->HasFlag(CI_INHABIT)) { - ircdproto->SendJoin(findbot(Config.s_ChanServ), this->name.c_str(), (this->c ? this->c->creation_time : time(NULL))); - /* - * If channel was forbidden, etc, set it +si to prevent rejoin - */ + /* If channel was forbidden, etc, set it +si to prevent rejoin */ if (set_modes) { c->SetMode(NULL, CMODE_NOEXTERNAL); @@ -700,15 +708,18 @@ bool ChannelInfo::CheckKick(User *user) c->SetMode(NULL, CMODE_INVITE); } - t = new ChanServTimer(Config.CSInhabit, this->name.c_str()); - this->SetFlag(CI_INHABIT); + /* This channel has no bot assigned to it, join ChanServ */ + if (!this->bi) + { + ircdproto->SendJoin(findbot(Config.s_ChanServ), this->name.c_str(), this->c->creation_time); + } + + /* Set a timer for this channel to part the bots later */ + new ChanServTimer(this->c); } - if (c) - { - c->SetMode(NULL, CMODE_BAN, mask); - ircdproto->SendKick(whosends(this), c, user, "%s", reason); - } + this->c->SetMode(NULL, CMODE_BAN, mask); + this->c->Kick(NULL, user, "%s", reason); return true; } diff --git a/src/core/cs_forbid.c b/src/core/cs_forbid.c index adf82ab2c..718cd41a1 100644 --- a/src/core/cs_forbid.c +++ b/src/core/cs_forbid.c @@ -68,7 +68,6 @@ class CommandCSForbid : public Command if ((c = findchan(ci->name.c_str()))) { struct c_userlist *cu, *nextu; - const char *av[3]; /* Before banning everyone, it might be prudent to clear +e and +I lists.. * to prevent ppl from rejoining.. ~ Viper */ diff --git a/src/core/cs_set.c b/src/core/cs_set.c index dbe35f79c..277649cfb 100644 --- a/src/core/cs_set.c +++ b/src/core/cs_set.c @@ -217,7 +217,7 @@ class CommandCSSet : public Command if (!modeparams.GetToken(param)) continue; - if (!cmp->IsValid(param.c_str())) + if (!cmp->IsValid(param)) continue; ci->SetMLock(cmp->Name, true, param); diff --git a/src/core/os_defcon.c b/src/core/os_defcon.c index 081cefa23..e9583adfc 100644 --- a/src/core/os_defcon.c +++ b/src/core/os_defcon.c @@ -461,7 +461,7 @@ void defconParseModeString(const char *str) continue; } - if (!cmp->IsValid(param.c_str())) + if (!cmp->IsValid(param)) continue; SetDefConParam(cmp->Name, param); diff --git a/src/modes.cpp b/src/modes.cpp index 23cc87132..9f886cc3e 100644 --- a/src/modes.cpp +++ b/src/modes.cpp @@ -168,9 +168,9 @@ ChannelModeStatus::~ChannelModeStatus() * @param value The key * @return true or false */ -bool ChannelModeKey::IsValid(const char *value) +bool ChannelModeKey::IsValid(const std::string &value) { - if (value && *value != ':' && !strchr(value, ',')) + if (!value.empty() && value.find(':') != std::string::npos && value.find(',') != std::string::npos) return true; return false; 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<ChannelMode *> 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<ChannelMode *>::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<ChannelMode *> 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<ChannelMode *>::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<ChannelMode *> 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<ChannelMode *>::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<long>(u->timestamp)); u->nc->Shrink("authenticationtoken"); - u->nc->Extend("authenticationtoken", new ExtensibleItemPointerArray<char>(svidbuf)); + u->nc->Extend("authenticationtoken", new ExtensibleItemPointerArray<char>(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<ChannelMode *> 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<ChannelMode *>::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<ChannelModeList *>(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<ChannelModeList *>(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<ChannelModeList *>(ModeManager::FindChannelModeByName(CMODE_INVITEOVERRIDE)); + + if (cml->IsValid(buf)) + cml->AddMask(c, buf.c_str()); + } + else + { + std::list<ChannelMode *> 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<ChannelMode *>::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; diff --git a/src/users.c b/src/users.c index 137190d05..c98a99133 100644 --- a/src/users.c +++ b/src/users.c @@ -277,7 +277,7 @@ User::~User() while (c) { c2 = c->next; - chan_deluser(this, c->chan); + c->chan->DeleteUser(this); delete c; c = c2; } |