The InspIRCd Project
Home | Developers | Wiki | Forums | Bug Tracker | SVN | Download | Blog | Stats
Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

main.cpp

Go to the documentation of this file.
00001 /*       +------------------------------------+
00002  *       | Inspire Internet Relay Chat Daemon |
00003  *       +------------------------------------+
00004  *
00005  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
00006  * See: http://www.inspircd.org/wiki/index.php/Credits
00007  *
00008  * This program is free but copyrighted software; see
00009  *          the file COPYING for details.
00010  *
00011  * ---------------------------------------------------
00012  */
00013 
00014 /* $ModDesc: Provides a spanning tree server link protocol */
00015 
00016 #include "inspircd.h"
00017 #include "configreader.h"
00018 #include "users.h"
00019 #include "channels.h"
00020 #include "modules.h"
00021 #include "commands/cmd_whois.h"
00022 #include "commands/cmd_stats.h"
00023 #include "socket.h"
00024 #include "wildcard.h"
00025 #include "xline.h"
00026 #include "transport.h"
00027 
00028 #include "m_spanningtree/timesynctimer.h"
00029 #include "m_spanningtree/resolvers.h"
00030 #include "m_spanningtree/main.h"
00031 #include "m_spanningtree/utils.h"
00032 #include "m_spanningtree/treeserver.h"
00033 #include "m_spanningtree/link.h"
00034 #include "m_spanningtree/treesocket.h"
00035 #include "m_spanningtree/rconnect.h"
00036 #include "m_spanningtree/rsquit.h"
00037 
00038 /* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h m_spanningtree/rconnect.h m_spanningtree/rsquit.h */
00039 
00040 ModuleSpanningTree::ModuleSpanningTree(InspIRCd* Me)
00041         : Module(Me), max_local(0), max_global(0)
00042 {
00043         ServerInstance->UseInterface("InspSocketHook");
00044         Utils = new SpanningTreeUtilities(Me, this);
00045         command_rconnect = new cmd_rconnect(ServerInstance, this, Utils);
00046         ServerInstance->AddCommand(command_rconnect);
00047         command_rsquit = new cmd_rsquit(ServerInstance, this, Utils);
00048         ServerInstance->AddCommand(command_rsquit);
00049 }
00050 
00051 void ModuleSpanningTree::ShowLinks(TreeServer* Current, userrec* user, int hops)
00052 {
00053         std::string Parent = Utils->TreeRoot->GetName();
00054         if (Current->GetParent())
00055         {
00056                 Parent = Current->GetParent()->GetName();
00057         }
00058         for (unsigned int q = 0; q < Current->ChildCount(); q++)
00059         {
00060                 if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str()))))
00061                 {
00062                         if (*user->oper)
00063                         {
00064                                  ShowLinks(Current->GetChild(q),user,hops+1);
00065                         }
00066                 }
00067                 else
00068                 {
00069                         ShowLinks(Current->GetChild(q),user,hops+1);
00070                 }
00071         }
00072         /* Don't display the line if its a uline, hide ulines is on, and the user isnt an oper */
00073         if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetName().c_str())) && (!IS_OPER(user)))
00074                 return;
00075         /* Or if the server is hidden and they're not an oper */
00076         else if ((Current->Hidden) && (!IS_OPER(user)))
00077                 return;
00078 
00079         user->WriteServ("364 %s %s %s :%d %s",  user->nick,Current->GetName().c_str(),
00080                         (Utils->FlatLinks && (!IS_OPER(user))) ? ServerInstance->Config->ServerName : Parent.c_str(),
00081                         (Utils->FlatLinks && (!IS_OPER(user))) ? 0 : hops,
00082                         Current->GetDesc().c_str());
00083 }
00084 
00085 int ModuleSpanningTree::CountLocalServs()
00086 {
00087         return Utils->TreeRoot->ChildCount();
00088 }
00089 
00090 int ModuleSpanningTree::CountServs()
00091 {
00092         return Utils->serverlist.size();
00093 }
00094 
00095 void ModuleSpanningTree::HandleLinks(const char** parameters, int pcnt, userrec* user)
00096 {
00097         ShowLinks(Utils->TreeRoot,user,0);
00098         user->WriteServ("365 %s * :End of /LINKS list.",user->nick);
00099         return;
00100 }
00101 
00102 void ModuleSpanningTree::HandleLusers(const char** parameters, int pcnt, userrec* user)
00103 {
00104         unsigned int n_users = ServerInstance->UserCount();
00105 
00106         /* Only update these when someone wants to see them, more efficient */
00107         if ((unsigned int)ServerInstance->LocalUserCount() > max_local)
00108                 max_local = ServerInstance->LocalUserCount();
00109         if (n_users > max_global)
00110                 max_global = n_users;
00111 
00112         unsigned int ulined_count = 0;
00113         unsigned int ulined_local_count = 0;
00114 
00115         /* If ulined are hidden and we're not an oper, count the number of ulined servers hidden,
00116          * locally and globally (locally means directly connected to us)
00117          */
00118         if ((Utils->HideULines) && (!*user->oper))
00119         {
00120                 for (server_hash::iterator q = Utils->serverlist.begin(); q != Utils->serverlist.end(); q++)
00121                 {
00122                         if (ServerInstance->ULine(q->second->GetName().c_str()))
00123                         {
00124                                 ulined_count++;
00125                                 if (q->second->GetParent() == Utils->TreeRoot)
00126                                         ulined_local_count++;
00127                         }
00128                 }
00129         }
00130         user->WriteServ("251 %s :There are %d users and %d invisible on %d servers",user->nick,
00131                         n_users-ServerInstance->InvisibleUserCount(),
00132                         ServerInstance->InvisibleUserCount(),
00133                         ulined_count ? this->CountServs() - ulined_count : this->CountServs());
00134 
00135         if (ServerInstance->OperCount())
00136                 user->WriteServ("252 %s %d :operator(s) online",user->nick,ServerInstance->OperCount());
00137 
00138         if (ServerInstance->UnregisteredUserCount())
00139                 user->WriteServ("253 %s %d :unknown connections",user->nick,ServerInstance->UnregisteredUserCount());
00140         
00141         if (ServerInstance->ChannelCount())
00142                 user->WriteServ("254 %s %d :channels formed",user->nick,ServerInstance->ChannelCount());
00143         
00144         user->WriteServ("255 %s :I have %d clients and %d servers",user->nick,ServerInstance->LocalUserCount(),ulined_local_count ? this->CountLocalServs() - ulined_local_count : this->CountLocalServs());
00145         user->WriteServ("265 %s :Current Local Users: %d  Max: %d",user->nick,ServerInstance->LocalUserCount(),max_local);
00146         user->WriteServ("266 %s :Current Global Users: %d  Max: %d",user->nick,n_users,max_global);
00147         return;
00148 }
00149 
00150 std::string ModuleSpanningTree::TimeToStr(time_t secs)
00151 {
00152         time_t mins_up = secs / 60;
00153         time_t hours_up = mins_up / 60;
00154         time_t days_up = hours_up / 24;
00155         secs = secs % 60;
00156         mins_up = mins_up % 60;
00157         hours_up = hours_up % 24;
00158         return ((days_up ? (ConvToStr(days_up) + "d") : std::string(""))
00159                         + (hours_up ? (ConvToStr(hours_up) + "h") : std::string(""))
00160                         + (mins_up ? (ConvToStr(mins_up) + "m") : std::string(""))
00161                         + ConvToStr(secs) + "s");
00162 }
00163 
00164 const std::string ModuleSpanningTree::MapOperInfo(TreeServer* Current)
00165 {
00166         time_t secs_up = ServerInstance->Time() - Current->age;
00167         return (" [Up: " + TimeToStr(secs_up) + " Lag: "+ConvToStr(Current->rtt)+"ms]");
00168 }
00169 
00170 // WARNING: NOT THREAD SAFE - DONT GET ANY SMART IDEAS.
00171 void ModuleSpanningTree::ShowMap(TreeServer* Current, userrec* user, int depth, char matrix[250][250], float &totusers, float &totservers)
00172 {
00173         if (line < 250)
00174         {
00175                 for (int t = 0; t < depth; t++)
00176                 {
00177                         matrix[line][t] = ' ';
00178                 }
00179                 // For Aligning, we need to work out exactly how deep this thing is, and produce
00180                 // a 'Spacer' String to compensate.
00181                 char spacer[80];
00182                 memset(spacer,' ',80);
00183                 if ((80 - Current->GetName().length() - depth) > 1) {
00184                         spacer[80 - Current->GetName().length() - depth] = '\0';
00185                 }
00186                 else
00187                 {
00188                         spacer[5] = '\0';
00189                 }
00190                 float percent;
00191                 char text[250];
00192                 /* Neat and tidy default values, as we're dealing with a matrix not a simple string */
00193                 memset(text, 0, 250);
00194 
00195                 if (ServerInstance->clientlist->size() == 0) {
00196                         // If there are no users, WHO THE HELL DID THE /MAP?!?!?!
00197                         percent = 0;
00198                 }
00199                 else
00200                 {
00201                         percent = ((float)Current->GetUserCount() / (float)ServerInstance->clientlist->size()) * 100;
00202                 }
00203                 const std::string operdata = IS_OPER(user) ? MapOperInfo(Current) : "";
00204                 snprintf(text, 249, "%s %s%5d [%5.2f%%]%s", Current->GetName().c_str(), spacer, Current->GetUserCount(), percent, operdata.c_str());
00205                 totusers += Current->GetUserCount();
00206                 totservers++;
00207                 strlcpy(&matrix[line][depth], text, 249);
00208                 line++;
00209                 for (unsigned int q = 0; q < Current->ChildCount(); q++)
00210                 {
00211                         if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str()))))
00212                         {
00213                                 if (*user->oper)
00214                                 {
00215                                         ShowMap(Current->GetChild(q),user,(Utils->FlatLinks && (!*user->oper)) ? depth : depth+2,matrix,totusers,totservers);
00216                                 }
00217                         }
00218                         else
00219                         {
00220                                 ShowMap(Current->GetChild(q),user,(Utils->FlatLinks && (!*user->oper)) ? depth : depth+2,matrix,totusers,totservers);
00221                         }
00222                 }
00223         }
00224 }
00225 
00226 int ModuleSpanningTree::HandleMotd(const char** parameters, int pcnt, userrec* user)
00227 {
00228         if (pcnt > 0)
00229         {
00230                 if (match(ServerInstance->Config->ServerName, parameters[0]))
00231                         return 0;
00232 
00233                 /* Remote MOTD, the server is within the 1st parameter */
00234                 std::deque<std::string> params;
00235                 params.push_back(parameters[0]);
00236                 /* Send it out remotely, generate no reply yet */
00237                 TreeServer* s = Utils->FindServerMask(parameters[0]);
00238                 if (s)
00239                 {
00240                         params[0] = s->GetName();
00241                         Utils->DoOneToOne(user->nick, "MOTD", params, s->GetName());
00242                 }
00243                 else
00244                         user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]);
00245                 return 1;
00246         }
00247         return 0;
00248 }
00249 
00250 int ModuleSpanningTree::HandleAdmin(const char** parameters, int pcnt, userrec* user)
00251 {
00252         if (pcnt > 0)
00253         {
00254                 if (match(ServerInstance->Config->ServerName, parameters[0]))
00255                         return 0;
00256 
00257                 /* Remote ADMIN, the server is within the 1st parameter */
00258                 std::deque<std::string> params;
00259                 params.push_back(parameters[0]);
00260                 /* Send it out remotely, generate no reply yet */
00261                 TreeServer* s = Utils->FindServerMask(parameters[0]);
00262                 if (s)
00263                 {
00264                         params[0] = s->GetName();
00265                         Utils->DoOneToOne(user->nick, "ADMIN", params, s->GetName());
00266                 }
00267                 else
00268                         user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]);
00269                 return 1;
00270         }
00271         return 0;
00272 }
00273 
00274 int ModuleSpanningTree::HandleModules(const char** parameters, int pcnt, userrec* user)
00275 {
00276         if (pcnt > 0)
00277         {
00278                 if (match(ServerInstance->Config->ServerName, parameters[0]))
00279                         return 0;
00280 
00281                 std::deque<std::string> params;
00282                 params.push_back(parameters[0]);
00283                 TreeServer* s = Utils->FindServerMask(parameters[0]);
00284                 if (s)
00285                 {
00286                         params[0] = s->GetName();
00287                         Utils->DoOneToOne(user->nick, "MODULES", params, s->GetName());
00288                 }
00289                 else
00290                         user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]);
00291                 return 1;
00292         }
00293         return 0;
00294 }
00295 
00296 int ModuleSpanningTree::HandleStats(const char** parameters, int pcnt, userrec* user)
00297 {
00298         if (pcnt > 1)
00299         {
00300                 if (match(ServerInstance->Config->ServerName, parameters[1]))
00301                         return 0;
00302 
00303                 /* Remote STATS, the server is within the 2nd parameter */
00304                 std::deque<std::string> params;
00305                 params.push_back(parameters[0]);
00306                 params.push_back(parameters[1]);
00307                 /* Send it out remotely, generate no reply yet */
00308 
00309                 TreeServer* s = Utils->FindServerMask(parameters[1]);
00310                 if (s)
00311                 {
00312                         params[1] = s->GetName();
00313                         Utils->DoOneToOne(user->nick, "STATS", params, s->GetName());
00314                 }
00315                 else
00316                 {
00317                         user->WriteServ( "402 %s %s :No such server", user->nick, parameters[1]);
00318                 }
00319                 return 1;
00320         }
00321         return 0;
00322 }
00323 
00324 // Ok, prepare to be confused.
00325 // After much mulling over how to approach this, it struck me that
00326 // the 'usual' way of doing a /MAP isnt the best way. Instead of
00327 // keeping track of a ton of ascii characters, and line by line
00328 // under recursion working out where to place them using multiplications
00329 // and divisons, we instead render the map onto a backplane of characters
00330 // (a character matrix), then draw the branches as a series of "L" shapes
00331 // from the nodes. This is not only friendlier on CPU it uses less stack.
00332 void ModuleSpanningTree::HandleMap(const char** parameters, int pcnt, userrec* user)
00333 {
00334         // This array represents a virtual screen which we will
00335         // "scratch" draw to, as the console device of an irc
00336         // client does not provide for a proper terminal.
00337         float totusers = 0;
00338         float totservers = 0;
00339         char matrix[250][250];
00340         for (unsigned int t = 0; t < 250; t++)
00341         {
00342                 matrix[t][0] = '\0';
00343         }
00344         line = 0;
00345         // The only recursive bit is called here.
00346         ShowMap(Utils->TreeRoot,user,0,matrix,totusers,totservers);
00347         // Process each line one by one. The algorithm has a limit of
00348         // 128 servers (which is far more than a spanning tree should have
00349         // anyway, so we're ok). This limit can be raised simply by making
00350         // the character matrix deeper, 128 rows taking 10k of memory.
00351         for (int l = 1; l < line; l++)
00352         {
00353                 // scan across the line looking for the start of the
00354                 // servername (the recursive part of the algorithm has placed
00355                 // the servers at indented positions depending on what they
00356                 // are related to)
00357                 int first_nonspace = 0;
00358                 while (matrix[l][first_nonspace] == ' ')
00359                 {
00360                         first_nonspace++;
00361                 }
00362                 first_nonspace--;
00363                 // Draw the `- (corner) section: this may be overwritten by
00364                 // another L shape passing along the same vertical pane, becoming
00365                 // a |- (branch) section instead.
00366                 matrix[l][first_nonspace] = '-';
00367                 matrix[l][first_nonspace-1] = '`';
00368                 int l2 = l - 1;
00369                 // Draw upwards until we hit the parent server, causing possibly
00370                 // other corners (`-) to become branches (|-)
00371                 while ((matrix[l2][first_nonspace-1] == ' ') || (matrix[l2][first_nonspace-1] == '`'))
00372                 {
00373                         matrix[l2][first_nonspace-1] = '|';
00374                         l2--;
00375                 }
00376         }
00377         // dump the whole lot to the user. This is the easy bit, honest.
00378         for (int t = 0; t < line; t++)
00379         {
00380                 user->WriteServ("006 %s :%s",user->nick,&matrix[t][0]);
00381         }
00382         float avg_users = totusers / totservers;
00383         user->WriteServ("270 %s :%.0f server%s and %.0f user%s, average %.2f users per server",user->nick,totservers,(totservers > 1 ? "s" : ""),totusers,(totusers > 1 ? "s" : ""),avg_users);
00384         user->WriteServ("007 %s :End of /MAP",user->nick);
00385         return;
00386 }
00387 
00388 int ModuleSpanningTree::HandleSquit(const char** parameters, int pcnt, userrec* user)
00389 {
00390         TreeServer* s = Utils->FindServerMask(parameters[0]);
00391         if (s)
00392         {
00393                 if (s == Utils->TreeRoot)
00394                 {
00395                         user->WriteServ("NOTICE %s :*** SQUIT: Foolish mortal, you cannot make a server SQUIT itself! (%s matches local server name)",user->nick,parameters[0]);
00396                         return 1;
00397                 }
00398                 TreeSocket* sock = s->GetSocket();
00399                 if (sock)
00400                 {
00401                         ServerInstance->SNO->WriteToSnoMask('l',"SQUIT: Server \002%s\002 removed from network by %s",parameters[0],user->nick);
00402                         sock->Squit(s,std::string("Server quit by ") + user->GetFullRealHost());
00403                         ServerInstance->SE->DelFd(sock);
00404                         sock->Close();
00405                 }
00406                 else
00407                 {
00408                         if (IS_LOCAL(user))
00409                                 user->WriteServ("NOTICE %s :*** WARNING: Using SQUIT to split remote servers is deprecated. Please use RSQUIT instead.",user->nick);
00410                 }
00411         }
00412         else
00413         {
00414                  user->WriteServ("NOTICE %s :*** SQUIT: The server \002%s\002 does not exist on the network.",user->nick,parameters[0]);
00415         }
00416         return 1;
00417 }
00418 
00419 int ModuleSpanningTree::HandleTime(const char** parameters, int pcnt, userrec* user)
00420 {
00421         if ((IS_LOCAL(user)) && (pcnt))
00422         {
00423                 TreeServer* found = Utils->FindServerMask(parameters[0]);
00424                 if (found)
00425                 {
00426                         // we dont' override for local server
00427                         if (found == Utils->TreeRoot)
00428                                 return 0;
00429                         
00430                         std::deque<std::string> params;
00431                         params.push_back(found->GetName());
00432                         params.push_back(user->nick);
00433                         Utils->DoOneToOne(ServerInstance->Config->ServerName,"TIME",params,found->GetName());
00434                 }
00435                 else
00436                 {
00437                         user->WriteServ("402 %s %s :No such server",user->nick,parameters[0]);
00438                 }
00439         }
00440         return 1;
00441 }
00442 
00443 int ModuleSpanningTree::HandleRemoteWhois(const char** parameters, int pcnt, userrec* user)
00444 {
00445         if ((IS_LOCAL(user)) && (pcnt > 1))
00446         {
00447                 userrec* remote = ServerInstance->FindNick(parameters[1]);
00448                 if ((remote) && (remote->GetFd() < 0))
00449                 {
00450                         std::deque<std::string> params;
00451                         params.push_back(parameters[1]);
00452                         Utils->DoOneToOne(user->nick,"IDLE",params,remote->server);
00453                         return 1;
00454                 }
00455                 else if (!remote)
00456                 {
00457                         user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[1]);
00458                         user->WriteServ("318 %s %s :End of /WHOIS list.",user->nick, parameters[1]);
00459                         return 1;
00460                 }
00461         }
00462         return 0;
00463 }
00464 
00465 void ModuleSpanningTree::DoPingChecks(time_t curtime)
00466 {
00467         for (unsigned int j = 0; j < Utils->TreeRoot->ChildCount(); j++)
00468         {
00469                 TreeServer* serv = Utils->TreeRoot->GetChild(j);
00470                 TreeSocket* sock = serv->GetSocket();
00471                 if (sock)
00472                 {
00473                         if (curtime >= serv->NextPingTime())
00474                         {
00475                                 if (serv->AnsweredLastPing())
00476                                 {
00477                                         sock->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" PING "+serv->GetName());
00478                                         serv->SetNextPingTime(curtime + Utils->PingFreq);
00479                                         serv->LastPing = curtime;
00480 #ifndef WIN32
00481                                         timeval t;
00482                                         gettimeofday(&t, NULL);
00483                                         long ts = (t.tv_sec * 1000) + (t.tv_usec / 1000);
00484                                         serv->LastPingMsec = ts;
00485 #endif
00486                                         serv->Warned = false;
00487                                 }
00488                                 else
00489                                 {
00490                                         /* they didnt answer, boot them */
00491                                         sock->SendError("Ping timeout");
00492                                         sock->Squit(serv,"Ping timeout");
00493                                         ServerInstance->SE->DelFd(sock);
00494                                         sock->Close();
00495                                         return;
00496                                 }
00497                         }
00498                         else if ((Utils->PingWarnTime) && (!serv->Warned) && (curtime >= serv->NextPingTime() - (Utils->PingFreq - Utils->PingWarnTime)) && (!serv->AnsweredLastPing()))
00499                         {
00500                                 /* The server hasnt responded, send a warning to opers */
00501                                 ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", serv->GetName().c_str(), Utils->PingWarnTime);
00502                                 serv->Warned = true;
00503                         }
00504                 }
00505         }
00506 
00507         /* Cancel remote burst mode on any servers which still have it enabled due to latency/lack of data.
00508          * This prevents lost REMOTECONNECT notices
00509          */
00510         for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++)
00511                 Utils->SetRemoteBursting(i->second, false);
00512 }
00513 
00514 void ModuleSpanningTree::ConnectServer(Link* x)
00515 {
00516         bool ipvalid = true;
00517         QueryType start_type = DNS_QUERY_A;
00518 #ifdef IPV6
00519         start_type = DNS_QUERY_AAAA;
00520         if (strchr(x->IPAddr.c_str(),':'))
00521         {
00522                 in6_addr n;
00523                 if (inet_pton(AF_INET6, x->IPAddr.c_str(), &n) < 1)
00524                         ipvalid = false;
00525         }
00526         else
00527 #endif
00528         {
00529                 in_addr n;
00530                 if (inet_aton(x->IPAddr.c_str(),&n) < 1)
00531                         ipvalid = false;
00532         }
00533 
00534         /* Do we already have an IP? If so, no need to resolve it. */
00535         if (ipvalid)
00536         {
00537                 /* Gave a hook, but it wasnt one we know */
00538                 if ((!x->Hook.empty()) && (Utils->hooks.find(x->Hook.c_str()) == Utils->hooks.end()))
00539                         return;
00540                 TreeSocket* newsocket = new TreeSocket(Utils, ServerInstance, x->IPAddr,x->Port,false,x->Timeout ? x->Timeout : 10,x->Name.c_str(), x->Bind, x->Hook.empty() ? NULL : Utils->hooks[x->Hook.c_str()]);
00541                 if (newsocket->GetFd() > -1)
00542                 {
00543                         /* Handled automatically on success */
00544                 }
00545                 else
00546                 {
00547                         RemoteMessage(NULL, "CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(),strerror(errno));
00548                         if (ServerInstance->SocketCull.find(newsocket) == ServerInstance->SocketCull.end())
00549                                 ServerInstance->SocketCull[newsocket] = newsocket;
00550                         Utils->DoFailOver(x);
00551                 }
00552         }
00553         else
00554         {
00555                 try
00556                 {
00557                         bool cached;
00558                         ServernameResolver* snr = new ServernameResolver((Module*)this, Utils, ServerInstance,x->IPAddr, *x, cached, start_type);
00559                         ServerInstance->AddResolver(snr, cached);
00560                 }
00561                 catch (ModuleException& e)
00562                 {
00563                         RemoteMessage(NULL, "CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason());
00564                         Utils->DoFailOver(x);
00565                 }
00566         }
00567 }
00568 
00569 void ModuleSpanningTree::AutoConnectServers(time_t curtime)
00570 {
00571         for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
00572         {
00573                 if ((x->AutoConnect) && (curtime >= x->NextConnectTime))
00574                 {
00575                         x->NextConnectTime = curtime + x->AutoConnect;
00576                         TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());
00577                         if (x->FailOver.length())
00578                         {
00579                                 TreeServer* CheckFailOver = Utils->FindServer(x->FailOver.c_str());
00580                                 if (CheckFailOver)
00581                                 {
00582                                         /* The failover for this server is currently a member of the network.
00583                                          * The failover probably succeeded, where the main link did not.
00584                                          * Don't try the main link until the failover is gone again.
00585                                          */
00586                                         continue;
00587                                 }
00588                         }
00589                         if (!CheckDupe)
00590                         {
00591                                 // an autoconnected server is not connected. Check if its time to connect it
00592                                 ServerInstance->SNO->WriteToSnoMask('l',"AUTOCONNECT: Auto-connecting server \002%s\002 (%lu seconds until next attempt)",x->Name.c_str(),x->AutoConnect);
00593                                 this->ConnectServer(&(*x));
00594                         }
00595                 }
00596         }
00597 }
00598 
00599 int ModuleSpanningTree::HandleVersion(const char** parameters, int pcnt, userrec* user)
00600 {
00601         // we've already checked if pcnt > 0, so this is safe
00602         TreeServer* found = Utils->FindServerMask(parameters[0]);
00603         if (found)
00604         {
00605                 std::string Version = found->GetVersion();
00606                 user->WriteServ("351 %s :%s",user->nick,Version.c_str());
00607                 if (found == Utils->TreeRoot)
00608                 {
00609                         ServerInstance->Config->Send005(user);
00610                 }
00611         }
00612         else
00613         {
00614                 user->WriteServ("402 %s %s :No such server",user->nick,parameters[0]);
00615         }
00616         return 1;
00617 }
00618 
00619 /* This method will attempt to get a link message out to as many people as is required.
00620  * If a user is provided, and that user is local, then the user is sent the message using
00621  * WriteServ (they are the local initiator of that message). If the user is remote, they are
00622  * sent that message remotely via PUSH.
00623  * If the user is NULL, then the notice is sent locally via WriteToSnoMask with snomask 'l',
00624  * and remotely via SNONOTICE with mask 'l'.
00625  */
00626 void ModuleSpanningTree::RemoteMessage(userrec* user, const char* format, ...)
00627 {
00628         /* This could cause an infinite loop, because DoOneToMany() will, on error,
00629          * call TreeSocket::OnError(), which in turn will call this function to
00630          * notify everyone of the error. So, drop any messages that are generated
00631          * during the sending of another message. -Special */
00632         static bool SendingRemoteMessage = false;
00633         if (SendingRemoteMessage)
00634                 return;
00635         SendingRemoteMessage = true;
00636 
00637         std::deque<std::string> params;
00638         char text[MAXBUF];
00639         va_list argsPtr;
00640 
00641         va_start(argsPtr, format);
00642         vsnprintf(text, MAXBUF, format, argsPtr);
00643         va_end(argsPtr);
00644 
00645         if (!user)
00646         {
00647                 /* No user, target it generically at everyone */
00648                 ServerInstance->SNO->WriteToSnoMask('l', "%s", text);
00649                 params.push_back("l");
00650                 params.push_back(std::string(":") + text);
00651                 Utils->DoOneToMany(ServerInstance->Config->ServerName, "SNONOTICE", params);
00652         }
00653         else
00654         {
00655                 if (IS_LOCAL(user))
00656                         user->WriteServ("NOTICE %s :%s", user->nick, text);
00657                 else
00658                 {
00659                         params.push_back(user->nick);
00660                         params.push_back(std::string("::") + ServerInstance->Config->ServerName + " NOTICE " + user->nick + " :*** From " +
00661                                         ServerInstance->Config->ServerName+ ": " + text);
00662                         Utils->DoOneToMany(ServerInstance->Config->ServerName, "PUSH", params);
00663                 }
00664         }
00665         
00666         SendingRemoteMessage = false;
00667 }
00668         
00669 int ModuleSpanningTree::HandleConnect(const char** parameters, int pcnt, userrec* user)
00670 {
00671         for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
00672         {
00673                 if (ServerInstance->MatchText(x->Name.c_str(),parameters[0]))
00674                 {
00675                         TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());
00676                         if (!CheckDupe)
00677                         {
00678                                 RemoteMessage(user, "*** CONNECT: Connecting to server: \002%s\002 (%s:%d)",x->Name.c_str(),(x->HiddenFromStats ? "<hidden>" : x->IPAddr.c_str()),x->Port);
00679                                 ConnectServer(&(*x));
00680                                 return 1;
00681                         }
00682                         else
00683                         {
00684                                 RemoteMessage(user, "*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002",x->Name.c_str(),CheckDupe->GetParent()->GetName().c_str());
00685                                 return 1;
00686                         }
00687                 }
00688         }
00689         RemoteMessage(user, "*** CONNECT: No server matching \002%s\002 could be found in the config file.",parameters[0]);
00690         return 1;
00691 }
00692 
00693 void ModuleSpanningTree::BroadcastTimeSync()
00694 {
00695         if (Utils->MasterTime)
00696         {
00697                 std::deque<std::string> params;
00698                 params.push_back(ConvToStr(ServerInstance->Time(false)));
00699                 params.push_back("FORCE");
00700                 Utils->DoOneToMany(Utils->TreeRoot->GetName(), "TIMESET", params);
00701         }
00702 }
00703 
00704 int ModuleSpanningTree::OnStats(char statschar, userrec* user, string_list &results)
00705 {
00706         if ((statschar == 'c') || (statschar == 'n'))
00707         {
00708                 for (unsigned int i = 0; i < Utils->LinkBlocks.size(); i++)
00709                 {
00710                         results.push_back(std::string(ServerInstance->Config->ServerName)+" 213 "+user->nick+" "+statschar+" *@"+(Utils->LinkBlocks[i].HiddenFromStats ? "<hidden>" : Utils->LinkBlocks[i].IPAddr)+" * "+Utils->LinkBlocks[i].Name.c_str()+" "+ConvToStr(Utils->LinkBlocks[i].Port)+" "+(Utils->LinkBlocks[i].Hook.empty() ? "plaintext" : Utils->LinkBlocks[i].Hook)+" "+(Utils->LinkBlocks[i].AutoConnect ? 'a' : '-')+'s');
00711                         if (statschar == 'c')
00712                                 results.push_back(std::string(ServerInstance->Config->ServerName)+" 244 "+user->nick+" H * * "+Utils->LinkBlocks[i].Name.c_str());
00713                 }
00714                 results.push_back(std::string(ServerInstance->Config->ServerName)+" 219 "+user->nick+" "+statschar+" :End of /STATS report");
00715                 ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",(!strcmp(user->server,ServerInstance->Config->ServerName) ? "Stats" : "Remote stats"),statschar,user->nick,user->ident,user->host);
00716                 return 1;
00717         }
00718 
00719         if (statschar == 'p')
00720         {
00721                 /* show all server ports, after showing client ports. -- w00t */
00722 
00723                 for (unsigned int i = 0; i < Utils->Bindings.size(); i++)
00724                 {
00725                         std::string ip = Utils->Bindings[i]->IP;
00726                         if (ip.empty())
00727                                 ip = "*";
00728 
00729                         std::string transport("plaintext");
00730                         if (Utils->Bindings[i]->GetHook())
00731                                 transport = InspSocketNameRequest(this, Utils->Bindings[i]->GetHook()).Send();
00732 
00733                         results.push_back(ConvToStr(ServerInstance->Config->ServerName) + " 249 "+user->nick+" :" + ip + ":" + ConvToStr(Utils->Bindings[i]->port)+
00734                                 " (server, " + transport + ")");
00735                 }
00736         }
00737         return 0;
00738 }
00739 
00740 int ModuleSpanningTree::OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
00741 {
00742         /* If the command doesnt appear to be valid, we dont want to mess with it. */
00743         if (!validated)
00744                 return 0;
00745 
00746         if (command == "CONNECT")
00747         {
00748                 return this->HandleConnect(parameters,pcnt,user);
00749         }
00750         else if (command == "STATS")
00751         {
00752                 return this->HandleStats(parameters,pcnt,user);
00753         }
00754         else if (command == "MOTD")
00755         {
00756                 return this->HandleMotd(parameters,pcnt,user);
00757         }
00758         else if (command == "ADMIN")
00759         {
00760                 return this->HandleAdmin(parameters,pcnt,user);
00761         }
00762         else if (command == "SQUIT")
00763         {
00764                 return this->HandleSquit(parameters,pcnt,user);
00765         }
00766         else if (command == "MAP")
00767         {
00768                 this->HandleMap(parameters,pcnt,user);
00769                 return 1;
00770         }
00771         else if ((command == "TIME") && (pcnt > 0))
00772         {
00773                 return this->HandleTime(parameters,pcnt,user);
00774         }
00775         else if (command == "LUSERS")
00776         {
00777                 this->HandleLusers(parameters,pcnt,user);
00778                 return 1;
00779         }
00780         else if (command == "LINKS")
00781         {
00782                 this->HandleLinks(parameters,pcnt,user);
00783                 return 1;
00784         }
00785         else if (command == "WHOIS")
00786         {
00787                 if (pcnt > 1)
00788                 {
00789                         // remote whois
00790                         return this->HandleRemoteWhois(parameters,pcnt,user);
00791                 }
00792         }
00793         else if ((command == "VERSION") && (pcnt > 0))
00794         {
00795                 this->HandleVersion(parameters,pcnt,user);
00796                 return 1;
00797         }
00798         else if ((command == "MODULES") && (pcnt > 0))
00799         {
00800                 return this->HandleModules(parameters,pcnt,user);
00801         }
00802         return 0;
00803 }
00804 
00805 void ModuleSpanningTree::OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line)
00806 {
00807         if ((result == CMD_SUCCESS) && (ServerInstance->IsValidModuleCommand(command, pcnt, user)))
00808         {
00809                 // this bit of code cleverly routes all module commands
00810                 // to all remote severs *automatically* so that modules
00811                 // can just handle commands locally, without having
00812                 // to have any special provision in place for remote
00813                 // commands and linking protocols.
00814                 std::deque<std::string> params;
00815                 params.clear();
00816 
00817                 /* To make sure that parameters with spaces, or empty
00818                  * parameters, etc, are always send properly, *always*
00819                  * prefix the last parameter with a :. This also removes
00820                  * an extra strchr() */
00821                 for (int j = 0; j < pcnt - 1; j++)
00822                 {
00823                         params.push_back(parameters[j]);
00824                 }
00825                 if (pcnt)
00826                         params.push_back(":" + std::string(parameters[pcnt - 1]));
00827                 
00828                 Utils->DoOneToMany(user->nick,command,params);
00829         }
00830 }
00831 
00832 void ModuleSpanningTree::OnGetServerDescription(const std::string &servername,std::string &description)
00833 {
00834         TreeServer* s = Utils->FindServer(servername);
00835         if (s)
00836         {
00837                 description = s->GetDesc();
00838         }
00839 }
00840 
00841 void ModuleSpanningTree::OnUserInvite(userrec* source,userrec* dest,chanrec* channel)
00842 {
00843         if (IS_LOCAL(source))
00844         {
00845                 std::deque<std::string> params;
00846                 params.push_back(dest->nick);
00847                 params.push_back(channel->name);
00848                 Utils->DoOneToMany(source->nick,"INVITE",params);
00849         }
00850 }
00851 
00852 void ModuleSpanningTree::OnPostLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic)
00853 {
00854         std::deque<std::string> params;
00855         params.push_back(chan->name);
00856         params.push_back(":"+topic);
00857         Utils->DoOneToMany(user->nick,"TOPIC",params);
00858 }
00859 
00860 void ModuleSpanningTree::OnWallops(userrec* user, const std::string &text)
00861 {
00862         if (IS_LOCAL(user))
00863         {
00864                 std::deque<std::string> params;
00865                 params.push_back(":"+text);
00866                 Utils->DoOneToMany(user->nick,"WALLOPS",params);
00867         }
00868 }
00869 
00870 void ModuleSpanningTree::OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
00871 {
00872         if (target_type == TYPE_USER)
00873         {
00874                 userrec* d = (userrec*)dest;
00875                 if ((d->GetFd() < 0) && (IS_LOCAL(user)))
00876                 {
00877                         std::deque<std::string> params;
00878                         params.clear();
00879                         params.push_back(d->nick);
00880                         params.push_back(":"+text);
00881                         Utils->DoOneToOne(user->nick,"NOTICE",params,d->server);
00882                 }
00883         }
00884         else if (target_type == TYPE_CHANNEL)
00885         {
00886                 if (IS_LOCAL(user))
00887                 {
00888                         chanrec *c = (chanrec*)dest;
00889                         if (c)
00890                         {
00891                                 std::string cname = c->name;
00892                                 if (status)
00893                                         cname = status + cname;
00894                                 TreeServerList list;
00895                                 Utils->GetListOfServersForChannel(c,list,status,exempt_list);
00896                                 for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
00897                                 {
00898                                         TreeSocket* Sock = i->second->GetSocket();
00899                                         if (Sock)
00900                                                 Sock->WriteLine(":"+std::string(user->nick)+" NOTICE "+cname+" :"+text);
00901                                 }
00902                         }
00903                 }
00904         }
00905         else if (target_type == TYPE_SERVER)
00906         {
00907                 if (IS_LOCAL(user))
00908                 {
00909                         char* target = (char*)dest;
00910                         std::deque<std::string> par;
00911                         par.push_back(target);
00912                         par.push_back(":"+text);
00913                         Utils->DoOneToMany(user->nick,"NOTICE",par);
00914                 }
00915         }
00916 }
00917 
00918 void ModuleSpanningTree::OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
00919 {
00920         if (target_type == TYPE_USER)
00921         {
00922                 // route private messages which are targetted at clients only to the server
00923                 // which needs to receive them
00924                 userrec* d = (userrec*)dest;
00925                 if ((d->GetFd() < 0) && (IS_LOCAL(user)))
00926                 {
00927                         std::deque<std::string> params;
00928                         params.clear();
00929                         params.push_back(d->nick);
00930                         params.push_back(":"+text);
00931                         Utils->DoOneToOne(user->nick,"PRIVMSG",params,d->server);
00932                 }
00933         }
00934         else if (target_type == TYPE_CHANNEL)
00935         {
00936                 if (IS_LOCAL(user))
00937                 {
00938                         chanrec *c = (chanrec*)dest;
00939                         if (c)
00940                         {
00941                                 std::string cname = c->name;
00942                                 if (status)
00943                                         cname = status + cname;
00944                                 TreeServerList list;
00945                                 Utils->GetListOfServersForChannel(c,list,status,exempt_list);
00946                                 for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
00947                                 {
00948                                         TreeSocket* Sock = i->second->GetSocket();
00949                                         if (Sock)
00950                                                 Sock->WriteLine(":"+std::string(user->nick)+" PRIVMSG "+cname+" :"+text);
00951                                 }
00952                         }
00953                 }
00954         }
00955         else if (target_type == TYPE_SERVER)
00956         {
00957                 if (IS_LOCAL(user))
00958                 {
00959                         char* target = (char*)dest;
00960                         std::deque<std::string> par;
00961                         par.push_back(target);
00962                         par.push_back(":"+text);
00963                         Utils->DoOneToMany(user->nick,"PRIVMSG",par);
00964                 }
00965         }
00966 }
00967 
00968 void ModuleSpanningTree::OnBackgroundTimer(time_t curtime)
00969 {
00970         AutoConnectServers(curtime);
00971         DoPingChecks(curtime);
00972         if (curtime % 60 == 0)
00973         {
00974                 if (Utils->EnableTimeSync)
00975                         BroadcastTimeSync();
00976                 Utils->RefreshIPCache();
00977         }
00978 }
00979 
00980 void ModuleSpanningTree::OnUserJoin(userrec* user, chanrec* channel, bool &silent)
00981 {
00982         // Only do this for local users
00983         if (IS_LOCAL(user))
00984         {
00985                 if (channel->GetUserCounter() == 1)
00986                 {
00987                         std::deque<std::string> params;
00988                         // set up their permissions and the channel TS with FJOIN.
00989                         // All users are FJOINed now, because a module may specify
00990                         // new joining permissions for the user.
00991                         params.push_back(channel->name);
00992                         params.push_back(ConvToStr(channel->age));
00993                         params.push_back(std::string(channel->GetAllPrefixChars(user))+","+std::string(user->nick));
00994                         Utils->DoOneToMany(ServerInstance->Config->ServerName,"FJOIN",params);
00995                         /* First user in, sync the modes for the channel */
00996                         params.pop_back();
00997                         params.push_back(channel->ChanModes(true));
00998                         Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",params);
00999                 }
01000                 else
01001                 {
01002                         std::deque<std::string> params;
01003                         params.push_back(channel->name);
01004                         params.push_back(ConvToStr(channel->age));
01005                         Utils->DoOneToMany(user->nick,"JOIN",params);
01006                 }
01007         }
01008 }
01009 
01010 void ModuleSpanningTree::OnChangeHost(userrec* user, const std::string &newhost)
01011 {
01012         // only occurs for local clients
01013         if (user->registered != REG_ALL)
01014                 return;
01015         std::deque<std::string> params;
01016         params.push_back(newhost);
01017         Utils->DoOneToMany(user->nick,"FHOST",params);
01018 }
01019 
01020 void ModuleSpanningTree::OnChangeName(userrec* user, const std::string &gecos)
01021 {
01022         // only occurs for local clients
01023         if (user->registered != REG_ALL)
01024                 return;
01025         std::deque<std::string> params;
01026         params.push_back(gecos);
01027         Utils->DoOneToMany(user->nick,"FNAME",params);
01028 }
01029 
01030 void ModuleSpanningTree::OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent)
01031 {
01032         if (IS_LOCAL(user))
01033         {
01034                 std::deque<std::string> params;
01035                 params.push_back(channel->name);
01036                 if (!partmessage.empty())
01037                         params.push_back(":"+partmessage);
01038                 Utils->DoOneToMany(user->nick,"PART",params);
01039         }
01040 }
01041 
01042 void ModuleSpanningTree::OnUserConnect(userrec* user)
01043 {
01044         char agestr[MAXBUF];
01045         if (IS_LOCAL(user))
01046         {
01047                 std::deque<std::string> params;
01048                 snprintf(agestr,MAXBUF,"%lu",(unsigned long)user->age);
01049                 params.push_back(agestr);
01050                 params.push_back(user->nick);
01051                 params.push_back(user->host);
01052                 params.push_back(user->dhost);
01053                 params.push_back(user->ident);
01054                 params.push_back("+"+std::string(user->FormatModes()));
01055                 params.push_back(user->GetIPString());
01056                 params.push_back(":"+std::string(user->fullname));
01057                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"NICK",params);
01058                 // User is Local, change needs to be reflected!
01059                 TreeServer* SourceServer = Utils->FindServer(user->server);
01060                 if (SourceServer)
01061                 {
01062                         SourceServer->AddUserCount();
01063                 }
01064         }
01065 }
01066 
01067 void ModuleSpanningTree::OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
01068 {
01069         if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
01070         {
01071                 std::deque<std::string> params;
01072 
01073                 if (oper_message != reason)
01074                 {
01075                         params.push_back(":"+oper_message);
01076                         Utils->DoOneToMany(user->nick,"OPERQUIT",params);
01077                 }
01078                 params.clear();
01079                 params.push_back(":"+reason);
01080                 Utils->DoOneToMany(user->nick,"QUIT",params);
01081         }
01082         // Regardless, We need to modify the user Counts..
01083         TreeServer* SourceServer = Utils->FindServer(user->server);
01084         if (SourceServer)
01085         {
01086                 SourceServer->DelUserCount();
01087         }
01088 }
01089 
01090 void ModuleSpanningTree::OnUserPostNick(userrec* user, const std::string &oldnick)
01091 {
01092         if (IS_LOCAL(user))
01093         {
01094                 std::deque<std::string> params;
01095                 params.push_back(user->nick);
01096                 Utils->DoOneToMany(oldnick,"NICK",params);
01097         }
01098 }
01099 
01100 void ModuleSpanningTree::OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent)
01101 {
01102         if ((source) && (IS_LOCAL(source)))
01103         {
01104                 std::deque<std::string> params;
01105                 params.push_back(chan->name);
01106                 params.push_back(user->nick);
01107                 params.push_back(":"+reason);
01108                 Utils->DoOneToMany(source->nick,"KICK",params);
01109         }
01110         else if (!source)
01111         {
01112                 std::deque<std::string> params;
01113                 params.push_back(chan->name);
01114                 params.push_back(user->nick);
01115                 params.push_back(":"+reason);
01116                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"KICK",params);
01117         }
01118 }
01119 
01120 void ModuleSpanningTree::OnRemoteKill(userrec* source, userrec* dest, const std::string &reason, const std::string &operreason)
01121 {
01122         if (!IS_LOCAL(source)) return; // Don't need to do anything for a non-local source.
01123         std::deque<std::string> params;
01124         params.push_back(":"+reason);
01125         Utils->DoOneToMany(dest->nick,"OPERQUIT",params);
01126         params.clear();
01127         params.push_back(dest->nick);
01128         params.push_back(":"+reason);
01129         dest->SetOperQuit(operreason);
01130         Utils->DoOneToMany(source->nick,"KILL",params);
01131 }
01132 
01133 void ModuleSpanningTree::OnRehash(userrec* user, const std::string &parameter)
01134 {
01135         if (!parameter.empty())
01136         {
01137                 std::deque<std::string> params;
01138                 params.push_back(parameter);
01139                 Utils->DoOneToMany(user ? user->nick : ServerInstance->Config->ServerName, "REHASH", params);
01140                 // check for self
01141                 if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameter))
01142                 {
01143                         ServerInstance->WriteOpers("*** Remote rehash initiated locally by \002%s\002", user ? user->nick : ServerInstance->Config->ServerName);
01144                         ServerInstance->RehashServer();
01145                 }
01146         }
01147         Utils->ReadConfiguration(true);
01148         InitializeDisabledCommands(ServerInstance->Config->DisabledCommands, ServerInstance);
01149 }
01150 
01151 // note: the protocol does not allow direct umode +o except
01152 // via NICK with 8 params. sending OPERTYPE infers +o modechange
01153 // locally.
01154 void ModuleSpanningTree::OnOper(userrec* user, const std::string &opertype)
01155 {
01156         if (IS_LOCAL(user))
01157         {
01158                 std::deque<std::string> params;
01159                 params.push_back(opertype);
01160                 Utils->DoOneToMany(user->nick,"OPERTYPE",params);
01161         }
01162 }
01163 
01164 void ModuleSpanningTree::OnLine(userrec* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason)
01165 {
01166         if (!source)
01167         {
01168                 /* Server-set lines */
01169                 char data[MAXBUF];
01170                 snprintf(data,MAXBUF,"%c %s %s %lu %lu :%s", linetype, host.c_str(), ServerInstance->Config->ServerName, (unsigned long)ServerInstance->Time(false),
01171                                 (unsigned long)duration, reason.c_str());
01172                 std::deque<std::string> params;
01173                 params.push_back(data);
01174                 Utils->DoOneToMany(ServerInstance->Config->ServerName, "ADDLINE", params);
01175         }
01176         else
01177         {
01178                 if (IS_LOCAL(source))
01179                 {
01180                         char type[8];
01181                         snprintf(type,8,"%cLINE",linetype);
01182                         std::string stype = type;
01183                         if (adding)
01184                         {
01185                                 char sduration[MAXBUF];
01186                                 snprintf(sduration,MAXBUF,"%ld",duration);
01187                                 std::deque<std::string> params;
01188                                 params.push_back(host);
01189                                 params.push_back(sduration);
01190                                 params.push_back(":"+reason);
01191                                 Utils->DoOneToMany(source->nick,stype,params);
01192                         }
01193                         else
01194                         {
01195                                 std::deque<std::string> params;
01196                                 params.push_back(host);
01197                                 Utils->DoOneToMany(source->nick,stype,params);
01198                         }
01199                 }
01200         }
01201 }
01202 
01203 void ModuleSpanningTree::OnAddGLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask)
01204 {
01205         OnLine(source,hostmask,true,'G',duration,reason);
01206 }
01207         
01208 void ModuleSpanningTree::OnAddZLine(long duration, userrec* source, const std::string &reason, const std::string &ipmask)
01209 {
01210         OnLine(source,ipmask,true,'Z',duration,reason);
01211 }
01212 
01213 void ModuleSpanningTree::OnAddQLine(long duration, userrec* source, const std::string &reason, const std::string &nickmask)
01214 {
01215         OnLine(source,nickmask,true,'Q',duration,reason);
01216 }
01217 
01218 void ModuleSpanningTree::OnAddELine(long duration, userrec* source, const std::string &reason, const std::string &hostmask)
01219 {
01220         OnLine(source,hostmask,true,'E',duration,reason);
01221 }
01222 
01223 void ModuleSpanningTree::OnDelGLine(userrec* source, const std::string &hostmask)
01224 {
01225         OnLine(source,hostmask,false,'G',0,"");
01226 }
01227 
01228 void ModuleSpanningTree::OnDelZLine(userrec* source, const std::string &ipmask)
01229 {
01230         OnLine(source,ipmask,false,'Z',0,"");
01231 }
01232 
01233 void ModuleSpanningTree::OnDelQLine(userrec* source, const std::string &nickmask)
01234 {
01235         OnLine(source,nickmask,false,'Q',0,"");
01236 }
01237 
01238 void ModuleSpanningTree::OnDelELine(userrec* source, const std::string &hostmask)
01239 {
01240         OnLine(source,hostmask,false,'E',0,"");
01241 }
01242 
01243 void ModuleSpanningTree::OnMode(userrec* user, void* dest, int target_type, const std::string &text)
01244 {
01245         if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
01246         {
01247                 std::deque<std::string> params;
01248                 std::string command;
01249 
01250                 if (target_type == TYPE_USER)
01251                 {
01252                         userrec* u = (userrec*)dest;
01253                         params.push_back(u->nick);
01254                         params.push_back(text);
01255                         command = "MODE";
01256                 }
01257                 else
01258                 {
01259                         chanrec* c = (chanrec*)dest;
01260                         params.push_back(c->name);
01261                         params.push_back(ConvToStr(c->age));
01262                         params.push_back(text);
01263                         command = "FMODE";
01264                 }
01265                 Utils->DoOneToMany(user->nick, command, params);
01266         }
01267 }
01268 
01269 void ModuleSpanningTree::OnSetAway(userrec* user)
01270 {
01271         if (IS_LOCAL(user))
01272         {
01273                 std::deque<std::string> params;
01274                 params.push_back(":"+std::string(user->awaymsg));
01275                 Utils->DoOneToMany(user->nick,"AWAY",params);
01276         }
01277 }
01278 
01279 void ModuleSpanningTree::OnCancelAway(userrec* user)
01280 {
01281         if (IS_LOCAL(user))
01282         {
01283                 std::deque<std::string> params;
01284                 params.clear();
01285                 Utils->DoOneToMany(user->nick,"AWAY",params);
01286         }
01287 }
01288 
01289 void ModuleSpanningTree::ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline)
01290 {
01291         TreeSocket* s = (TreeSocket*)opaque;
01292         if (target)
01293         {
01294                 if (target_type == TYPE_USER)
01295                 {
01296                         userrec* u = (userrec*)target;
01297                         s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" FMODE "+u->nick+" "+ConvToStr(u->age)+" "+modeline);
01298                 }
01299                 else
01300                 {
01301                         chanrec* c = (chanrec*)target;
01302                         s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" FMODE "+c->name+" "+ConvToStr(c->age)+" "+modeline);
01303                 }
01304         }
01305 }
01306 
01307 void ModuleSpanningTree::ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata)
01308 {
01309         TreeSocket* s = (TreeSocket*)opaque;
01310         if (target)
01311         {
01312                 if (target_type == TYPE_USER)
01313                 {
01314                         userrec* u = (userrec*)target;
01315                         s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA "+u->nick+" "+extname+" :"+extdata);
01316                 }
01317                 else if (target_type == TYPE_CHANNEL)
01318                 {
01319                         chanrec* c = (chanrec*)target;
01320                         s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA "+c->name+" "+extname+" :"+extdata);
01321                 }
01322         }
01323         if (target_type == TYPE_OTHER)
01324         {
01325                 s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA * "+extname+" :"+extdata);
01326         }
01327 }
01328 
01329 void ModuleSpanningTree::OnEvent(Event* event)
01330 {
01331         std::deque<std::string>* params = (std::deque<std::string>*)event->GetData();
01332         if (event->GetEventID() == "send_metadata")
01333         {
01334                 if (params->size() < 3)
01335                         return;
01336                 (*params)[2] = ":" + (*params)[2];
01337                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"METADATA",*params);
01338         }
01339         else if (event->GetEventID() == "send_topic")
01340         {
01341                 if (params->size() < 2)
01342                         return;
01343                 (*params)[1] = ":" + (*params)[1];
01344                 params->insert(params->begin() + 1,ServerInstance->Config->ServerName);
01345                 params->insert(params->begin() + 1,ConvToStr(ServerInstance->Time(true)));
01346                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"FTOPIC",*params);
01347         }
01348         else if (event->GetEventID() == "send_mode")
01349         {
01350                 if (params->size() < 2)
01351                         return;
01352                 // Insert the TS value of the object, either userrec or chanrec
01353                 time_t ourTS = 0;
01354                 userrec* a = ServerInstance->FindNick((*params)[0]);
01355                 if (a)
01356                 {
01357                         ourTS = a->age;
01358                         Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODE",*params);
01359                         return;
01360                 }
01361                 else
01362                 {
01363                         chanrec* a = ServerInstance->FindChan((*params)[0]);
01364                         if (a)
01365                         {
01366                                 ourTS = a->age;
01367                                 params->insert(params->begin() + 1,ConvToStr(ourTS));
01368                                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",*params);
01369                         }
01370                 }
01371         }
01372         else if (event->GetEventID() == "send_mode_explicit")
01373         {
01374                 if (params->size() < 2)
01375                         return;
01376                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODE",*params);
01377         }
01378         else if (event->GetEventID() == "send_opers")
01379         {
01380                 if (params->size() < 1)
01381                         return;
01382                 (*params)[0] = ":" + (*params)[0];
01383                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"OPERNOTICE",*params);
01384         }
01385         else if (event->GetEventID() == "send_modeset")
01386         {
01387                 if (params->size() < 2)
01388                         return;
01389                 (*params)[1] = ":" + (*params)[1];
01390                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODENOTICE",*params);
01391         }
01392         else if (event->GetEventID() == "send_snoset")
01393         {
01394                 if (params->size() < 2)
01395                         return;
01396                 (*params)[1] = ":" + (*params)[1];
01397                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"SNONOTICE",*params);
01398         }
01399         else if (event->GetEventID() == "send_push")
01400         {
01401                 if (params->size() < 2)
01402                         return;
01403                         
01404                 userrec *a = ServerInstance->FindNick((*params)[0]);
01405                         
01406                 if (!a)
01407                         return;
01408                         
01409                 (*params)[1] = ":" + (*params)[1];
01410                 Utils->DoOneToOne(ServerInstance->Config->ServerName, "PUSH", *params, a->server);
01411         }
01412 }
01413 
01414 ModuleSpanningTree::~ModuleSpanningTree()
01415 {
01416         /* This will also free the listeners */
01417         delete Utils;
01418         ServerInstance->DoneWithInterface("InspSocketHook");
01419 }
01420 
01421 Version ModuleSpanningTree::GetVersion()
01422 {
01423         return Version(1,1,0,2,VF_VENDOR,API_VERSION);
01424 }
01425 
01426 void ModuleSpanningTree::Implements(char* List)
01427 {
01428         List[I_OnPreCommand] = List[I_OnGetServerDescription] = List[I_OnUserInvite] = List[I_OnPostLocalTopicChange] = 1;
01429         List[I_OnWallops] = List[I_OnUserNotice] = List[I_OnUserMessage] = List[I_OnBackgroundTimer] = 1;
01430         List[I_OnUserJoin] = List[I_OnChangeHost] = List[I_OnChangeName] = List[I_OnUserPart] = List[I_OnUserConnect] = 1;
01431         List[I_OnUserQuit] = List[I_OnUserPostNick] = List[I_OnUserKick] = List[I_OnRemoteKill] = List[I_OnRehash] = 1;
01432         List[I_OnOper] = List[I_OnAddGLine] = List[I_OnAddZLine] = List[I_OnAddQLine] = List[I_OnAddELine] = 1;
01433         List[I_OnDelGLine] = List[I_OnDelZLine] = List[I_OnDelQLine] = List[I_OnDelELine] = List[I_ProtoSendMode] = List[I_OnMode] = 1;
01434         List[I_OnStats] = List[I_ProtoSendMetaData] = List[I_OnEvent] = List[I_OnSetAway] = List[I_OnCancelAway] = List[I_OnPostCommand] = 1;
01435 }
01436 
01437 /* It is IMPORTANT that m_spanningtree is the last module in the chain
01438  * so that any activity it sees is FINAL, e.g. we arent going to send out
01439  * a NICK message before m_cloaking has finished putting the +x on the user,
01440  * etc etc.
01441  * Therefore, we return PRIORITY_LAST to make sure we end up at the END of
01442  * the module call queue.
01443  */
01444 Priority ModuleSpanningTree::Prioritize()
01445 {
01446         return PRIORITY_LAST;
01447 }
01448 
01449 MODULE_INIT(ModuleSpanningTree)
01450