X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fmodules%2Fm_spanningtree%2Ffjoin.cpp;h=faf5345427a6e2828a27e34c4d91e62fef015f89;hb=3624c137a6db85eaab0372550c9dca79d6d21e55;hp=3d119ee786bbc095accac2dc05e77db87d32333b;hpb=57608fe351cff19679b1d78fb5cbfb7cad89dfc1;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/modules/m_spanningtree/fjoin.cpp b/src/modules/m_spanningtree/fjoin.cpp index 3d119ee78..faf534542 100644 --- a/src/modules/m_spanningtree/fjoin.cpp +++ b/src/modules/m_spanningtree/fjoin.cpp @@ -1,30 +1,35 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ +/* + * InspIRCd -- Internet Relay Chat Daemon * - * InspIRCd: (C) 2002-2008 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits + * Copyright (C) 2009-2010 Daniel De Graaf + * Copyright (C) 2008 Robin Burchell + * Copyright (C) 2008 Dennis Friis + * Copyright (C) 2008 Craig Edwards * - * This program is free but copyrighted software; see - * the file COPYING for details. + * This file is part of InspIRCd. InspIRCd is free software: you can + * redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, version 2. * - * --------------------------------------------------- + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ -#include "inspircd.h" -#include "xline.h" - -#include "m_spanningtree/treesocket.h" -#include "m_spanningtree/treeserver.h" -#include "m_spanningtree/utils.h" - -/* $ModDep: m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */ +#include "inspircd.h" +#include "commands.h" +#include "treeserver.h" +#include "treesocket.h" -/** FJOIN, similar to TS6 SJOIN, but not quite. */ -bool TreeSocket::ForceJoin(const std::string &source, std::deque ¶ms) +/** FJOIN, almost identical to TS6 SJOIN, except for nicklist handling. */ +CmdResult CommandFJoin::Handle(const std::vector& params, User *srcuser) { - /* 1.1 FJOIN works as follows: + SpanningTreeUtilities* Utils = ((ModuleSpanningTree*)(Module*)creator)->Utils; + /* 1.1+ FJOIN works as follows: * * Each FJOIN is sent along with a timestamp, and the side with the lowest * timestamp 'wins'. From this point on we will refer to this side as the @@ -50,170 +55,174 @@ bool TreeSocket::ForceJoin(const std::string &source, std::deque &p * losing side, so only its own modes get applied. Life is simple for those * who succeed at internets. :-) * - * NOTE: Unlike TS6 and dreamforge and other protocols which have SJOIN, - * FJOIN does not contain the simple-modes such as +iklmnsp. Why not, - * you ask? Well, quite simply because we don't need to. They'll be sent - * after the FJOIN by FMODE, and FMODE is timestamped, so in the event - * the losing side sends any modes for the channel which shouldnt win, - * they wont as their timestamp will be too high :-) + * Syntax: + * : FJOIN :[[modes,] [[modes,] ... ]] + * The last parameter is a list consisting of zero or more (modelist, uuid) + * pairs (permanent channels may have zero users). The mode list for each + * user is a concatenation of the mode letters the user has on the channel + * (e.g.: "ov" if the user is opped and voiced). The order of the mode letters + * are not important but if a server ecounters an unknown mode letter, it will + * drop the link to avoid desync. + * + * InspIRCd 2.0 and older required a comma before the uuid even if the user + * had no prefix modes on the channel, InspIRCd 2.2 and later does not require + * a comma in this case anymore. + * */ - if (params.size() < 2) - return true; - - irc::modestacker modestack(true); /* Modes to apply from the users in the user list */ - User* who = NULL; /* User we are currently checking */ - std::string channel = params[0]; /* Channel name, as a string */ - time_t TS = atoi(params[1].c_str()); /* Timestamp given to us for remote side */ - irc::tokenstream users((params.size() > 2) ? params[2] : ""); /* users from the user list */ - bool apply_other_sides_modes = true; /* True if we are accepting the other side's modes */ - Channel* chan = this->Instance->FindChan(channel); /* The channel we're sending joins to */ - time_t ourTS = chan ? chan->age : Instance->Time()+600; /* The TS of our side of the link */ - bool created = !chan; /* True if the channel doesnt exist here yet */ - std::string item; /* One item in the list of nicks */ - - if (params.size() > 2) - params[2] = ":" + params[2]; - - Utils->DoOneToAllButSender(source,"FJOIN",params,source); - + time_t TS = ConvToInt(params[1]); if (!TS) { - Instance->Logs->Log("m_spanningtree",DEFAULT,"*** BUG? *** TS of 0 sent to FJOIN. Are some services authors smoking craq, or is it 1970 again?. Dropped."); - Instance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FJOIN with a TS of zero. Total craq. Command was dropped.", source.c_str()); - return true; + ServerInstance->Logs->Log("m_spanningtree", LOG_DEFAULT, "*** BUG? *** TS of 0 sent to FJOIN. Are some services authors smoking craq, or is it 1970 again?. Dropped."); + ServerInstance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FJOIN with a TS of zero. Total craq. Command was dropped.", srcuser->server.c_str()); + return CMD_INVALID; } - if (created) - chan = new Channel(Instance, channel, ourTS); - - /* If our TS is less than theirs, we dont accept their modes */ - if (ourTS < TS) - apply_other_sides_modes = false; + const std::string& channel = params[0]; + Channel* chan = ServerInstance->FindChan(channel); + bool apply_other_sides_modes = true; - /* Our TS greater than theirs, clear all our modes from the channel, accept theirs. */ - if (ourTS > TS) + if (!chan) { - std::deque param_list; - if (Utils->AnnounceTSChange && chan) - chan->WriteChannelWithServ(Instance->Config->ServerName, "NOTICE %s :TS for %s changed from %lu to %lu", chan->name, chan->name, (unsigned long) ourTS, (unsigned long) TS); - ourTS = TS; - if (!created) - { - chan->age = TS; - param_list.push_back(channel); - this->RemoveStatus(Instance->Config->GetSID(), param_list); - } + chan = new Channel(channel, TS); } - - /* Now, process every 'prefixes,nick' pair */ - while (users.GetToken(item)) + else { - const char* usr = item.c_str(); - if (usr && *usr) + time_t ourTS = chan->age; + if (TS != ourTS) { - const char* unparsedmodes = usr; - std::string modes; + ServerInstance->SNO->WriteToSnoMask('d', "Merge FJOIN received for %s, ourTS: %lu, TS: %lu, difference: %lu", + chan->name.c_str(), (unsigned long)ourTS, (unsigned long)TS, (unsigned long)(ourTS - TS)); + /* If our TS is less than theirs, we dont accept their modes */ + if (ourTS < TS) + { + apply_other_sides_modes = false; + } + else if (ourTS > TS) + { + /* Our TS greater than theirs, clear all our modes from the channel, accept theirs. */ + if (Utils->AnnounceTSChange) + chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :TS for %s changed from %lu to %lu", chan->name.c_str(), channel.c_str(), (unsigned long) ourTS, (unsigned long) TS); + // while the name is equal in case-insensitive compare, it might differ in case; use the remote version + chan->name = channel; + chan->age = TS; + chan->ClearInvites(); - /* Iterate through all modes for this user and check they are valid. */ - while ((*unparsedmodes) && (*unparsedmodes != ',')) - { - ModeHandler *mh = Instance->Modes->FindMode(*unparsedmodes, MODETYPE_CHANNEL); - if (mh) - modes += *unparsedmodes; - else + CommandFJoin::RemoveStatus(chan); + + // XXX: If the channel does not exist in the chan hash at this point, create it so the remote modes can be applied on it. + // This happens to 0-user permanent channels on the losing side, because those are removed (from the chan hash, then + // deleted later) as soon as the permchan mode is removed from them. + if (ServerInstance->FindChan(channel) == NULL) { - this->SendError(std::string("Invalid prefix '")+(*unparsedmodes)+"' in FJOIN"); - return false; + chan = new Channel(channel, TS); } - - usr++; - unparsedmodes++; } + } + } - /* Advance past the comma, to the nick */ - usr++; - - /* Check the user actually exists */ - who = this->Instance->FindUUID(usr); - if (who) - { - /* Check that the user's 'direction' is correct */ - TreeServer* route_back_again = Utils->BestRouteTo(who->server); - if ((!route_back_again) || (route_back_again->GetSocket() != this)) - continue; + /* First up, apply their channel modes if they won the TS war */ + if (apply_other_sides_modes) + { + std::vector modelist; + modelist.push_back(channel); - /* Add any modes this user had to the mode stack */ - for (std::string::iterator x = modes.begin(); x != modes.end(); ++x) - modestack.Push(*x, who->nick); + /* Remember, params[params.size() - 1] is userlist, and we don't want to apply *that* */ + modelist.insert(modelist.end(), params.begin()+2, params.end()-1); + ServerInstance->Modes->Process(modelist, srcuser, ModeParser::MODE_LOCALONLY | ModeParser::MODE_MERGE); + } - Channel::JoinUser(this->Instance, who, channel.c_str(), true, "", true, TS); - } - else - { - Instance->Logs->Log("m_spanningtree",SPARSE,"Warning! Invalid user %s in FJOIN to channel %s IGNORED", usr, channel.c_str()); - continue; - } - } + irc::modestacker modestack(true); + TreeSocket* src_socket = Utils->FindServer(srcuser->server)->GetRoute()->GetSocket(); + + /* Now, process every 'modes,uuid' pair */ + irc::tokenstream users(*params.rbegin()); + std::string item; + irc::modestacker* modestackptr = (apply_other_sides_modes ? &modestack : NULL); + while (users.GetToken(item)) + { + if (!ProcessModeUUIDPair(item, src_socket, chan, modestackptr)) + return CMD_INVALID; } /* Flush mode stacker if we lost the FJOIN or had equal TS */ if (apply_other_sides_modes) + CommandFJoin::ApplyModeStack(srcuser, chan, modestack); + + return CMD_SUCCESS; +} + +bool CommandFJoin::ProcessModeUUIDPair(const std::string& item, TreeSocket* src_socket, Channel* chan, irc::modestacker* modestack) +{ + std::string::size_type comma = item.find(','); + + // Comma not required anymore if the user has no modes + std::string uuid = ((comma == std::string::npos) ? item : item.substr(comma+1)); + User* who = ServerInstance->FindUUID(uuid); + if (!who) { - std::deque stackresult; - const char* mode_junk[MAXMODES+2]; - mode_junk[0] = channel.c_str(); + // Probably KILLed, ignore + return true; + } + + /* Check that the user's 'direction' is correct */ + SpanningTreeUtilities* Utils = ((ModuleSpanningTree*)(Module*)creator)->Utils; + TreeServer* route_back_again = Utils->BestRouteTo(who->server); + if ((!route_back_again) || (route_back_again->GetSocket() != src_socket)) + { + return true; + } - while (modestack.GetStackedLine(stackresult)) + /* Check if the user received at least one mode */ + if ((modestack) && (comma > 0) && (comma != std::string::npos)) + { + /* Iterate through the modes and see if they are valid here, if so, apply */ + std::string::const_iterator commait = item.begin()+comma; + for (std::string::const_iterator i = item.begin(); i != commait; ++i) { - for (size_t j = 0; j < stackresult.size(); j++) + if (!ServerInstance->Modes->FindMode(*i, MODETYPE_CHANNEL)) { - mode_junk[j+1] = stackresult[j].c_str(); + ServerInstance->SNO->WriteToSnoMask('d', "Unrecognised mode '%c' for a user in FJOIN, dropping link", *i); + return false; } - Instance->SendMode(mode_junk, stackresult.size() + 1, Instance->FakeClient); + + /* Add any modes this user had to the mode stack */ + modestack->Push(*i, who->nick); } } + chan->ForceJoin(who, NULL, route_back_again->bursting); return true; } -/** TODO: This creates a total mess of output and needs to really use irc::modestacker. - */ -bool TreeSocket::RemoveStatus(const std::string &prefix, std::deque ¶ms) +void CommandFJoin::RemoveStatus(Channel* c) { - if (params.size() < 1) - return true; - - Channel* c = Instance->FindChan(params[0]); + irc::modestacker stack(false); - if (c) + for (char modeletter = 'A'; modeletter <= 'z'; ++modeletter) { - irc::modestacker stack(false); - std::deque stackresult; - const char* mode_junk[MAXMODES+2]; - mode_junk[0] = c->name; + ModeHandler* mh = ServerInstance->Modes->FindMode(modeletter, MODETYPE_CHANNEL); + + /* Passing a pointer to a modestacker here causes the mode to be put onto the mode stack, + * rather than applied immediately. Module unloads require this to be done immediately, + * for this function we require tidyness instead. Fixes bug #493 + */ + if (mh) + mh->RemoveMode(c, stack); + } - for (char modeletter = 'A'; modeletter <= 'z'; ++modeletter) - { - ModeHandler* mh = Instance->Modes->FindMode(modeletter, MODETYPE_CHANNEL); - - /* Passing a pointer to a modestacker here causes the mode to be put onto the mode stack, - * rather than applied immediately. Module unloads require this to be done immediately, - * for this function we require tidyness instead. Fixes bug #493 - */ - if (mh) - mh->RemoveMode(c, &stack); - } + ApplyModeStack(ServerInstance->FakeClient, c, stack); +} - while (stack.GetStackedLine(stackresult)) - { - for (size_t j = 0; j < stackresult.size(); j++) - mode_junk[j+1] = stackresult[j].c_str(); +void CommandFJoin::ApplyModeStack(User* srcuser, Channel* c, irc::modestacker& stack) +{ + parameterlist stackresult; + stackresult.push_back(c->name); - Instance->SendMode(mode_junk, stackresult.size() + 1, Instance->FakeClient); - } + while (stack.GetStackedLine(stackresult)) + { + ServerInstance->Modes->Process(stackresult, srcuser, ModeParser::MODE_LOCALONLY); + stackresult.erase(stackresult.begin() + 1, stackresult.end()); } - return true; } -