X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fmodules%2Fm_spanningtree.cpp;h=7e7e91382e44cad7cc3e2c6d0e31bf60e65cbf8c;hb=8144a16f89b75f527e4d860e7176c9c1a8b14eca;hp=f9a89bf5ca5c01a6cd105f5b0bfa0182bcec5ed3;hpb=af42b46c28af610f918ba15869a84ecb228e423e;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/modules/m_spanningtree.cpp b/src/modules/m_spanningtree.cpp index f9a89bf5c..7e7e91382 100644 --- a/src/modules/m_spanningtree.cpp +++ b/src/modules/m_spanningtree.cpp @@ -18,32 +18,27 @@ using namespace std; -#include -#include -#include -#include "globals.h" -#include "inspircd_config.h" -#include "hash_map.h" #include "configreader.h" #include "users.h" #include "channels.h" #include "modules.h" -#include "commands.h" #include "commands/cmd_whois.h" #include "commands/cmd_stats.h" #include "socket.h" - #include "inspircd.h" #include "wildcard.h" -#include "inspstring.h" -#include "hashcomp.h" #include "xline.h" -#include "typedefs.h" #include "cull_list.h" #include "aes.h" #define nspace __gnu_cxx + +/** If you make a change which breaks the protocol, increment this. + * If you completely change the protocol, completely change the number. + */ +const long ProtocolVersion = 1101; + /* * The server list in InspIRCd is maintained as two structures * which hold the data in different ways. Most of the time, we @@ -71,7 +66,7 @@ class ModuleSpanningTree; static ModuleSpanningTree* TreeProtocolModule; static InspIRCd* ServerInstance; -/* Any socket can have one of five states at any one time. +/** Any socket can have one of five states at any one time. * The LISTENER state indicates a socket which is listening * for connections. It cannot receive data itself, only incoming * sockets. @@ -170,7 +165,7 @@ class UserManager : public classbase }; -/* Each server in the tree is represented by one class of +/** Each server in the tree is represented by one class of * type TreeServer. A locally connected TreeServer can * have a class of type TreeSocket associated with it, for * remote servers, the TreeSocket entry will be NULL. @@ -184,7 +179,6 @@ class UserManager : public classbase * TreeServer items, deleting and inserting them as they * are created and destroyed. */ - class TreeServer : public classbase { InspIRCd* ServerInstance; /* Creator */ @@ -202,7 +196,7 @@ class TreeServer : public classbase public: - /* We don't use this constructor. Its a dummy, and won't cause any insertion + /** We don't use this constructor. Its a dummy, and won't cause any insertion * of the TreeServer into the hash_map. See below for the two we DO use. */ TreeServer(InspIRCd* Instance) : ServerInstance(Instance) @@ -215,7 +209,7 @@ class TreeServer : public classbase VersionString = ServerInstance->GetVersionString(); } - /* We use this constructor only to create the 'root' item, TreeRoot, which + /** We use this constructor only to create the 'root' item, TreeRoot, which * represents our own server. Therefore, it has no route, no parent, and * no socket associated with it. Its version string is our own local version. */ @@ -230,7 +224,7 @@ class TreeServer : public classbase AddHashEntry(); } - /* When we create a new server, we call this constructor to initialize it. + /** When we create a new server, we call this constructor to initialize it. * This constructor initializes the server's Route and Parent, and sets up * its ping counters so that it will be pinged one minute from now. */ @@ -318,7 +312,7 @@ class TreeServer : public classbase return time_to_die.size(); } - /* This method is used to add the structure to the + /** This method is used to add the structure to the * hash_map for linear searches. It is only called * by the constructors. */ @@ -330,7 +324,7 @@ class TreeServer : public classbase serverlist[this->ServerName.c_str()] = this; } - /* This method removes the reference to this object + /** This method removes the reference to this object * from the hash_map which is used for linear searches. * It is only called by the default destructor. */ @@ -342,10 +336,9 @@ class TreeServer : public classbase serverlist.erase(iter); } - /* These accessors etc should be pretty self- + /** These accessors etc should be pretty self- * explanitory. */ - TreeServer* GetRoute() { return Route; @@ -462,7 +455,7 @@ class TreeServer : public classbase return false; } - /* Removes child nodes of this node, and of that node, etc etc. + /** Removes child nodes of this node, and of that node, etc etc. * This is used during netsplits to automatically tidy up the * server tree. It is slow, we don't use it for much else. */ @@ -492,27 +485,30 @@ class TreeServer : public classbase } }; -/* The Link class might as well be a struct, +/** The Link class might as well be a struct, * but this is C++ and we don't believe in structs (!). * It holds the entire information of one * tag from the main config file. We maintain a list * of them, and populate the list on rehash/load. */ - class Link : public classbase { public: - irc::string Name; - std::string IPAddr; - int Port; - std::string SendPass; - std::string RecvPass; - unsigned long AutoConnect; - time_t NextConnectTime; - std::string EncryptionKey; - bool HiddenFromStats; + irc::string Name; + std::string IPAddr; + int Port; + std::string SendPass; + std::string RecvPass; + unsigned long AutoConnect; + time_t NextConnectTime; + std::string EncryptionKey; + bool HiddenFromStats; + irc::string FailOver; }; +void DoFailOver(Link* x); +Link* FindLink(const std::string& name); + /* The usual stuff for inspircd modules, * plus the vector of Link classes which we * use to store the tags from the config @@ -521,7 +517,7 @@ class Link : public classbase ConfigReader *Conf; std::vector LinkBlocks; -/* Yay for fast searches! +/** Yay for fast searches! * This is hundreds of times faster than recursion * or even scanning a linked list, especially when * there are more than a few servers to deal with. @@ -541,7 +537,7 @@ TreeServer* FindServer(std::string ServerName) } } -/* Returns the locally connected server we must route a +/** Returns the locally connected server we must route a * message through to reach server 'ServerName'. This * only applies to one-to-one and not one-to-many routing. * See the comments for the constructor of TreeServer @@ -562,7 +558,7 @@ TreeServer* BestRouteTo(std::string ServerName) } } -/* Find the first server matching a given glob mask. +/** Find the first server matching a given glob mask. * Theres no find-using-glob method of hash_map [awwww :-(] * so instead, we iterate over the list using an iterator * and match each one until we get a hit. Yes its slow, @@ -595,24 +591,29 @@ class cmd_rconnect : public command_t syntax = " "; } - void Handle (const char** parameters, int pcnt, userrec *user) + CmdResult Handle (const char** parameters, int pcnt, userrec *user) { user->WriteServ("NOTICE %s :*** RCONNECT: Sending remote connect to \002%s\002 to connect server \002%s\002.",user->nick,parameters[0],parameters[1]); /* Is this aimed at our server? */ if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameters[0])) { /* Yes, initiate the given connect */ - ServerInstance->WriteOpers("*** Remote CONNECT from %s matching \002%s\002, connecting server \002%s\002",user->nick,parameters[0],parameters[1]); + ServerInstance->SNO->WriteToSnoMask('l',"Remote CONNECT from %s matching \002%s\002, connecting server \002%s\002",user->nick,parameters[0],parameters[1]); const char* para[1]; para[0] = parameters[1]; - Creator->OnPreCommand("CONNECT", para, 1, user, true); + std::string original_command = std::string("CONNECT ") + parameters[1]; + Creator->OnPreCommand("CONNECT", para, 1, user, true, original_command); + + return CMD_SUCCESS; } + + return CMD_FAILURE; } }; -/* Every SERVER connection inbound or outbound is represented by +/** Every SERVER connection inbound or outbound is represented by * an object of type TreeSocket. * TreeSockets, being inherited from InspSocket, can be tied into * the core socket engine, and we cn therefore receive activity events @@ -623,7 +624,6 @@ class cmd_rconnect : public command_t * maintain a list of servers, some of which are directly connected, * some of which are not. */ - class TreeSocket : public InspSocket { std::string myhost; @@ -644,7 +644,7 @@ class TreeSocket : public InspSocket public: - /* Because most of the I/O gubbins are encapsulated within + /** Because most of the I/O gubbins are encapsulated within * InspSocket, we just call the superclass constructor for * most of the action, and append a few of our own values * to it. @@ -667,7 +667,7 @@ class TreeSocket : public InspSocket this->ctx_out = NULL; } - /* When a listening socket gives us a new file descriptor, + /** When a listening socket gives us a new file descriptor, * we must associate it with a socket without creating a new * connection. This constructor is used for this purpose. */ @@ -695,17 +695,17 @@ class TreeSocket : public InspSocket ctx_in = new AES(); ctx_out = new AES(); - ServerInstance->Log(DEBUG,"Initialized AES key %s",key.c_str()); + Instance->Log(DEBUG,"Initialized AES key %s",key.c_str()); // key must be 16, 24, 32 etc bytes (multiple of 8) keylength = key.length(); if (!(keylength == 16 || keylength == 24 || keylength == 32)) { - this->Instance->WriteOpers("*** \2ERROR\2: Key length for encryptionkey is not 16, 24 or 32 bytes in length!"); - ServerInstance->Log(DEBUG,"Key length not 16, 24 or 32 characters!"); + this->Instance->SNO->WriteToSnoMask('l',"\2ERROR\2: Key length for encryptionkey is not 16, 24 or 32 bytes in length!"); + Instance->Log(DEBUG,"Key length not 16, 24 or 32 characters!"); } else { - this->Instance->WriteOpers("*** \2AES\2: Initialized %d bit encryption to server %s",keylength*8,SName.c_str()); + this->Instance->SNO->WriteToSnoMask('l',"\2AES\2: Initialized %d bit encryption to server %s",keylength*8,SName.c_str()); ctx_in->MakeKey(key.c_str(), "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", keylength, keylength); ctx_out->MakeKey(key.c_str(), "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ @@ -713,7 +713,7 @@ class TreeSocket : public InspSocket } } - /* When an outbound connection finishes connecting, we receive + /** When an outbound connection finishes connecting, we receive * this event, and must send our SERVER string to the other * side. If the other side is happy, as outlined in the server * to server docs on the inspircd.org site, the other side @@ -728,13 +728,13 @@ class TreeSocket : public InspSocket { if (x->Name == this->myhost) { - this->Instance->WriteOpers("*** Connection to \2"+myhost+"\2["+(x->HiddenFromStats ? "" : this->GetIP())+"] established."); + this->Instance->SNO->WriteToSnoMask('l',"Connection to \2"+myhost+"\2["+(x->HiddenFromStats ? "" : this->GetIP())+"] started."); this->SendCapabilities(); if (x->EncryptionKey != "") { if (!(x->EncryptionKey.length() == 16 || x->EncryptionKey.length() == 24 || x->EncryptionKey.length() == 32)) { - this->Instance->WriteOpers("\2WARNING\2: Your encryption key is NOT 16, 24 or 32 characters in length, encryption will \2NOT\2 be enabled."); + this->Instance->SNO->WriteToSnoMask('l',"\2WARNING\2: Your encryption key is NOT 16, 24 or 32 characters in length, encryption will \2NOT\2 be enabled."); } else { @@ -753,7 +753,7 @@ class TreeSocket : public InspSocket * If that happens the connection hangs here until it's closed. Unlikely * and rather harmless. */ - this->Instance->WriteOpers("*** Connection to \2"+myhost+"\2 lost link tag(!)"); + this->Instance->SNO->WriteToSnoMask('l',"Connection to \2"+myhost+"\2 lost link tag(!)"); return true; } @@ -765,7 +765,10 @@ class TreeSocket : public InspSocket */ if (e == I_ERR_CONNECT) { - this->Instance->WriteOpers("*** Connection failed: Connection refused"); + this->Instance->SNO->WriteToSnoMask('l',"Connection failed: Connection to \002"+myhost+"\002 refused"); + Link* MyLink = FindLink(myhost); + if (MyLink) + DoFailOver(MyLink); } } @@ -777,7 +780,7 @@ class TreeSocket : public InspSocket return true; } - /* Recursively send the server tree with distances as hops. + /** Recursively send the server tree with distances as hops. * This is used during network burst to inform the other server * (and any of ITS servers too) of what servers we know about. * If at any point any of these servers already exist on the other @@ -810,7 +813,7 @@ class TreeSocket : public InspSocket for (int i = 0; i <= this->Instance->GetModuleCount(); i++) { - if ((this->Instance->modules[i]->GetVersion().Flags & VF_STATIC) || (this->Instance->modules[i]->GetVersion().Flags & VF_COMMON)) + if (this->Instance->modules[i]->GetVersion().Flags & VF_COMMON) modlist.push_back(this->Instance->Config->module_names[i]); } sort(modlist.begin(),modlist.end()); @@ -856,7 +859,7 @@ class TreeSocket : public InspSocket #ifdef SUPPORT_IP6LINKS ip6support = 1; #endif - this->WriteLine("CAPAB CAPABILITIES :NICKMAX="+ConvToStr(NICKMAX)+" HALFOP="+ConvToStr(this->Instance->Config->AllowHalfop)+" CHANMAX="+ConvToStr(CHANMAX)+" MAXMODES="+ConvToStr(MAXMODES)+" IDENTMAX="+ConvToStr(IDENTMAX)+" MAXQUIT="+ConvToStr(MAXQUIT)+" MAXTOPIC="+ConvToStr(MAXTOPIC)+" MAXKICK="+ConvToStr(MAXKICK)+" MAXGECOS="+ConvToStr(MAXGECOS)+" MAXAWAY="+ConvToStr(MAXAWAY)+" IP6NATIVE="+ConvToStr(ip6)+" IP6SUPPORT="+ConvToStr(ip6support)); + this->WriteLine("CAPAB CAPABILITIES :NICKMAX="+ConvToStr(NICKMAX)+" HALFOP="+ConvToStr(this->Instance->Config->AllowHalfop)+" CHANMAX="+ConvToStr(CHANMAX)+" MAXMODES="+ConvToStr(MAXMODES)+" IDENTMAX="+ConvToStr(IDENTMAX)+" MAXQUIT="+ConvToStr(MAXQUIT)+" MAXTOPIC="+ConvToStr(MAXTOPIC)+" MAXKICK="+ConvToStr(MAXKICK)+" MAXGECOS="+ConvToStr(MAXGECOS)+" MAXAWAY="+ConvToStr(MAXAWAY)+" IP6NATIVE="+ConvToStr(ip6)+" IP6SUPPORT="+ConvToStr(ip6support)+" PROTOCOL="+ConvToStr(ProtocolVersion)); this->WriteLine("CAPAB END"); } @@ -942,6 +945,18 @@ class TreeSocket : public InspSocket if (((this->CapKeys.find("NICKMAX") == this->CapKeys.end()) || ((this->CapKeys.find("NICKMAX") != this->CapKeys.end()) && (this->CapKeys.find("NICKMAX")->second != ConvToStr(NICKMAX))))) reason = "Maximum nickname lengths differ or remote nickname length not specified"; + if (((this->CapKeys.find("PROTOCOL") == this->CapKeys.end()) || ((this->CapKeys.find("PROTOCOL") != this->CapKeys.end()) && (this->CapKeys.find("PROTOCOL")->second != ConvToStr(ProtocolVersion))))) + { + if (this->CapKeys.find("PROTOCOL") != this->CapKeys.end()) + { + reason = "Mismatched protocol versions "+this->CapKeys.find("PROTOCOL")->second+" and "+ConvToStr(ProtocolVersion); + } + else + { + reason = "Protocol version not specified"; + } + } + if (((this->CapKeys.find("HALFOP") == this->CapKeys.end()) && (Instance->Config->AllowHalfop)) || ((this->CapKeys.find("HALFOP") != this->CapKeys.end()) && (this->CapKeys.find("HALFOP")->second != ConvToStr(Instance->Config->AllowHalfop)))) reason = "We don't both have halfop support enabled/disabled identically"; @@ -1008,7 +1023,7 @@ class TreeSocket : public InspSocket return true; } - /* This function forces this server to quit, removing this server + /** This function forces this server to quit, removing this server * and any users on it (and servers and users below that, etc etc). * It's very slow and pretty clunky, but luckily unless your network * is having a REAL bad hair day, this function shouldnt be called @@ -1030,7 +1045,7 @@ class TreeSocket : public InspSocket num_lost_users += Current->QuitUsers(from); } - /* This is a wrapper function for SquitServer above, which + /** This is a wrapper function for SquitServer above, which * does some validation first and passes on the SQUIT to all * other remaining servers. */ @@ -1061,11 +1076,11 @@ class TreeSocket : public InspSocket } else { - ServerInstance->Log(DEFAULT,"Squit from unknown server"); + Instance->Log(DEFAULT,"Squit from unknown server"); } } - /* FMODE command - server mode with timestamp checks */ + /** FMODE command - server mode with timestamp checks */ bool ForceMode(std::string source, std::deque ¶ms) { /* Chances are this is a 1.0 FMODE without TS */ @@ -1136,7 +1151,7 @@ class TreeSocket : public InspSocket */ if (TS == ourTS) { - ServerInstance->Log(DEBUG,"Entering TS equality check"); + Instance->Log(DEBUG,"Entering TS equality check"); ModeHandler* mh = NULL; unsigned long paramptr = 3; std::string to_bounce = ""; @@ -1246,7 +1261,7 @@ class TreeSocket : public InspSocket if ((mh->GetNumParams(adding) > 0) && (paramptr < params.size())) { - ServerInstance->Log(DEBUG,"Mode removal %d %d",adding, mh->GetNumParams(adding)); + Instance->Log(DEBUG,"Mode removal %d %d",adding, mh->GetNumParams(adding)); params_to_keep.push_back(params[paramptr++]); } } @@ -1280,6 +1295,7 @@ class TreeSocket : public InspSocket newparams.push_back(params[0]); newparams.push_back(ConvToStr(ourTS)); newparams.push_back(to_bounce+params_to_bounce); + Instance->Log(DEBUG,"BOUNCE BACK: %s",(to_bounce+params_to_bounce).c_str()); DoOneToOne(this->Instance->Config->ServerName,"FMODE",newparams,sourceserv); } @@ -1290,23 +1306,23 @@ class TreeSocket : public InspSocket modelist[0] = params[0].c_str(); modelist[1] = to_keep.c_str(); - if (params_to_keep.size() > 2) + if (params_to_keep.size() > 1) { - for (q = 2; (q < params_to_keep.size()) && (q < 64); q++) + for (q = 0; (q < params_to_keep.size()) && (q < 64); q++) { - ServerInstance->Log(DEBUG,"Item %d of %d", q, params_to_keep.size()); + Instance->Log(DEBUG,"KEEP Item %d of %d: %s", q, params_to_keep.size(), params_to_keep[q].c_str()); modelist[n++] = params_to_keep[q].c_str(); } } if (smode) { - ServerInstance->Log(DEBUG,"Send mode"); + Instance->Log(DEBUG,"Send mode"); this->Instance->SendMode(modelist, n+2, who); } else { - ServerInstance->Log(DEBUG,"Send mode client"); + Instance->Log(DEBUG,"Send mode client"); this->Instance->CallCommandHandler("MODE", modelist, n+2, who); } @@ -1421,11 +1437,11 @@ class TreeSocket : public InspSocket newparams[2] = modebounce; /* Only send it back the way it came, no need to send it anywhere else */ DoOneToOne(this->Instance->Config->ServerName,"FMODE",newparams,sourceserv); - ServerInstance->Log(DEBUG,"FMODE bounced intelligently, our TS less than theirs and the other server is NOT a uline."); + Instance->Log(DEBUG,"FMODE bounced intelligently, our TS less than theirs and the other server is NOT a uline."); } else { - ServerInstance->Log(DEBUG,"Allow modes, TS lower for sender"); + Instance->Log(DEBUG,"Allow modes, TS lower for sender"); /* The server was ulined, but something iffy is up with the TS. * Sound the alarm bells! */ @@ -1449,7 +1465,7 @@ class TreeSocket : public InspSocket return true; } - /* FTOPIC command */ + /** FTOPIC command */ bool ForceTopic(std::string source, std::deque ¶ms) { if (params.size() != 4) @@ -1493,7 +1509,7 @@ class TreeSocket : public InspSocket return true; } - /* FJOIN, similar to unreal SJOIN */ + /** FJOIN, similar to unreal SJOIN */ bool ForceJoin(std::string source, std::deque ¶ms) { if (params.size() < 3) @@ -1530,7 +1546,23 @@ class TreeSocket : public InspSocket ourTS = us->age; } - ServerInstance->Log(DEBUG,"FJOIN detected, our TS=%lu, their TS=%lu",ourTS,TS); + /* XXX: PAY ATTENTION: + * In 1.1, if they have the newer channel, we immediately clear + * all status modes from our users. We then accept their modes. + * If WE have the newer channel its the other side's job to do this. + * Note that this causes the losing server to send out confirming + * FMODE lines. + */ + if ((ourTS > TS) || (this->Instance->ULine(source.c_str()))) + { + Instance->Log(DEBUG,"FJOIN detected, our TS=%lu, their TS=%lu",ourTS,TS); + std::deque param_list; + us->age = TS; + ourTS = TS; + param_list.push_back(chan->name); + Instance->Log(DEBUG,"REMOVE ALL STATUS MODES FROM OUR USERS *NOW*"); + this->RemoveStatus(Instance->Config->ServerName, param_list); + } irc::tokenstream users(params[2]); std::string item = "*"; @@ -1553,7 +1585,7 @@ class TreeSocket : public InspSocket int ntimes = 0; while ((*permissions) && (*permissions != ',')) { - ModeHandler* mh = ServerInstance->Modes->FindPrefix(*permissions); + ModeHandler* mh = Instance->Modes->FindPrefix(*permissions); if (mh) { ntimes++; @@ -1572,7 +1604,7 @@ class TreeSocket : public InspSocket /* Did they get any modes? How many times? */ for (int k = 0; k < ntimes; k++) - mode_users[modectr++] = strdup(usr); // XXX + mode_users[modectr++] = strdup(usr); who = this->Instance->FindNick(usr); if (who) @@ -1586,34 +1618,15 @@ class TreeSocket : public InspSocket if ((ourTS >= TS) || (this->Instance->ULine(who->server))) { /* We also always let u-lined clients win, no matter what the TS value */ - ServerInstance->Log(DEBUG,"Our our channel newer than theirs, accepting their modes"); + Instance->Log(DEBUG,"Our our channel newer than theirs, accepting their modes"); this->Instance->SendMode((const char**)mode_users,modectr,who); if (ourTS != TS) { - ServerInstance->Log(DEFAULT,"Channel TS for %s changed from %lu to %lu",us->name,ourTS,TS); + Instance->Log(DEFAULT,"Channel TS for %s changed from %lu to %lu",us->name,ourTS,TS); us->age = TS; ourTS = TS; } } - else - { - ServerInstance->Log(DEBUG,"Their channel newer than ours, bouncing their modes"); - /* bouncy bouncy! */ - std::deque params; - /* modes are now being UNSET... */ - *mode_users[1] = '-'; - for (unsigned int x = 0; x < modectr; x++) - { - if (x == 1) - { - params.push_back(ConvToStr(us->age)); - } - params.push_back(mode_users[x]); - - } - // tell everyone to bounce the modes. bad modes, bad! - DoOneToMany(this->Instance->Config->ServerName,"FMODE",params); - } strcpy(mode_users[1],"+"); for (unsigned int f = 2; f < modectr; f++) free(mode_users[f]); @@ -1622,11 +1635,8 @@ class TreeSocket : public InspSocket } else { - for (unsigned int f = 2; f < modectr; f++) - free(mode_users[f]); - - this->WriteLine("ERROR :Invalid user '"+std::string(usr)+"' in FJOIN to '"+channel+"'"); - return false; + Instance->Log(SPARSE,"Warning! Invalid user in FJOIN to channel %s IGNORED", channel.c_str()); + continue; } } } @@ -1637,30 +1647,15 @@ class TreeSocket : public InspSocket { if (ourTS >= TS) { - ServerInstance->Log(DEBUG,"Our our channel newer than theirs, accepting their modes"); + Instance->Log(DEBUG,"Our our channel newer than theirs, accepting their modes"); this->Instance->SendMode((const char**)mode_users,modectr,who); if (ourTS != TS) { - ServerInstance->Log(DEFAULT,"Channel TS for %s changed from %lu to %lu",us->name,ourTS,TS); + Instance->Log(DEFAULT,"Channel TS for %s changed from %lu to %lu",us->name,ourTS,TS); us->age = TS; ourTS = TS; } } - else - { - ServerInstance->Log(DEBUG,"Their channel newer than ours, bouncing their modes"); - std::deque params; - *mode_users[1] = '-'; - for (unsigned int x = 0; x < modectr; x++) - { - if (x == 1) - { - params.push_back(ConvToStr(us->age)); - } - params.push_back(mode_users[x]); - } - DoOneToMany(this->Instance->Config->ServerName,"FMODE",params); - } for (unsigned int f = 2; f < modectr; f++) free(mode_users[f]); @@ -1668,27 +1663,7 @@ class TreeSocket : public InspSocket return true; } - bool SyncChannelTS(std::string source, std::deque ¶ms) - { - if (params.size() >= 2) - { - chanrec* c = this->Instance->FindChan(params[0]); - if (c) - { - time_t theirTS = atoi(params[1].c_str()); - time_t ourTS = c->age; - if (ourTS >= theirTS) - { - ServerInstance->Log(DEBUG,"Updating timestamp for %s, our timestamp was %lu and theirs is %lu",c->name,ourTS,theirTS); - c->age = theirTS; - } - } - } - DoOneToAllButSender(this->Instance->Config->ServerName,"SYNCTS",params,source); - return true; - } - - /* NICK command */ + /** NICK command */ bool IntroduceClient(std::string source, std::deque ¶ms) { if (params.size() < 8) @@ -1705,17 +1680,19 @@ class TreeSocket : public InspSocket /* This used to have a pretty craq'y loop doing the same thing, * now we just let the STL do the hard work (more efficiently) */ - params[5] = params[5].substr(params[5].find_first_not_of('+')); + std::string::size_type pos_after_plus = params[5].find_first_not_of('+'); + if (pos_after_plus != std::string::npos) + params[5] = params[5].substr(pos_after_plus); const char* tempnick = params[1].c_str(); - ServerInstance->Log(DEBUG,"Introduce client %s!%s@%s",tempnick,params[4].c_str(),params[2].c_str()); + Instance->Log(DEBUG,"Introduce client %s!%s@%s",tempnick,params[4].c_str(),params[2].c_str()); user_hash::iterator iter = this->Instance->clientlist.find(tempnick); if (iter != this->Instance->clientlist.end()) { // nick collision - ServerInstance->Log(DEBUG,"Nick collision on %s!%s@%s: %lu %lu",tempnick,params[4].c_str(),params[2].c_str(),(unsigned long)age,(unsigned long)iter->second->age); + Instance->Log(DEBUG,"Nick collision on %s!%s@%s: %lu %lu",tempnick,params[4].c_str(),params[2].c_str(),(unsigned long)age,(unsigned long)iter->second->age); this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+tempnick+" :Nickname collision"); return true; } @@ -1735,12 +1712,14 @@ class TreeSocket : public InspSocket for (std::string::iterator v = params[5].begin(); v != params[5].end(); v++) _new->modes[(*v)-65] = 1; +#ifdef SUPPORT_IP6LINKS if (params[6].find_first_of(":") != std::string::npos) _new->SetSockAddr(AF_INET6, params[6].c_str(), 0); else +#endif _new->SetSockAddr(AF_INET, params[6].c_str(), 0); - this->Instance->WriteOpers("*** Client connecting at %s: %s!%s@%s [%s]",_new->server,_new->nick,_new->ident,_new->host, _new->GetIPString()); + this->Instance->SNO->WriteToSnoMask('C',"Client connecting at %s: %s!%s@%s [%s]",_new->server,_new->nick,_new->ident,_new->host, _new->GetIPString()); params[7] = ":" + params[7]; DoOneToAllButSender(source,"NICK",params,source); @@ -1749,20 +1728,20 @@ class TreeSocket : public InspSocket TreeServer* SourceServer = FindServer(source); if (SourceServer) { - ServerInstance->Log(DEBUG,"Found source server of %s",_new->nick); + Instance->Log(DEBUG,"Found source server of %s",_new->nick); SourceServer->AddUserCount(); } return true; } - /* Send one or more FJOINs for a channel of users. + /** Send one or more FJOINs for a channel of users. * If the length of a single line is more than 480-NICKMAX * in length, it is split over multiple lines. */ void SendFJoins(TreeServer* Current, chanrec* c) { - ServerInstance->Log(DEBUG,"Sending FJOINs to other server for %s",c->name); + Instance->Log(DEBUG,"Sending FJOINs to other server for %s",c->name); char list[MAXBUF]; std::string individual_halfops = std::string(":")+this->Instance->Config->ServerName+" FMODE "+c->name+" "+ConvToStr(c->age); @@ -1808,7 +1787,7 @@ class TreeSocket : public InspSocket this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" FMODE "+c->name+" "+ConvToStr(c->age)+" +"+c->ChanModes(true)+modes+" "+params); } - /* Send G, Q, Z and E lines */ + /** Send G, Q, Z and E lines */ void SendXLines(TreeServer* Current) { char data[MAXBUF]; @@ -1816,49 +1795,49 @@ class TreeSocket : public InspSocket const char* sn = n.c_str(); int iterations = 0; /* Yes, these arent too nice looking, but they get the job done */ - for (std::vector::iterator i = Instance->XLines->zlines.begin(); i != Instance->XLines->zlines.end(); i++, iterations++) + for (std::vector::iterator i = Instance->XLines->zlines.begin(); i != Instance->XLines->zlines.end(); i++, iterations++) { - snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s",sn,i->ipaddr,i->source,(unsigned long)i->set_time,(unsigned long)i->duration,i->reason); + snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s",sn,(*i)->ipaddr,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); this->WriteLine(data); } - for (std::vector::iterator i = Instance->XLines->qlines.begin(); i != Instance->XLines->qlines.end(); i++, iterations++) + for (std::vector::iterator i = Instance->XLines->qlines.begin(); i != Instance->XLines->qlines.end(); i++, iterations++) { - snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s",sn,i->nick,i->source,(unsigned long)i->set_time,(unsigned long)i->duration,i->reason); + snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s",sn,(*i)->nick,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); this->WriteLine(data); } - for (std::vector::iterator i = Instance->XLines->glines.begin(); i != Instance->XLines->glines.end(); i++, iterations++) + for (std::vector::iterator i = Instance->XLines->glines.begin(); i != Instance->XLines->glines.end(); i++, iterations++) { - snprintf(data,MAXBUF,":%s ADDLINE G %s %s %lu %lu :%s",sn,i->hostmask,i->source,(unsigned long)i->set_time,(unsigned long)i->duration,i->reason); + snprintf(data,MAXBUF,":%s ADDLINE G %s %s %lu %lu :%s",sn,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); this->WriteLine(data); } - for (std::vector::iterator i = Instance->XLines->elines.begin(); i != Instance->XLines->elines.end(); i++, iterations++) + for (std::vector::iterator i = Instance->XLines->elines.begin(); i != Instance->XLines->elines.end(); i++, iterations++) { - snprintf(data,MAXBUF,":%s ADDLINE E %s %s %lu %lu :%s",sn,i->hostmask,i->source,(unsigned long)i->set_time,(unsigned long)i->duration,i->reason); + snprintf(data,MAXBUF,":%s ADDLINE E %s %s %lu %lu :%s",sn,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); this->WriteLine(data); } - for (std::vector::iterator i = Instance->XLines->pzlines.begin(); i != Instance->XLines->pzlines.end(); i++, iterations++) + for (std::vector::iterator i = Instance->XLines->pzlines.begin(); i != Instance->XLines->pzlines.end(); i++, iterations++) { - snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s",sn,i->ipaddr,i->source,(unsigned long)i->set_time,(unsigned long)i->duration,i->reason); + snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s",sn,(*i)->ipaddr,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); this->WriteLine(data); } - for (std::vector::iterator i = Instance->XLines->pqlines.begin(); i != Instance->XLines->pqlines.end(); i++, iterations++) + for (std::vector::iterator i = Instance->XLines->pqlines.begin(); i != Instance->XLines->pqlines.end(); i++, iterations++) { - snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s",sn,i->nick,i->source,(unsigned long)i->set_time,(unsigned long)i->duration,i->reason); + snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s",sn,(*i)->nick,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); this->WriteLine(data); } - for (std::vector::iterator i = Instance->XLines->pglines.begin(); i != Instance->XLines->pglines.end(); i++, iterations++) + for (std::vector::iterator i = Instance->XLines->pglines.begin(); i != Instance->XLines->pglines.end(); i++, iterations++) { - snprintf(data,MAXBUF,":%s ADDLINE G %s %s %lu %lu :%s",sn,i->hostmask,i->source,(unsigned long)i->set_time,(unsigned long)i->duration,i->reason); + snprintf(data,MAXBUF,":%s ADDLINE G %s %s %lu %lu :%s",sn,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); this->WriteLine(data); } - for (std::vector::iterator i = Instance->XLines->pelines.begin(); i != Instance->XLines->pelines.end(); i++, iterations++) + for (std::vector::iterator i = Instance->XLines->pelines.begin(); i != Instance->XLines->pelines.end(); i++, iterations++) { - snprintf(data,MAXBUF,":%s ADDLINE E %s %s %lu %lu :%s",sn,i->hostmask,i->source,(unsigned long)i->set_time,(unsigned long)i->duration,i->reason); + snprintf(data,MAXBUF,":%s ADDLINE E %s %s %lu %lu :%s",sn,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); this->WriteLine(data); } } - /* Send channel modes and topics */ + /** Send channel modes and topics */ void SendChannelModes(TreeServer* Current) { char data[MAXBUF]; @@ -1884,7 +1863,7 @@ class TreeSocket : public InspSocket } } - /* send all users and their oper state/modes */ + /** send all users and their oper state/modes */ void SendUsers(TreeServer* Current) { char data[MAXBUF]; @@ -1915,7 +1894,7 @@ class TreeSocket : public InspSocket } } - /* This function is called when we want to send a netburst to a local + /** This function is called when we want to send a netburst to a local * server. There is a set order we must do this, because for example * users require their servers to exist, and channels require their * users to exist. You get the idea. @@ -1926,7 +1905,7 @@ class TreeSocket : public InspSocket std::string endburst = "ENDBURST"; // Because by the end of the netburst, it could be gone! std::string name = s->GetName(); - this->Instance->WriteOpers("*** Bursting to \2"+name+"\2."); + this->Instance->SNO->WriteToSnoMask('l',"Bursting to \2"+name+"\2."); this->WriteLine(burst); /* send our version string */ this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" VERSION :"+this->Instance->GetVersionString()); @@ -1939,10 +1918,10 @@ class TreeSocket : public InspSocket this->SendXLines(s); FOREACH_MOD_I(this->Instance,I_OnSyncOtherMetaData,OnSyncOtherMetaData((Module*)TreeProtocolModule,(void*)this)); this->WriteLine(endburst); - this->Instance->WriteOpers("*** Finished bursting to \2"+name+"\2."); + this->Instance->SNO->WriteToSnoMask('l',"Finished bursting to \2"+name+"\2."); } - /* This function is called when we receive data from a remote + /** This function is called when we receive data from a remote * server. We buffer the data in a std::string (it doesnt stay * there for long), reading using InspSocket::Read() which can * read up to 16 kilobytes in one operation. @@ -1975,14 +1954,14 @@ class TreeSocket : public InspSocket char result[1024]; memset(result,0,1024); memset(out,0,1024); - ServerInstance->Log(DEBUG,"Original string '%s'",ret.c_str()); + Instance->Log(DEBUG,"Original string '%s'",ret.c_str()); /* ERROR + CAPAB is still allowed unencryped */ if ((ret.substr(0,7) != "ERROR :") && (ret.substr(0,6) != "CAPAB ")) { int nbytes = from64tobits(out, ret.c_str(), 1024); if ((nbytes > 0) && (nbytes < 1024)) { - ServerInstance->Log(DEBUG,"m_spanningtree: decrypt %d bytes",nbytes); + Instance->Log(DEBUG,"m_spanningtree: decrypt %d bytes",nbytes); ctx_in->Decrypt(out, result, nbytes, 0); for (int t = 0; t < nbytes; t++) if (result[t] == '\7') result[t] = 0; @@ -1992,7 +1971,7 @@ class TreeSocket : public InspSocket } if (!this->ProcessLine(ret)) { - ServerInstance->Log(DEBUG,"ProcessLine says no!"); + Instance->Log(DEBUG,"ProcessLine says no!"); return false; } } @@ -2006,7 +1985,7 @@ class TreeSocket : public InspSocket int WriteLine(std::string line) { - ServerInstance->Log(DEBUG,"OUT: %s",line.c_str()); + Instance->Log(DEBUG,"OUT: %s",line.c_str()); if (this->ctx_out) { char result[10240]; @@ -2017,7 +1996,7 @@ class TreeSocket : public InspSocket int n = this->keylength - (line.length() % this->keylength); if (n) { - ServerInstance->Log(DEBUG,"Append %d chars to line to make it %d long from %d, key length %d",n,n+line.length(),line.length(),this->keylength); + Instance->Log(DEBUG,"Append %d chars to line to make it %d long from %d, key length %d",n,n+line.length(),line.length(),this->keylength); line.append(n,'\7'); } } @@ -2035,17 +2014,14 @@ class TreeSocket : public InspSocket { if (params.size() < 1) return false; - this->Instance->WriteOpers("*** ERROR from %s: %s",(InboundServerName != "" ? InboundServerName.c_str() : myhost.c_str()),params[0].c_str()); + this->Instance->SNO->WriteToSnoMask('l',"ERROR from %s: %s",(InboundServerName != "" ? InboundServerName.c_str() : myhost.c_str()),params[0].c_str()); /* we will return false to cause the socket to close. */ return false; } - /* remote MOTD. leet, huh? */ + /** remote MOTD. leet, huh? */ bool Motd(std::string prefix, std::deque ¶ms) { - /* Get the reply to a STATS query if it matches this servername, - * and send it back as a load of PUSH queries - */ if (params.size() > 0) { if (this->Instance->MatchText(this->Instance->Config->ServerName, params[0])) @@ -2060,23 +2036,23 @@ class TreeSocket : public InspSocket par.push_back(prefix); par.push_back(""); - if (!ServerInstance->Config->MOTD.size()) + if (!Instance->Config->MOTD.size()) { - par[1] = std::string("::")+ServerInstance->Config->ServerName+" 422 "+source->nick+" :Message of the day file is missing."; + par[1] = std::string("::")+Instance->Config->ServerName+" 422 "+source->nick+" :Message of the day file is missing."; DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); return true; } - par[1] = std::string("::")+ServerInstance->Config->ServerName+" 375 "+source->nick+" :"+ServerInstance->Config->ServerName+" message of the day"; + par[1] = std::string("::")+Instance->Config->ServerName+" 375 "+source->nick+" :"+Instance->Config->ServerName+" message of the day"; DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); - for (unsigned int i = 0; i < ServerInstance->Config->MOTD.size(); i++) + for (unsigned int i = 0; i < Instance->Config->MOTD.size(); i++) { - par[1] = std::string("::")+ServerInstance->Config->ServerName+" 372 "+source->nick+" :- "+ServerInstance->Config->MOTD[i]; + par[1] = std::string("::")+Instance->Config->ServerName+" 372 "+source->nick+" :- "+Instance->Config->MOTD[i]; DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); } - par[1] = std::string("::")+ServerInstance->Config->ServerName+" 376 "+source->nick+" End of message of the day."; + par[1] = std::string("::")+Instance->Config->ServerName+" 376 "+source->nick+" End of message of the day."; DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); } } @@ -2091,6 +2067,47 @@ class TreeSocket : public InspSocket return true; } + /** remote ADMIN. leet, huh? */ + bool Admin(std::string prefix, std::deque ¶ms) + { + if (params.size() > 0) + { + if (this->Instance->MatchText(this->Instance->Config->ServerName, params[0])) + { + /* It's for our server */ + string_list results; + userrec* source = this->Instance->FindNick(prefix); + + if (source) + { + std::deque par; + par.push_back(prefix); + par.push_back(""); + + par[1] = std::string("::")+Instance->Config->ServerName+" 256 "+source->nick+" :Administrative info for "+Instance->Config->ServerName; + DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); + + par[1] = std::string("::")+Instance->Config->ServerName+" 257 "+source->nick+" :Name - "+Instance->Config->AdminName; + DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); + + par[1] = std::string("::")+Instance->Config->ServerName+" 258 "+source->nick+" :Nickname - "+Instance->Config->AdminNick; + DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); + + par[1] = std::string("::")+Instance->Config->ServerName+" 258 "+source->nick+" :E-Mail - "+Instance->Config->AdminEmail; + DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server); + } + } + else + { + /* Pass it on */ + userrec* source = this->Instance->FindNick(prefix); + if (source) + DoOneToOne(prefix, "ADMIN", params, params[0]); + } + } + return true; + } + bool Stats(std::string prefix, std::deque ¶ms) { /* Get the reply to a STATS query if it matches this servername, @@ -2128,14 +2145,14 @@ class TreeSocket : public InspSocket } - /* Because the core won't let users or even SERVERS set +o, + /** Because the core won't let users or even SERVERS set +o, * we use the OPERTYPE command to do this. */ bool OperType(std::string prefix, std::deque ¶ms) { if (params.size() != 1) { - ServerInstance->Log(DEBUG,"Received invalid oper type from %s",prefix.c_str()); + Instance->Log(DEBUG,"Received invalid oper type from %s",prefix.c_str()); return true; } std::string opertype = params[0]; @@ -2149,7 +2166,7 @@ class TreeSocket : public InspSocket return true; } - /* Because Andy insists that services-compatible servers must + /** Because Andy insists that services-compatible servers must * implement SVSNICK and SVSJOIN, that's exactly what we do :p */ bool ForceNick(std::string prefix, std::deque ¶ms) @@ -2204,7 +2221,7 @@ class TreeSocket : public InspSocket if (this->Instance->MatchText(this->Instance->Config->ServerName,servermask)) { - this->Instance->WriteOpers("*** Remote rehash initiated from server \002"+prefix+"\002."); + this->Instance->SNO->WriteToSnoMask('l',"Remote rehash initiated from server \002"+prefix+"\002."); this->Instance->RehashServer(); ReadConfiguration(false); } @@ -2360,27 +2377,27 @@ class TreeSocket : public InspSocket switch (*(params[0].c_str())) { case 'Z': - propogate = ServerInstance->XLines->add_zline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str()); - ServerInstance->XLines->zline_set_creation_time(params[1].c_str(), atoi(params[3].c_str())); + propogate = Instance->XLines->add_zline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str()); + Instance->XLines->zline_set_creation_time(params[1].c_str(), atoi(params[3].c_str())); break; case 'Q': - propogate = ServerInstance->XLines->add_qline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str()); - ServerInstance->XLines->qline_set_creation_time(params[1].c_str(), atoi(params[3].c_str())); + propogate = Instance->XLines->add_qline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str()); + Instance->XLines->qline_set_creation_time(params[1].c_str(), atoi(params[3].c_str())); break; case 'E': - propogate = ServerInstance->XLines->add_eline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str()); - ServerInstance->XLines->eline_set_creation_time(params[1].c_str(), atoi(params[3].c_str())); + propogate = Instance->XLines->add_eline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str()); + Instance->XLines->eline_set_creation_time(params[1].c_str(), atoi(params[3].c_str())); break; case 'G': - propogate = ServerInstance->XLines->add_gline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str()); - ServerInstance->XLines->gline_set_creation_time(params[1].c_str(), atoi(params[3].c_str())); + propogate = Instance->XLines->add_gline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str()); + Instance->XLines->gline_set_creation_time(params[1].c_str(), atoi(params[3].c_str())); break; case 'K': - propogate = ServerInstance->XLines->add_kline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str()); + propogate = Instance->XLines->add_kline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str()); break; default: /* Just in case... */ - this->Instance->WriteOpers("*** \2WARNING\2: Invalid xline type '"+params[0]+"' sent by server "+prefix+", ignored!"); + this->Instance->SNO->WriteToSnoMask('x',"\2WARNING\2: Invalid xline type '"+params[0]+"' sent by server "+prefix+", ignored!"); propogate = false; break; } @@ -2390,19 +2407,19 @@ class TreeSocket : public InspSocket { if (atoi(params[4].c_str())) { - this->Instance->WriteOpers("*** %s Added %cLINE on %s to expire in %lu seconds (%s).",prefix.c_str(),*(params[0].c_str()),params[1].c_str(),atoi(params[4].c_str()),params[5].c_str()); + this->Instance->SNO->WriteToSnoMask('x',"%s Added %cLINE on %s to expire in %lu seconds (%s).",prefix.c_str(),*(params[0].c_str()),params[1].c_str(),atoi(params[4].c_str()),params[5].c_str()); } else { - this->Instance->WriteOpers("*** %s Added permenant %cLINE on %s (%s).",prefix.c_str(),*(params[0].c_str()),params[1].c_str(),params[5].c_str()); + this->Instance->SNO->WriteToSnoMask('x',"%s Added permenant %cLINE on %s (%s).",prefix.c_str(),*(params[0].c_str()),params[1].c_str(),params[5].c_str()); } params[5] = ":" + params[5]; DoOneToAllButSender(prefix,"ADDLINE",params,prefix); } if (!this->bursting) { - ServerInstance->Log(DEBUG,"Applying lines..."); - ServerInstance->XLines->apply_lines(APPLY_ZLINES|APPLY_GLINES|APPLY_QLINES); + Instance->Log(DEBUG,"Applying lines..."); + Instance->XLines->apply_lines(APPLY_ZLINES|APPLY_GLINES|APPLY_QLINES); } return true; } @@ -2428,12 +2445,12 @@ class TreeSocket : public InspSocket if (params.size() < 1) return true; - ServerInstance->Log(DEBUG,"In IDLE command"); + Instance->Log(DEBUG,"In IDLE command"); userrec* u = this->Instance->FindNick(prefix); if (u) { - ServerInstance->Log(DEBUG,"USER EXISTS: %s",u->nick); + Instance->Log(DEBUG,"USER EXISTS: %s",u->nick); // an incoming request if (params.size() == 1) { @@ -2441,10 +2458,9 @@ class TreeSocket : public InspSocket if ((x) && (IS_LOCAL(x))) { userrec* x = this->Instance->FindNick(params[0]); - ServerInstance->Log(DEBUG,"Got IDLE"); char signon[MAXBUF]; char idle[MAXBUF]; - ServerInstance->Log(DEBUG,"Sending back IDLE 3"); + snprintf(signon,MAXBUF,"%lu",(unsigned long)x->signon); snprintf(idle,MAXBUF,"%lu",(unsigned long)abs((x->idle_lastmsg)-time(NULL))); std::deque par; @@ -2466,7 +2482,6 @@ class TreeSocket : public InspSocket userrec* who_to_send_to = this->Instance->FindNick(who_did_the_whois); if ((who_to_send_to) && (IS_LOCAL(who_to_send_to))) { - ServerInstance->Log(DEBUG,"Got final IDLE"); // an incoming reply to a whois we sent out std::string nick_whoised = prefix; unsigned long signon = atoi(params[1].c_str()); @@ -2587,6 +2602,52 @@ class TreeSocket : public InspSocket } } + bool RemoveStatus(std::string prefix, std::deque ¶ms) + { + if (params.size() < 1) + return true; + + chanrec* c = Instance->FindChan(params[0]); + + if (c) + { + CUList *ulist = c->GetUsers(); + for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) + { + std::string modesequence = Instance->Modes->ModeString(i->second, c); + if (modesequence.length()) + { + modesequence = "-" + modesequence; + + std::deque items; + const char* y[127]; + unsigned int z = 0; + std::string x = "*"; + irc::spacesepstream sep(modesequence); + + while ((x = sep.GetToken()) != "") + { + if (!z) + { + y[z++] = c->name; + items.push_back(c->name); + items.push_back(ConvToStr(c->age)); + } + items.push_back(x); + y[z++] = (items.end() - 1)->c_str(); + } + + DoOneToMany(Instance->Config->ServerName, "FMODE", items); + userrec* n = new userrec(Instance); + n->SetFd(FD_MAGIC_NUMBER); + Instance->SendMode(y,z,n); + delete n; + } + } + } + return true; + } + bool RemoteServer(std::string prefix, std::deque ¶ms) { if (params.size() < 4) @@ -2607,14 +2668,14 @@ class TreeSocket : public InspSocket if (CheckDupe) { this->WriteLine("ERROR :Server "+servername+" already exists!"); - this->Instance->WriteOpers("*** Server connection from \2"+servername+"\2 denied, already exists"); + this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+servername+"\2 denied, already exists"); return false; } TreeServer* Node = new TreeServer(this->Instance,servername,description,ParentOfThis,NULL); ParentOfThis->AddChild(Node); params[3] = ":" + params[3]; DoOneToAllButSender(prefix,"SERVER",params,prefix); - this->Instance->WriteOpers("*** Server \002"+prefix+"\002 introduced server \002"+servername+"\002 ("+description+")"); + this->Instance->SNO->WriteToSnoMask('l',"Server \002"+prefix+"\002 introduced server \002"+servername+"\002 ("+description+")"); return true; } @@ -2631,7 +2692,7 @@ class TreeSocket : public InspSocket if (hops) { this->WriteLine("ERROR :Server too far away for authentication"); - this->Instance->WriteOpers("*** Server connection from \2"+sname+"\2 denied, server is too far away for authentication"); + this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication"); return false; } std::string description = params[3]; @@ -2643,7 +2704,7 @@ class TreeSocket : public InspSocket if (CheckDupe) { this->WriteLine("ERROR :Server "+sname+" already exists on server "+CheckDupe->GetParent()->GetName()+"!"); - this->Instance->WriteOpers("*** Server connection from \2"+sname+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName()); + this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName()); return false; } // Begin the sync here. this kickstarts the @@ -2664,7 +2725,7 @@ class TreeSocket : public InspSocket } } this->WriteLine("ERROR :Invalid credentials"); - this->Instance->WriteOpers("*** Server connection from \2"+sname+"\2 denied, invalid link credentials"); + this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials"); return false; } @@ -2681,7 +2742,7 @@ class TreeSocket : public InspSocket if (hops) { this->WriteLine("ERROR :Server too far away for authentication"); - this->Instance->WriteOpers("*** Server connection from \2"+sname+"\2 denied, server is too far away for authentication"); + this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication"); return false; } std::string description = params[3]; @@ -2693,7 +2754,7 @@ class TreeSocket : public InspSocket if (CheckDupe) { this->WriteLine("ERROR :Server "+sname+" already exists on server "+CheckDupe->GetParent()->GetName()+"!"); - this->Instance->WriteOpers("*** Server connection from \2"+sname+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName()); + this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName()); return false; } /* If the config says this link is encrypted, but the remote side @@ -2703,10 +2764,10 @@ class TreeSocket : public InspSocket if ((x->EncryptionKey != "") && (!this->ctx_in)) { this->WriteLine("ERROR :This link requires AES encryption to be enabled. Plaintext connection refused."); - this->Instance->WriteOpers("*** Server connection from \2"+sname+"\2 denied, remote server did not enable AES."); + this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, remote server did not enable AES."); return false; } - this->Instance->WriteOpers("*** Verified incoming server connection from \002"+sname+"\002["+(x->HiddenFromStats ? "" : this->GetIP())+"] ("+description+")"); + this->Instance->SNO->WriteToSnoMask('l',"Verified incoming server connection from \002"+sname+"\002["+(x->HiddenFromStats ? "" : this->GetIP())+"] ("+description+")"); this->InboundServerName = sname; this->InboundDescription = description; // this is good. Send our details: Our server name and description and hopcount of 0, @@ -2718,7 +2779,7 @@ class TreeSocket : public InspSocket } } this->WriteLine("ERROR :Invalid credentials"); - this->Instance->WriteOpers("*** Server connection from \2"+sname+"\2 denied, invalid link credentials"); + this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials"); return false; } @@ -2743,7 +2804,7 @@ class TreeSocket : public InspSocket line = line.substr(0, line.find_first_of("\r\n")); - ServerInstance->Log(DEBUG,"IN: %s", line.c_str()); + Instance->Log(DEBUG,"IN: %s", line.c_str()); this->Split(line.c_str(),params); @@ -2771,7 +2832,7 @@ class TreeSocket : public InspSocket } else if ((this->ctx_in) && (command == "AES")) { - this->Instance->WriteOpers("*** \2AES\2: Encryption already enabled on this connection yet %s is trying to enable it twice!",params[0].c_str()); + this->Instance->SNO->WriteToSnoMask('l',"\2AES\2: Encryption already enabled on this connection yet %s is trying to enable it twice!",params[0].c_str()); } switch (this->LinkState) @@ -2839,13 +2900,13 @@ class TreeSocket : public InspSocket long delta = THEM-time(NULL); if ((delta < -600) || (delta > 600)) { - this->Instance->WriteOpers("*** \2ERROR\2: Your clocks are out by %d seconds (this is more than ten minutes). Link aborted, \2PLEASE SYNC YOUR CLOCKS!\2",abs(delta)); + this->Instance->SNO->WriteToSnoMask('l',"\2ERROR\2: Your clocks are out by %d seconds (this is more than ten minutes). Link aborted, \2PLEASE SYNC YOUR CLOCKS!\2",abs(delta)); this->WriteLine("ERROR :Your clocks are out by "+ConvToStr(abs(delta))+" seconds (this is more than ten minutes). Link aborted, PLEASE SYNC YOUR CLOCKS!"); return false; } else if ((delta < -60) || (delta > 60)) { - this->Instance->WriteOpers("*** \2WARNING\2: Your clocks are out by %d seconds, please consider synching your clocks.",abs(delta)); + this->Instance->SNO->WriteToSnoMask('l',"\2WARNING\2: Your clocks are out by %d seconds, please consider synching your clocks.",abs(delta)); } } this->LinkState = CONNECTED; @@ -2906,7 +2967,7 @@ class TreeSocket : public InspSocket if ((!route_back_again) || (route_back_again->GetSocket() != this)) { if (route_back_again) - ServerInstance->Log(DEBUG,"Protocol violation: Fake direction in command '%s' from connection '%s'",line.c_str(),this->GetName().c_str()); + Instance->Log(DEBUG,"Protocol violation: Fake direction in command '%s' from connection '%s'",line.c_str(),this->GetName().c_str()); return true; } @@ -2946,6 +3007,10 @@ class TreeSocket : public InspSocket { return this->Motd(prefix, params); } + else if (command == "ADMIN") + { + return this->Admin(prefix, params); + } else if (command == "SERVER") { return this->RemoteServer(prefix,params); @@ -2978,6 +3043,10 @@ class TreeSocket : public InspSocket { return this->MetaData(prefix,params); } + else if (command == "REMSTATUS") + { + return this->RemoveStatus(prefix,params); + } else if (command == "PING") { /* @@ -2988,7 +3057,7 @@ class TreeSocket : public InspSocket if (this->bursting) { this->bursting = false; - ServerInstance->XLines->apply_lines(APPLY_ZLINES|APPLY_GLINES|APPLY_QLINES); + Instance->XLines->apply_lines(APPLY_ZLINES|APPLY_GLINES|APPLY_QLINES); } if (prefix == "") { @@ -3006,7 +3075,7 @@ class TreeSocket : public InspSocket if (this->bursting) { this->bursting = false; - ServerInstance->XLines->apply_lines(APPLY_ZLINES|APPLY_GLINES|APPLY_QLINES); + Instance->XLines->apply_lines(APPLY_ZLINES|APPLY_GLINES|APPLY_QLINES); } if (prefix == "") { @@ -3089,13 +3158,13 @@ class TreeSocket : public InspSocket else if (command == "ENDBURST") { this->bursting = false; - ServerInstance->XLines->apply_lines(APPLY_ZLINES|APPLY_GLINES|APPLY_QLINES); + Instance->XLines->apply_lines(APPLY_ZLINES|APPLY_GLINES|APPLY_QLINES); std::string sourceserv = this->myhost; if (this->InboundServerName != "") { sourceserv = this->InboundServerName; } - this->Instance->WriteOpers("*** Received end of netburst from \2%s\2",sourceserv.c_str()); + this->Instance->SNO->WriteToSnoMask('l',"Received end of netburst from \2%s\2",sourceserv.c_str()); return true; } else @@ -3144,10 +3213,18 @@ class TreeSocket : public InspSocket { strparams[q] = params[q].c_str(); } - if (!this->Instance->CallCommandHandler(command.c_str(), strparams, params.size(), who)) + switch (this->Instance->CallCommandHandler(command.c_str(), strparams, params.size(), who)) { - this->WriteLine("ERROR :Unrecognised command '"+std::string(command.c_str())+"' -- possibly loaded mismatched modules"); - return false; + case CMD_INVALID: + this->WriteLine("ERROR :Unrecognised command '"+std::string(command.c_str())+"' -- possibly loaded mismatched modules"); + return false; + break; + case CMD_FAILURE: + return true; + break; + default: + /* CMD_SUCCESS and CMD_USER_DELETED fall through here */ + break; } } else @@ -3159,7 +3236,7 @@ class TreeSocket : public InspSocket } else { - ServerInstance->Log(DEBUG,"Command with unknown origin '%s'",prefix.c_str()); + Instance->Log(DEBUG,"Command with unknown origin '%s'",prefix.c_str()); return true; } } @@ -3186,7 +3263,10 @@ class TreeSocket : public InspSocket { if (this->LinkState == CONNECTING) { - this->Instance->WriteOpers("*** CONNECT: Connection to \002"+myhost+"\002 timed out."); + this->Instance->SNO->WriteToSnoMask('l',"CONNECT: Connection to \002"+myhost+"\002 timed out."); + Link* MyLink = FindLink(myhost); + if (MyLink) + DoFailOver(MyLink); } } @@ -3205,7 +3285,7 @@ class TreeSocket : public InspSocket { Squit(s,"Remote host closed the connection"); } - this->Instance->WriteOpers("Server '\2%s\2' closed the connection.",quitserver.c_str()); + this->Instance->SNO->WriteToSnoMask('l',"Connection to '\2%s\2' failed.",quitserver.c_str()); } virtual int OnIncomingConnection(int newsock, char* ip) @@ -3225,7 +3305,7 @@ class TreeSocket : public InspSocket if (!found) { - this->Instance->WriteOpers("Server connection from %s denied (no link blocks with that IP address)", ip); + this->Instance->SNO->WriteToSnoMask('l',"Server connection from %s denied (no link blocks with that IP address)", ip); close(newsock); return false; } @@ -3273,8 +3353,9 @@ class ServernameResolver : public Resolver else { /* Something barfed, show the opers */ - ServerInstance->WriteOpers("*** CONNECT: Error connecting \002%s\002: %s.",MyLink.Name.c_str(),strerror(errno)); + ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",MyLink.Name.c_str(),strerror(errno)); delete newsocket; + DoFailOver(&MyLink); } } } @@ -3282,7 +3363,8 @@ class ServernameResolver : public Resolver void OnError(ResolverError e, const std::string &errormessage) { /* Ooops! */ - ServerInstance->WriteOpers("*** CONNECT: Error connecting \002%s\002: Unable to resolve hostname - %s",MyLink.Name.c_str(),errormessage.c_str()); + ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: Unable to resolve hostname - %s",MyLink.Name.c_str(),errormessage.c_str()); + DoFailOver(&MyLink); } }; @@ -3319,7 +3401,7 @@ void AddThisServer(TreeServer* server, std::deque &list) list.push_back(server); } -// returns a list of DIRECT servernames for a specific channel +/** returns a list of DIRECT servernames for a specific channel */ void GetListOfServersForChannel(chanrec* c, std::deque &list) { CUList *ulist = c->GetUsers(); @@ -3536,6 +3618,7 @@ void ReadConfiguration(bool rebind) std::string Allow = Conf->ReadValue("link","allowmask",j); L.Name = (Conf->ReadValue("link","name",j)).c_str(); L.IPAddr = Conf->ReadValue("link","ipaddr",j); + L.FailOver = Conf->ReadValue("link","failover",j).c_str(); L.Port = Conf->ReadInteger("link","port",j,true); L.SendPass = Conf->ReadValue("link","sendpass",j); L.RecvPass = Conf->ReadValue("link","recvpass",j); @@ -3544,53 +3627,60 @@ void ReadConfiguration(bool rebind) L.HiddenFromStats = Conf->ReadFlag("link","hidden",j); L.NextConnectTime = time(NULL) + L.AutoConnect; /* Bugfix by brain, do not allow people to enter bad configurations */ - if ((L.IPAddr != "") && (L.RecvPass != "") && (L.SendPass != "") && (L.Name != "") && (L.Port)) + if (L.Name != ServerInstance->Config->ServerName) { - ValidIPs.push_back(L.IPAddr); + if ((L.IPAddr != "") && (L.RecvPass != "") && (L.SendPass != "") && (L.Name != "") && (L.Port)) + { + ValidIPs.push_back(L.IPAddr); - if (Allow.length()) - ValidIPs.push_back(Allow); + if (Allow.length()) + ValidIPs.push_back(Allow); - /* Needs resolving */ - insp_inaddr binip; - if (insp_aton(L.IPAddr.c_str(), &binip) < 1) + /* Needs resolving */ + insp_inaddr binip; + if (insp_aton(L.IPAddr.c_str(), &binip) < 1) + { + try + { + SecurityIPResolver* sr = new SecurityIPResolver(ServerInstance, L.IPAddr, L); + ServerInstance->AddResolver(sr); + } + catch (ModuleException& e) + { + ServerInstance->Log(DEBUG,"Error in resolver: %s",e.GetReason()); + } + } + + LinkBlocks.push_back(L); + ServerInstance->Log(DEBUG,"m_spanningtree: Read server %s with host %s:%d",L.Name.c_str(),L.IPAddr.c_str(),L.Port); + } + else { - try + if (L.IPAddr == "") + { + ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', IP address not defined!",L.Name.c_str()); + } + else if (L.RecvPass == "") + { + ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', recvpass not defined!",L.Name.c_str()); + } + else if (L.SendPass == "") + { + ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', sendpass not defined!",L.Name.c_str()); + } + else if (L.Name == "") { - SecurityIPResolver* sr = new SecurityIPResolver(ServerInstance, L.IPAddr, L); - ServerInstance->AddResolver(sr); + ServerInstance->Log(DEFAULT,"Invalid configuration, link tag without a name!"); } - catch (ModuleException& e) + else if (!L.Port) { - ServerInstance->Log(DEBUG,"Error in resolver: %s",e.GetReason()); + ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', no port specified!",L.Name.c_str()); } } - - LinkBlocks.push_back(L); - ServerInstance->Log(DEBUG,"m_spanningtree: Read server %s with host %s:%d",L.Name.c_str(),L.IPAddr.c_str(),L.Port); } else { - if (L.IPAddr == "") - { - ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', IP address not defined!",L.Name.c_str()); - } - else if (L.RecvPass == "") - { - ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', recvpass not defined!",L.Name.c_str()); - } - else if (L.SendPass == "") - { - ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', sendpass not defined!",L.Name.c_str()); - } - else if (L.Name == "") - { - ServerInstance->Log(DEFAULT,"Invalid configuration, link tag without a name!"); - } - else if (!L.Port) - { - ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', no port specified!",L.Name.c_str()); - } + ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', link tag has the same server name as the local server!",L.Name.c_str()); } } DELETE(Conf); @@ -3771,6 +3861,29 @@ class ModuleSpanningTree : public Module return 0; } + int HandleAdmin(const char** parameters, int pcnt, userrec* user) + { + if (pcnt > 0) + { + /* Remote ADMIN, the server is within the 1st parameter */ + std::deque params; + params.push_back(parameters[0]); + + /* Send it out remotely, generate no reply yet */ + TreeServer* s = FindServerMask(parameters[0]); + if (s) + { + DoOneToOne(user->nick, "ADMIN", params, s->GetName()); + } + else + { + user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]); + } + return 1; + } + return 0; + } + int HandleStats(const char** parameters, int pcnt, userrec* user) { if (pcnt > 1) @@ -3874,7 +3987,7 @@ class ModuleSpanningTree : public Module if (sock) { ServerInstance->Log(DEBUG,"Splitting server %s",s->GetName().c_str()); - ServerInstance->WriteOpers("*** SQUIT: Server \002%s\002 removed from network by %s",parameters[0],user->nick); + ServerInstance->SNO->WriteToSnoMask('l',"SQUIT: Server \002%s\002 removed from network by %s",parameters[0],user->nick); sock->Squit(s,"Server quit by "+std::string(user->nick)+"!"+std::string(user->ident)+"@"+std::string(user->host)); ServerInstance->SE->DelFd(sock); sock->Close(); @@ -3956,7 +4069,7 @@ class ModuleSpanningTree : public Module else { // they didnt answer, boot them - ServerInstance->WriteOpers("*** Server \002%s\002 pinged out",serv->GetName().c_str()); + ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 pinged out",serv->GetName().c_str()); sock->Squit(serv,"Ping timeout"); ServerInstance->SE->DelFd(sock); sock->Close(); @@ -3968,6 +4081,74 @@ class ModuleSpanningTree : public Module } } + void ConnectServer(Link* x) + { + insp_inaddr binip; + + /* Do we already have an IP? If so, no need to resolve it. */ + if (insp_aton(x->IPAddr.c_str(), &binip) > 0) + { + TreeSocket* newsocket = new TreeSocket(ServerInstance, x->IPAddr,x->Port,false,10,x->Name.c_str()); + if (newsocket->GetFd() > -1) + { + /* Handled automatically on success */ + } + else + { + ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(),strerror(errno)); + delete newsocket; + this->DoFailOver(x); + } + } + else + { + try + { + ServernameResolver* snr = new ServernameResolver(ServerInstance,x->IPAddr, *x); + ServerInstance->AddResolver(snr); + } + catch (ModuleException& e) + { + ServerInstance->Log(DEBUG,"Error in resolver: %s",e.GetReason()); + this->DoFailOver(x); + } + } + } + + void DoFailOver(Link* x) + { + if (x->FailOver.length()) + { + if (x->FailOver == x->Name) + { + ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Some muppet configured the failover for server \002%s\002 to point at itself. Not following it!", x->Name.c_str()); + return; + } + Link* TryThisOne = this->FindLink(x->FailOver.c_str()); + if (TryThisOne) + { + ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Trying failover link for \002%s\002: \002%s\002...", x->Name.c_str(), TryThisOne->Name.c_str()); + ConnectServer(TryThisOne); + } + else + { + ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Invalid failover server specified for server \002%s\002, will not follow!", x->Name.c_str()); + } + } + } + + Link* FindLink(const std::string& name) + { + for (std::vector::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++) + { + if (ServerInstance->MatchText(x->Name.c_str(), name.c_str())) + { + return &(*x); + } + } + return NULL; + } + void AutoConnectServers(time_t curtime) { for (std::vector::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++) @@ -3977,39 +4158,23 @@ class ModuleSpanningTree : public Module ServerInstance->Log(DEBUG,"Auto-Connecting %s",x->Name.c_str()); x->NextConnectTime = curtime + x->AutoConnect; TreeServer* CheckDupe = FindServer(x->Name.c_str()); - if (!CheckDupe) + if (x->FailOver.length()) { - // an autoconnected server is not connected. Check if its time to connect it - ServerInstance->WriteOpers("*** AUTOCONNECT: Auto-connecting server \002%s\002 (%lu seconds until next attempt)",x->Name.c_str(),x->AutoConnect); - - insp_inaddr binip; - - /* Do we already have an IP? If so, no need to resolve it. */ - if (insp_aton(x->IPAddr.c_str(), &binip) > 0) + TreeServer* CheckFailOver = FindServer(x->FailOver.c_str()); + if (CheckFailOver) { - TreeSocket* newsocket = new TreeSocket(ServerInstance, x->IPAddr,x->Port,false,10,x->Name.c_str()); - if (newsocket->GetFd() > -1) - { - } - else - { - ServerInstance->WriteOpers("*** AUTOCONNECT: Error autoconnecting \002%s\002: %s.",x->Name.c_str(),strerror(errno)); - delete newsocket; - } - } - else - { - try - { - ServernameResolver* snr = new ServernameResolver(ServerInstance,x->IPAddr, *x); - ServerInstance->AddResolver(snr); - } - catch (ModuleException& e) - { - ServerInstance->Log(DEBUG,"Error in resolver: %s",e.GetReason()); - } + /* The failover for this server is currently a member of the network. + * The failover probably succeeded, where the main link did not. + * Don't try the main link until the failover is gone again. + */ + continue; } - + } + if (!CheckDupe) + { + // an autoconnected server is not connected. Check if its time to connect it + ServerInstance->SNO->WriteToSnoMask('l',"AUTOCONNECT: Auto-connecting server \002%s\002 (%lu seconds until next attempt)",x->Name.c_str(),x->AutoConnect); + this->ConnectServer(&(*x)); } } } @@ -4062,33 +4227,7 @@ class ModuleSpanningTree : public Module if (!CheckDupe) { user->WriteServ("NOTICE %s :*** CONNECT: Connecting to server: \002%s\002 (%s:%d)",user->nick,x->Name.c_str(),(x->HiddenFromStats ? "" : x->IPAddr.c_str()),x->Port); - insp_inaddr binip; - - /* Do we already have an IP? If so, no need to resolve it. */ - if (insp_aton(x->IPAddr.c_str(), &binip) > 0) - { - TreeSocket* newsocket = new TreeSocket(ServerInstance,x->IPAddr,x->Port,false,10,x->Name.c_str()); - if (newsocket->GetFd() > -1) - { - } - else - { - ServerInstance->WriteOpers("*** CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(),strerror(errno)); - delete newsocket; - } - } - else - { - try - { - ServernameResolver* snr = new ServernameResolver(ServerInstance, x->IPAddr, *x); - ServerInstance->AddResolver(snr); - } - catch (ModuleException& e) - { - ServerInstance->Log(DEBUG,"Error in resolver: %s",e.GetReason()); - } - } + ConnectServer(&(*x)); return 1; } else @@ -4112,13 +4251,13 @@ class ModuleSpanningTree : public Module results.push_back(std::string(ServerInstance->Config->ServerName)+" 244 "+user->nick+" H * * "+LinkBlocks[i].Name.c_str()); } results.push_back(std::string(ServerInstance->Config->ServerName)+" 219 "+user->nick+" "+statschar+" :End of /STATS report"); - ServerInstance->WriteOpers("*** Notice: %s '%c' requested by %s (%s@%s)",(!strcmp(user->server,ServerInstance->Config->ServerName) ? "Stats" : "Remote stats"),statschar,user->nick,user->ident,user->host); + ServerInstance->SNO->WriteToSnoMask('t',"Notice: %s '%c' requested by %s (%s@%s)",(!strcmp(user->server,ServerInstance->Config->ServerName) ? "Stats" : "Remote stats"),statschar,user->nick,user->ident,user->host); return 1; } return 0; } - virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated) + virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) { /* If the command doesnt appear to be valid, we dont want to mess with it. */ if (!validated) @@ -4136,6 +4275,10 @@ class ModuleSpanningTree : public Module { return this->HandleMotd(parameters,pcnt,user); } + else if (command == "ADMIN") + { + return this->HandleAdmin(parameters,pcnt,user); + } else if (command == "SQUIT") { return this->HandleSquit(parameters,pcnt,user); @@ -4172,7 +4315,13 @@ class ModuleSpanningTree : public Module this->HandleVersion(parameters,pcnt,user); return 1; } - else if (ServerInstance->IsValidModuleCommand(command, pcnt, user)) + + return 0; + } + + virtual void OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line) + { + if ((result == CMD_SUCCESS) && (ServerInstance->IsValidModuleCommand(command, pcnt, user))) { // this bit of code cleverly routes all module commands // to all remote severs *automatically* so that modules @@ -4195,7 +4344,6 @@ class ModuleSpanningTree : public Module ServerInstance->Log(DEBUG,"Globally route '%s'",command.c_str()); DoOneToMany(user->nick,command,params); } - return 0; } virtual void OnGetServerDescription(const std::string &servername,std::string &description) @@ -4350,24 +4498,14 @@ class ModuleSpanningTree : public Module std::deque params; params.clear(); params.push_back(channel->name); - - if (channel->GetUserCounter() > 1) - { - // not the first in the channel - DoOneToMany(user->nick,"JOIN",params); - } - else - { - // first in the channel, set up their permissions - // and the channel TS with FJOIN. - char ts[24]; - snprintf(ts,24,"%lu",(unsigned long)channel->age); - params.clear(); - params.push_back(channel->name); - params.push_back(ts); - params.push_back("@,"+std::string(user->nick)); - DoOneToMany(ServerInstance->Config->ServerName,"FJOIN",params); - } + // set up their permissions and the channel TS with FJOIN. + // All users are FJOINed now, because a module may specify + // new joining permissions for the user. + params.clear(); + params.push_back(channel->name); + params.push_back(ConvToStr(channel->age)); + params.push_back(std::string(channel->GetAllPrefixChars(user))+","+std::string(user->nick)); + DoOneToMany(ServerInstance->Config->ServerName,"FJOIN",params); } } @@ -4666,17 +4804,26 @@ class ModuleSpanningTree : public Module virtual void OnEvent(Event* event) { + std::deque* params = (std::deque*)event->GetData(); + if (event->GetEventID() == "send_metadata") { - std::deque* params = (std::deque*)event->GetData(); if (params->size() < 3) return; (*params)[2] = ":" + (*params)[2]; DoOneToMany(ServerInstance->Config->ServerName,"METADATA",*params); } + else if (event->GetEventID() == "send_topic") + { + if (params->size() < 2) + return; + (*params)[1] = ":" + (*params)[1]; + params->insert(params->begin() + 1,ServerInstance->Config->ServerName); + params->insert(params->begin() + 1,ConvToStr(ServerInstance->Time())); + DoOneToMany(ServerInstance->Config->ServerName,"FTOPIC",*params); + } else if (event->GetEventID() == "send_mode") { - std::deque* params = (std::deque*)event->GetData(); if (params->size() < 2) return; // Insert the TS value of the object, either userrec or chanrec @@ -4716,7 +4863,7 @@ class ModuleSpanningTree : public Module List[I_OnUserQuit] = List[I_OnUserPostNick] = List[I_OnUserKick] = List[I_OnRemoteKill] = List[I_OnRehash] = 1; List[I_OnOper] = List[I_OnAddGLine] = List[I_OnAddZLine] = List[I_OnAddQLine] = List[I_OnAddELine] = 1; List[I_OnDelGLine] = List[I_OnDelZLine] = List[I_OnDelQLine] = List[I_OnDelELine] = List[I_ProtoSendMode] = List[I_OnMode] = 1; - List[I_OnStats] = List[I_ProtoSendMetaData] = List[I_OnEvent] = List[I_OnSetAway] = List[I_OnCancelAway] = 1; + List[I_OnStats] = List[I_ProtoSendMetaData] = List[I_OnEvent] = List[I_OnSetAway] = List[I_OnCancelAway] = List[I_OnPostCommand] = 1; } /* It is IMPORTANT that m_spanningtree is the last module in the chain @@ -4732,6 +4879,15 @@ class ModuleSpanningTree : public Module } }; +void DoFailOver(Link* x) +{ + TreeProtocolModule->DoFailOver(x); +} + +Link* FindLink(const std::string& name) +{ + return TreeProtocolModule->FindLink(name); +} class ModuleSpanningTreeFactory : public ModuleFactory {