From 98a92e20f1fa4a9c7e5749e063414483d4615c2e Mon Sep 17 00:00:00 2001 From: brain Date: Thu, 27 Jul 2006 17:59:20 +0000 Subject: Mode merging during FJOIN with ourts==theirts. Only +k and +l have CheckTimestamp methods atm, needs to be finished by me and TIDIED. git-svn-id: http://svn.inspircd.org/repository/trunk/inspircd@4556 e03df62e-2008-0410-955e-edbf42e46eb7 --- include/mode.h | 19 +++++ include/modes/cmode_k.h | 1 + include/modes/cmode_l.h | 1 + src/mode.cpp | 4 +- src/modes/cmode_k.cpp | 5 ++ src/modes/cmode_l.cpp | 6 ++ src/modules/m_spanningtree.cpp | 186 +++++++++++++++++++++++++++++++++++++++-- 7 files changed, 212 insertions(+), 10 deletions(-) diff --git a/include/mode.h b/include/mode.h index b248dfb3e..6e2f03788 100644 --- a/include/mode.h +++ b/include/mode.h @@ -182,6 +182,19 @@ class ModeHandler : public Extensible */ virtual bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel); + /** + * When a remote server needs to bounce a set of modes, it will call this method for every mode + * in the mode string to determine if the mode is set or not. + * @param source of the mode change, this will be NULL for a server mode + * @param dest Target user of the mode change, if this is a user mode + * @param channel Target channel of the mode change, if this is a channel mode + * @param parameter The parameter given for the mode change, or an empty string + * @returns The first value of the pair should be true if the mode is set with the given parameter. + * In the case of permissions modes such as channelmode +o, this should return true if the user given + * as the parameter has the given privilage on the given channel. The string value of the pair will hold + * the current setting for this mode set locally, when the bool is true, or, the parameter given. + * This allows the local server to enforce our locally set parameters back to a remote server. + */ virtual std::pair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter); }; @@ -347,6 +360,12 @@ class ModeParser : public classbase */ void Process(const char** parameters, int pcnt, userrec *user, bool servermode); + /** + * Find the mode handler for a given mode and type + * @param modeletter mode letter to search for + * @param type of mode to search for, user or channel + * @returns a pointer to a ModeHandler class, or NULL of there isnt a handler for the given mode + */ ModeHandler* FindMode(unsigned const char modeletter, ModeType mt); }; diff --git a/include/modes/cmode_k.h b/include/modes/cmode_k.h index bcb67b945..94a317df0 100644 --- a/include/modes/cmode_k.h +++ b/include/modes/cmode_k.h @@ -6,4 +6,5 @@ class ModeChannelKey : public ModeHandler ModeChannelKey(); ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding); std::pair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter); + bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel); }; diff --git a/include/modes/cmode_l.h b/include/modes/cmode_l.h index 3eb332c7f..886ece324 100644 --- a/include/modes/cmode_l.h +++ b/include/modes/cmode_l.h @@ -6,4 +6,5 @@ class ModeChannelLimit : public ModeHandler ModeChannelLimit(); ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding); std::pair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter); + bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel); }; diff --git a/src/mode.cpp b/src/mode.cpp index d5ee5acdd..5b32c707a 100644 --- a/src/mode.cpp +++ b/src/mode.cpp @@ -292,13 +292,13 @@ void ModeParser::Process(const char** parameters, int pcnt, userrec *user, bool * (e.g. are they a (half)op? */ - if (cstatus(user, targetchannel) < STATUS_HOP) + if ((IS_LOCAL(user)) && (cstatus(user, targetchannel) < STATUS_HOP)) { /* We don't have halfop */ log(DEBUG,"The user is not a halfop or above, checking other reasons for being able to set the modes"); /* Are we a uline or is it a servermode? */ - if ((!is_uline(user->server)) && (!servermode) && (IS_LOCAL(user))) + if ((!is_uline(user->server)) && (!servermode)) { /* Not enough permission: * NOT a uline and NOT a servermode, diff --git a/src/modes/cmode_k.cpp b/src/modes/cmode_k.cpp index 5e55ce972..e54aa688a 100644 --- a/src/modes/cmode_k.cpp +++ b/src/modes/cmode_k.cpp @@ -20,6 +20,11 @@ std::pair ModeChannelKey::ModeSet(userrec* source, userrec* de } } +bool ModeChannelKey::CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel) +{ + /* When TS is equal, the alphabetically later channel key wins */ + return (their_param < our_param); +} ModeAction ModeChannelKey::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) { diff --git a/src/modes/cmode_l.cpp b/src/modes/cmode_l.cpp index a5fa5329b..89d67026c 100644 --- a/src/modes/cmode_l.cpp +++ b/src/modes/cmode_l.cpp @@ -20,6 +20,12 @@ std::pair ModeChannelLimit::ModeSet(userrec* source, userrec* } } +bool ModeChannelLimit::CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel) +{ + /* When TS is equal, the higher channel limit wins */ + return (atoi(their_param.c_str()) < atoi(our_param.c_str())); +} + ModeAction ModeChannelLimit::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding) { if (adding) diff --git a/src/modules/m_spanningtree.cpp b/src/modules/m_spanningtree.cpp index cd64ed7c5..77929b382 100644 --- a/src/modules/m_spanningtree.cpp +++ b/src/modules/m_spanningtree.cpp @@ -919,7 +919,6 @@ class TreeSocket : public InspSocket { /* Everything else is fine to append to the modelist */ modelist[n++] = params[q].c_str(); - log(DEBUG,"Add param: %s",params[q].c_str()); } } @@ -939,6 +938,182 @@ class TreeSocket : public InspSocket ourTS = chan->age; } } + + /* TS is equal: Merge the mode changes, use voooodoooooo on modes + * with parameters. + */ + if (TS == ourTS) + { + log(DEBUG,"Entering TS equality check"); + ModeHandler* mh = NULL; + unsigned long paramptr = 3; + std::string to_bounce = ""; + std::string to_keep = ""; + std::vector params_to_keep; + std::string params_to_bounce = ""; + bool adding = true; + char cur_change = 1; + char old_change = 0; + char old_bounce_change = 0; + /* Merge modes, basically do special stuff to mode with params */ + for (std::string::iterator x = params[2].begin(); x != params[2].end(); x++) + { + switch (*x) + { + case '-': + adding = false; + break; + case '+': + adding = true; + break; + default: + if (adding) + { + /* We only care about whats being set, + * not whats being unset + */ + mh = ServerInstance->ModeGrok->FindMode(*x, chan ? MODETYPE_CHANNEL : MODETYPE_USER); + + if ((mh->GetNumParams(adding) > 0) && (!mh->IsListMode())) + { + /* We only want to do special things to + * modes with parameters, we are going to rewrite + * those parameters + */ + std::pair ret; + adding ? cur_change = '+' : cur_change = '-'; + + ret = mh->ModeSet(smode ? NULL : who, dst, chan, params[paramptr]); + + /* The mode is set here, check which we should keep */ + if (ret.first) + { + bool which_to_keep = mh->CheckTimeStamp(TS, ourTS, params[paramptr], ret.second, chan); + + if (which_to_keep == true) + { + /* Keep ours, bounce theirs: + * Send back ours to them and + * drop their mode changs + */ + adding ? cur_change = '+' : cur_change = '-'; + if (cur_change != old_bounce_change) + to_bounce += cur_change; + to_bounce += *x; + old_bounce_change = cur_change; + + if ((mh->GetNumParams(adding) > 0) && (paramptr < params.size())) + params_to_bounce.append(" ").append(ret.second); + } + else + { + /* Keep theirs: Accept their mode change, + * do nothing else + */ + adding ? cur_change = '+' : cur_change = '-'; + if (cur_change != old_change) + to_keep += cur_change; + to_keep += *x; + old_change = cur_change; + + if ((mh->GetNumParams(adding) > 0) && (paramptr < params.size())) + params_to_keep.push_back(params[paramptr]); + } + } + else + { + /* Mode isnt set here, we want it */ + adding ? cur_change = '+' : cur_change = '-'; + if (cur_change != old_change) + to_keep += cur_change; + to_keep += *x; + old_change = cur_change; + + if ((mh->GetNumParams(adding) > 0) && (paramptr < params.size())) + params_to_keep.push_back(params[paramptr]); + } + + paramptr++; + } + else + { + mh = ServerInstance->ModeGrok->FindMode(*x, chan ? MODETYPE_CHANNEL : MODETYPE_USER); + + if (mh) + { + adding ? cur_change = '+' : cur_change = '-'; + + /* Just keep this, safe to merge with no checks + * it has no parameters + */ + + if (cur_change != old_change) + to_keep += cur_change; + to_keep += *x; + old_change = cur_change; + + if ((mh->GetNumParams(adding) > 0) && (paramptr < params.size())) + { + log(DEBUG,"Mode removal %d %d",adding, mh->GetNumParams(adding)); + params_to_keep.push_back(params[paramptr++]); + } + } + } + } + else + { + mh = ServerInstance->ModeGrok->FindMode(*x, chan ? MODETYPE_CHANNEL : MODETYPE_USER); + + if (mh) + { + /* Taking a mode away */ + adding ? cur_change = '+' : cur_change = '-'; + + if (cur_change != old_change) + to_keep += cur_change; + to_keep += *x; + old_change = cur_change; + + if ((mh->GetNumParams(adding) > 0) && (paramptr < params.size())) + params_to_keep.push_back(params[paramptr++]); + } + } + break; + } + } + + if (to_bounce.length()) + { + std::deque newparams; + newparams.push_back(params[0]); + newparams.push_back(ConvToStr(ourTS)); + newparams.push_back(to_bounce+params_to_bounce); + DoOneToOne(Srv->GetServerName(),"FMODE",newparams,sourceserv); + } + + if (to_keep.length()) + { + n = 0; + modelist[0] = params[0].c_str(); + modelist[1] = to_keep.c_str(); + + for (unsigned int q = 2; (q < params.size()) && (q < 64); q++) + modelist[q] = params_to_keep[n++].c_str(); + + if (smode) + { + Srv->SendMode(modelist, n+2, who); + } + else + { + Srv->CallCommandHandler("MODE", modelist, n+2, who); + } + + /* HOT POTATO! PASS IT ON! */ + DoOneToAllButSender(source,"FMODE",params,sourceserv); + } + } + else /* U-lined servers always win regardless of their TS */ if ((TS > ourTS) && (!Srv->IsUlined(source))) { @@ -3938,9 +4113,6 @@ class ModuleSpanningTree : public Module virtual void OnMode(userrec* user, void* dest, int target_type, const std::string &text) { - /* 1.1 Series InspIRCd Spanning Tree now uses FMODE for all user modes, - * with a timestamp to prevent certain types of modehack - */ if ((user->fd > -1) && (user->registered == 7)) { if (target_type == TYPE_USER) @@ -3948,18 +4120,16 @@ class ModuleSpanningTree : public Module userrec* u = (userrec*)dest; std::deque params; params.push_back(u->nick); - params.push_back(ConvToStr(u->age)); params.push_back(text); - DoOneToMany(user->nick,"FMODE",params); + DoOneToMany(user->nick,"MODE",params); } else { chanrec* c = (chanrec*)dest; std::deque params; params.push_back(c->name); - params.push_back(ConvToStr(c->age)); params.push_back(text); - DoOneToMany(user->nick,"FMODE",params); + DoOneToMany(user->nick,"MODE",params); } } } -- cgit v1.2.3