/** If you make a change which breaks the protocol, increment this.
* If you completely change the protocol, completely change the number.
+ *
+ * IMPORTANT: If you make changes, document your changes here, without fail:
+ * http://www.inspircd.org/wiki/List_of_protocol_changes_between_versions
+ *
+ * Failure to document your protocol changes will result in a painfully
+ * painful death by pain. You have been warned.
*/
-const long ProtocolVersion = 1101;
+const long ProtocolVersion = 1102;
/*
* The server list in InspIRCd is maintained as two structures
*/
typedef nspace::hash_map<std::string, TreeServer*, nspace::hash<string>, irc::StrHashComp> server_hash;
+typedef std::map<TreeServer*,TreeServer*> TreeServerList;
/** The Link class might as well be a struct,
* but this is C++ and we don't believe in structs (!).
void ReadConfiguration(bool rebind);
/** Add a server to the server list for GetListOfServersForChannel
*/
- void AddThisServer(TreeServer* server, std::deque<TreeServer*> &list);
+ void AddThisServer(TreeServer* server, TreeServerList &list);
/** Compile a list of servers which contain members of channel c
*/
- void GetListOfServersForChannel(chanrec* c, std::deque<TreeServer*> &list);
+ void GetListOfServersForChannel(chanrec* c, TreeServerList &list, char status, const CUList &exempt_list);
/** Find a server by name
*/
TreeServer* FindServer(const std::string &ServerName);
userrec* who = NULL; /* User we are currently checking */
std::string channel = params[0]; /* Channel name, as a string */
time_t TS = atoi(params[1].c_str()); /* Timestamp given to us for remote side */
+ bool created = false;
/* Try and find the channel */
chanrec* chan = this->Instance->FindChan(channel);
/* Does this channel exist? if it does, get its REAL timestamp */
if (chan)
ourTS = chan->age;
+ else
+ created = true; /* don't perform deops, and set TS to correct time after processing. */
/* In 1.1, if they have the newer channel, we immediately clear
* all status modes from our users. We then accept their modes.
{
std::deque<std::string> param_list;
- if (chan)
- chan->age = TS;
-
/* Lower the TS here */
if (Utils->AnnounceTSChange && chan)
chan->WriteChannelWithServ(Instance->Config->ServerName,
"NOTICE %s :TS for %s changed from %lu to %lu", chan->name, chan->name, ourTS, TS);
ourTS = TS;
-
param_list.push_back(channel);
- /* Zap all the privilage modes on our side */
- this->RemoveStatus(Instance->Config->ServerName, param_list);
+
+ /* Zap all the privilage modes on our side, if the channel exists here */
+ if (!created)
+ {
+ this->RemoveStatus(Instance->Config->ServerName, param_list);
+ chan->age = TS;
+ }
}
/* Put the final parameter of the FJOIN into a tokenstream ready to split it */
free(mode_users[f]);
}
+ /* if we newly created the channel, set it's TS properly. */
+ if (created)
+ {
+ /* find created channel .. */
+ chan = this->Instance->FindChan(channel);
+ if (chan)
+ /* w00t said this shouldnt be needed but it is.
+ * This isnt strictly true, as chan can be NULL
+ * if a nick collision has occured and therefore
+ * the channel was never created.
+ */
+ chan->age = TS;
+ }
+
/* All done. That wasnt so bad was it, you can wipe
* the sweat from your forehead now. :-)
*/
if (numusers)
buffer.append(list).append("\r\n");
+ /* Sorry for the hax. Because newly created channels assume +nt,
+ * if this channel doesnt have +nt, explicitly send -n and -t for the missing modes.
+ */
+ bool inverted = false;
+ if (!c->IsModeSet('n'))
+ {
+ modes.append("-n");
+ inverted = true;
+ }
+ if (!c->IsModeSet('t'))
+ {
+ modes.append("-t");
+ inverted = true;
+ }
+ if (inverted)
+ {
+ modes.append("+");
+ }
+
for (BanList::iterator b = c->bans.begin(); b != c->bans.end(); b++)
{
modes.append("b");
}
return true;
}
+ else if (command == "OPERNOTICE")
+ {
+ std::string sourceserv = this->myhost;
+
+ if (this->InboundServerName != "")
+ sourceserv = this->InboundServerName;
+
+ if (params.size() >= 1)
+ Instance->WriteOpers("*** From " + sourceserv + ": " + params[0]);
+
+ return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
+ }
+ else if (command == "MODENOTICE")
+ {
+ std::string sourceserv = this->myhost;
+ if (this->InboundServerName != "")
+ sourceserv = this->InboundServerName;
+ if (params.size() >= 2)
+ {
+ Instance->WriteMode(params[0].c_str(), WM_AND, "*** From %s: %s", sourceserv.c_str(), params[1].c_str());
+ }
+
+ return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
+ }
+ else if (command == "SNONOTICE")
+ {
+ std::string sourceserv = this->myhost;
+ if (this->InboundServerName != "")
+ sourceserv = this->InboundServerName;
+ if (params.size() >= 2)
+ {
+ Instance->SNO->WriteToSnoMask(*(params[0].c_str()), "From " + sourceserv + ": "+ params[1]);
+ }
+
+ return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
+ }
else if (command == "ENDBURST")
{
this->bursting = false;
delete TreeRoot;
}
-void SpanningTreeUtilities::AddThisServer(TreeServer* server, std::deque<TreeServer*> &list)
+void SpanningTreeUtilities::AddThisServer(TreeServer* server, TreeServerList &list)
{
- for (unsigned int c = 0; c < list.size(); c++)
- {
- if (list[c] == server)
- {
- return;
- }
- }
- list.push_back(server);
+ if (list.find(server) == list.end())
+ list[server] = server;
}
/** returns a list of DIRECT servernames for a specific channel */
-void SpanningTreeUtilities::GetListOfServersForChannel(chanrec* c, std::deque<TreeServer*> &list)
+void SpanningTreeUtilities::GetListOfServersForChannel(chanrec* c, TreeServerList &list, char status, const CUList &exempt_list)
{
- CUList *ulist = c->GetUsers();
+ CUList *ulist;
+ switch (status)
+ {
+ case '@':
+ ulist = c->GetOppedUsers();
+ break;
+ case '%':
+ ulist = c->GetHalfoppedUsers();
+ break;
+ case '+':
+ ulist = c->GetVoicedUsers();
+ break;
+ default:
+ ulist = c->GetUsers();
+ break;
+ }
for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
{
- if (i->second->GetFd() < 0)
+ if ((i->second->GetFd() < 0) && (exempt_list.find(i->second) == exempt_list.end()))
{
TreeServer* best = this->BestRouteTo(i->second->server);
if (best)
bool SpanningTreeUtilities::DoOneToAllButSenderRaw(const std::string &data, const std::string &omit, const std::string &prefix, const irc::string &command, std::deque<std::string> ¶ms)
{
+ char pfx = 0;
TreeServer* omitroute = this->BestRouteTo(omit);
if ((command == "NOTICE") || (command == "PRIVMSG"))
{
/* Prefixes */
if ((*(params[0].c_str()) == '@') || (*(params[0].c_str()) == '%') || (*(params[0].c_str()) == '+'))
{
+ pfx = params[0][0];
params[0] = params[0].substr(1, params[0].length()-1);
}
if ((*(params[0].c_str()) != '#') && (*(params[0].c_str()) != '$'))
else
{
chanrec* c = ServerInstance->FindChan(params[0]);
- if (c)
+ userrec* u = ServerInstance->FindNick(prefix);
+ if (c && u)
{
- std::deque<TreeServer*> list;
- GetListOfServersForChannel(c,list);
- unsigned int lsize = list.size();
- for (unsigned int i = 0; i < lsize; i++)
+ CUList elist;
+ TreeServerList list;
+ FOREACH_MOD(I_OnBuildExemptList, OnBuildExemptList((command == "PRIVMSG" ? MSG_PRIVMSG : MSG_NOTICE), c, u, pfx, elist));
+ GetListOfServersForChannel(c,list,pfx,elist);
+
+ for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
{
- TreeSocket* Sock = list[i]->GetSocket();
- if ((Sock) && (list[i]->GetName() != omit) && (omitroute != list[i]))
+ TreeSocket* Sock = i->second->GetSocket();
+ if ((Sock) && (i->second->GetName() != omit) && (omitroute != i->second))
{
Sock->WriteLine(data);
}
if (n_users > max_global)
max_global = n_users;
- user->WriteServ("251 %s :There are %d users and %d invisible on %d servers",user->nick,n_users-ServerInstance->InvisibleUserCount(),ServerInstance->InvisibleUserCount(),this->CountServs());
+ unsigned int ulined_count = 0;
+ unsigned int ulined_local_count = 0;
+
+ /* If ulined are hidden and we're not an oper, count the number of ulined servers hidden,
+ * locally and globally (locally means directly connected to us)
+ */
+ if ((Utils->HideULines) && (!*user->oper))
+ {
+ for (server_hash::iterator q = Utils->serverlist.begin(); q != Utils->serverlist.end(); q++)
+ {
+ if (ServerInstance->ULine(q->second->GetName().c_str()))
+ {
+ ulined_count++;
+ if (q->second->GetParent() == Utils->TreeRoot)
+ ulined_local_count++;
+ }
+ }
+ }
+
+ user->WriteServ("251 %s :There are %d users and %d invisible on %d servers",user->nick,n_users-ServerInstance->InvisibleUserCount(),ServerInstance->InvisibleUserCount(),ulined_count ? this->CountServs() - ulined_count : this->CountServs());
if (ServerInstance->OperCount())
user->WriteServ("252 %s %d :operator(s) online",user->nick,ServerInstance->OperCount());
if (ServerInstance->UnregisteredUserCount())
user->WriteServ("253 %s %d :unknown connections",user->nick,ServerInstance->UnregisteredUserCount());
if (ServerInstance->ChannelCount())
user->WriteServ("254 %s %d :channels formed",user->nick,ServerInstance->ChannelCount());
- user->WriteServ("254 %s :I have %d clients and %d servers",user->nick,ServerInstance->LocalUserCount(),this->CountLocalServs());
+ user->WriteServ("254 %s :I have %d clients and %d servers",user->nick,ServerInstance->LocalUserCount(),ulined_local_count ? this->CountLocalServs() - ulined_local_count : this->CountLocalServs());
user->WriteServ("265 %s :Current Local Users: %d Max: %d",user->nick,ServerInstance->LocalUserCount(),max_local);
user->WriteServ("266 %s :Current Global Users: %d Max: %d",user->nick,n_users,max_global);
return;
virtual int OnStats(char statschar, userrec* user, string_list &results)
{
- if (statschar == 'c')
+ if ((statschar == 'c') || (statschar == 'n'))
{
for (unsigned int i = 0; i < Utils->LinkBlocks.size(); i++)
{
- results.push_back(std::string(ServerInstance->Config->ServerName)+" 213 "+user->nick+" C *@"+(Utils->LinkBlocks[i].HiddenFromStats ? "<hidden>" : Utils->LinkBlocks[i].IPAddr)+" * "+Utils->LinkBlocks[i].Name.c_str()+" "+ConvToStr(Utils->LinkBlocks[i].Port)+" "+(Utils->LinkBlocks[i].EncryptionKey != "" ? 'e' : '-')+(Utils->LinkBlocks[i].AutoConnect ? 'a' : '-')+'s');
- results.push_back(std::string(ServerInstance->Config->ServerName)+" 244 "+user->nick+" H * * "+Utils->LinkBlocks[i].Name.c_str());
+ 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].EncryptionKey != "" ? 'e' : '-')+(Utils->LinkBlocks[i].AutoConnect ? 'a' : '-')+'s');
+ if (statschar == 'c')
+ results.push_back(std::string(ServerInstance->Config->ServerName)+" 244 "+user->nick+" H * * "+Utils->LinkBlocks[i].Name.c_str());
}
results.push_back(std::string(ServerInstance->Config->ServerName)+" 219 "+user->nick+" "+statschar+" :End of /STATS report");
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);
}
}
- virtual void OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status)
+ virtual void OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
{
if (target_type == TYPE_USER)
{
std::string cname = c->name;
if (status)
cname = status + cname;
- std::deque<TreeServer*> list;
- Utils->GetListOfServersForChannel(c,list);
- unsigned int ucount = list.size();
- for (unsigned int i = 0; i < ucount; i++)
+ TreeServerList list;
+ Utils->GetListOfServersForChannel(c,list,status,exempt_list);
+
+ for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
{
- TreeSocket* Sock = list[i]->GetSocket();
+ TreeSocket* Sock = i->second->GetSocket();
if (Sock)
Sock->WriteLine(":"+std::string(user->nick)+" NOTICE "+cname+" :"+text);
}
}
}
- virtual void OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status)
+ virtual void OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
{
if (target_type == TYPE_USER)
{
std::string cname = c->name;
if (status)
cname = status + cname;
- std::deque<TreeServer*> list;
- Utils->GetListOfServersForChannel(c,list);
- unsigned int ucount = list.size();
- for (unsigned int i = 0; i < ucount; i++)
+ TreeServerList list;
+ Utils->GetListOfServersForChannel(c,list,status,exempt_list);
+
+ for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
{
- TreeSocket* Sock = list[i]->GetSocket();
+ TreeSocket* Sock = i->second->GetSocket();
if (Sock)
Sock->WriteLine(":"+std::string(user->nick)+" PRIVMSG "+cname+" :"+text);
}
params.push_back(ConvToStr(channel->age));
params.push_back(std::string(channel->GetAllPrefixChars(user))+","+std::string(user->nick));
Utils->DoOneToMany(ServerInstance->Config->ServerName,"FJOIN",params);
+ if (channel->GetUserCounter() == 1)
+ {
+ /* First user in, sync the modes for the channel */
+ params.pop_back();
+ /* This is safe, all inspircd servers default to +nt */
+ params.push_back("+nt");
+ Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",params);
+ }
}
}
{
if (params->size() < 2)
return;
- // Insert the TS value of the object, either userrec or chanrec
Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODE",*params);
}
+ else if (event->GetEventID() == "send_opers")
+ {
+ if (params->size() < 1)
+ return;
+ (*params)[0] = ":" + (*params)[0];
+ Utils->DoOneToMany(ServerInstance->Config->ServerName,"OPERNOTICE",*params);
+ }
+ else if (event->GetEventID() == "send_modeset")
+ {
+ if (params->size() < 2)
+ return;
+ (*params)[1] = ":" + (*params)[1];
+ Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODENOTICE",*params);
+ }
+ else if (event->GetEventID() == "send_snoset")
+ {
+ if (params->size() < 2)
+ return;
+ (*params)[1] = ":" + (*params)[1];
+ Utils->DoOneToMany(ServerInstance->Config->ServerName,"SNONOTICE",*params);
+ }
else if (event->GetEventID() == "send_push")
{
if (params->size() < 2)