summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/base.cpp25
-rw-r--r--src/channels.cpp11
-rw-r--r--src/command_parse.cpp28
-rw-r--r--src/coremods/core_hostname_lookup.cpp4
-rw-r--r--src/coremods/core_stub.cpp2
-rw-r--r--src/coremods/core_user/cmd_mode.cpp9
-rw-r--r--src/cull_list.cpp6
-rw-r--r--src/inspircd.cpp2
-rw-r--r--src/inspsocket.cpp16
-rw-r--r--src/listmode.cpp3
-rw-r--r--src/modes/cmode_k.cpp3
-rw-r--r--src/modules/extra/m_geoip.cpp4
-rw-r--r--src/modules/m_banredirect.cpp2
-rw-r--r--src/modules/m_blockamsg.cpp3
-rw-r--r--src/modules/m_callerid.cpp2
-rw-r--r--src/modules/m_cap.cpp2
-rw-r--r--src/modules/m_cgiirc.cpp5
-rw-r--r--src/modules/m_cloaking.cpp2
-rw-r--r--src/modules/m_conn_join.cpp3
-rw-r--r--src/modules/m_conn_waitpong.cpp2
-rw-r--r--src/modules/m_customtitle.cpp2
-rw-r--r--src/modules/m_dccallow.cpp2
-rw-r--r--src/modules/m_delayjoin.cpp4
-rw-r--r--src/modules/m_delaymsg.cpp2
-rw-r--r--src/modules/m_dnsbl.cpp7
-rw-r--r--src/modules/m_ident.cpp3
-rw-r--r--src/modules/m_ircv3.cpp58
-rw-r--r--src/modules/m_kicknorejoin.cpp81
-rw-r--r--src/modules/m_ldapauth.cpp4
-rw-r--r--src/modules/m_mlock.cpp2
-rw-r--r--src/modules/m_nicklock.cpp4
-rw-r--r--src/modules/m_repeat.cpp2
-rw-r--r--src/modules/m_sasl.cpp5
-rw-r--r--src/modules/m_services_account.cpp6
-rw-r--r--src/modules/m_silence.cpp3
-rw-r--r--src/modules/m_spanningtree/commandbuilder.h7
-rw-r--r--src/modules/m_spanningtree/commands.h24
-rw-r--r--src/modules/m_spanningtree/fjoin.cpp96
-rw-r--r--src/modules/m_spanningtree/main.cpp59
-rw-r--r--src/modules/m_spanningtree/main.h4
-rw-r--r--src/modules/m_spanningtree/metadata.cpp4
-rw-r--r--src/modules/m_spanningtree/pingtimer.cpp102
-rw-r--r--src/modules/m_spanningtree/pingtimer.h77
-rw-r--r--src/modules/m_spanningtree/pong.cpp4
-rw-r--r--src/modules/m_spanningtree/treeserver.cpp83
-rw-r--r--src/modules/m_spanningtree/treeserver.h53
-rw-r--r--src/modules/m_spanningtree/treesocket.h2
-rw-r--r--src/modules/m_spanningtree/treesocket1.cpp5
-rw-r--r--src/modules/m_sqlauth.cpp4
-rw-r--r--src/modules/m_sslinfo.cpp6
-rw-r--r--src/modules/m_swhois.cpp4
-rw-r--r--src/modules/m_topiclock.cpp2
-rw-r--r--src/modules/m_watch.cpp2
-rw-r--r--src/usermanager.cpp28
-rw-r--r--src/users.cpp121
-rw-r--r--src/xline.cpp2
56 files changed, 615 insertions, 393 deletions
diff --git a/src/base.cpp b/src/base.cpp
index c131f4dae..67b136ec8 100644
--- a/src/base.cpp
+++ b/src/base.cpp
@@ -23,7 +23,9 @@
#include "inspircd.h"
#include "base.h"
#include <time.h>
+#ifdef INSPIRCD_ENABLE_RTTI
#include <typeinfo>
+#endif
classbase::classbase()
{
@@ -34,8 +36,12 @@ classbase::classbase()
CullResult classbase::cull()
{
if (ServerInstance)
+#ifdef INSPIRCD_ENABLE_RTTI
ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "classbase::-%s @%p",
typeid(*this).name(), (void*)this);
+#else
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "classbase::- @%p", (void*)this);
+#endif
return CullResult();
}
@@ -89,7 +95,9 @@ ServiceProvider::~ServiceProvider()
{
}
-ExtensionItem::ExtensionItem(const std::string& Key, Module* mod) : ServiceProvider(mod, Key, SERVICE_METADATA)
+ExtensionItem::ExtensionItem(const std::string& Key, ExtensibleType exttype, Module* mod)
+ : ServiceProvider(mod, Key, SERVICE_METADATA)
+ , type(exttype)
{
}
@@ -201,7 +209,8 @@ Extensible::~Extensible()
ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "Extensible destructor called without cull @%p", (void*)this);
}
-LocalExtItem::LocalExtItem(const std::string& Key, Module* mod) : ExtensionItem(Key, mod)
+LocalExtItem::LocalExtItem(const std::string& Key, ExtensibleType exttype, Module* mod)
+ : ExtensionItem(Key, exttype, mod)
{
}
@@ -218,8 +227,10 @@ void LocalExtItem::unserialize(SerializeFormat format, Extensible* container, co
{
}
-LocalStringExt::LocalStringExt(const std::string& Key, Module* Owner)
- : SimpleExtItem<std::string>(Key, Owner) { }
+LocalStringExt::LocalStringExt(const std::string& Key, ExtensibleType exttype, Module* Owner)
+ : SimpleExtItem<std::string>(Key, exttype, Owner)
+{
+}
LocalStringExt::~LocalStringExt()
{
@@ -232,7 +243,8 @@ std::string LocalStringExt::serialize(SerializeFormat format, const Extensible*
return "";
}
-LocalIntExt::LocalIntExt(const std::string& Key, Module* mod) : LocalExtItem(Key, mod)
+LocalIntExt::LocalIntExt(const std::string& Key, ExtensibleType exttype, Module* mod)
+ : LocalExtItem(Key, exttype, mod)
{
}
@@ -264,7 +276,8 @@ void LocalIntExt::free(void*)
{
}
-StringExtItem::StringExtItem(const std::string& Key, Module* mod) : ExtensionItem(Key, mod)
+StringExtItem::StringExtItem(const std::string& Key, ExtensibleType exttype, Module* mod)
+ : ExtensionItem(Key, exttype, mod)
{
}
diff --git a/src/channels.cpp b/src/channels.cpp
index fdf0f76e1..948538ff4 100644
--- a/src/channels.cpp
+++ b/src/channels.cpp
@@ -86,14 +86,13 @@ void Channel::CheckDestroy()
if (res == MOD_RES_DENY)
return;
+ // If the channel isn't in chanlist then it is already in the cull list, don't add it again
chan_hash::iterator iter = ServerInstance->chanlist.find(this->name);
- /* kill the record */
- if (iter != ServerInstance->chanlist.end())
- {
- FOREACH_MOD(OnChannelDelete, (this));
- ServerInstance->chanlist.erase(iter);
- }
+ if ((iter == ServerInstance->chanlist.end()) || (iter->second != this))
+ return;
+ FOREACH_MOD(OnChannelDelete, (this));
+ ServerInstance->chanlist.erase(iter);
ClearInvites();
ServerInstance->GlobalCulls.AddItem(this);
}
diff --git a/src/command_parse.cpp b/src/command_parse.cpp
index 793569d5b..c93dac65f 100644
--- a/src/command_parse.cpp
+++ b/src/command_parse.cpp
@@ -327,10 +327,38 @@ void CommandParser::RemoveCommand(Command* x)
cmdlist.erase(n);
}
+CommandBase::CommandBase(Module* mod, const std::string& cmd, unsigned int minpara, unsigned int maxpara)
+ : ServiceProvider(mod, cmd, SERVICE_COMMAND)
+ , flags_needed(0)
+ , min_params(minpara)
+ , max_params(maxpara)
+ , use_count(0)
+ , disabled(false)
+ , works_before_reg(false)
+ , allow_empty_last_param(true)
+ , Penalty(1)
+{
+}
+
CommandBase::~CommandBase()
{
}
+void CommandBase::EncodeParameter(std::string& parameter, int index)
+{
+}
+
+RouteDescriptor CommandBase::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+ return ROUTE_LOCALONLY;
+}
+
+Command::Command(Module* mod, const std::string& cmd, unsigned int minpara, unsigned int maxpara)
+ : CommandBase(mod, cmd, minpara, maxpara)
+ , force_manual_route(false)
+{
+}
+
Command::~Command()
{
ServerInstance->Parser.RemoveCommand(this);
diff --git a/src/coremods/core_hostname_lookup.cpp b/src/coremods/core_hostname_lookup.cpp
index 2c9de3c86..11cc5bbba 100644
--- a/src/coremods/core_hostname_lookup.cpp
+++ b/src/coremods/core_hostname_lookup.cpp
@@ -183,8 +183,8 @@ class ModuleHostnameLookup : public Module
public:
ModuleHostnameLookup()
- : dnsLookup("dnsLookup", this)
- , ptrHosts("ptrHosts", this)
+ : dnsLookup("dnsLookup", ExtensionItem::EXT_USER, this)
+ , ptrHosts("ptrHosts", ExtensionItem::EXT_USER, this)
, DNS(this, "DNS")
{
dl = &dnsLookup;
diff --git a/src/coremods/core_stub.cpp b/src/coremods/core_stub.cpp
index bb6590261..28adb9e6a 100644
--- a/src/coremods/core_stub.cpp
+++ b/src/coremods/core_stub.cpp
@@ -102,7 +102,7 @@ class CommandServer : public Command
}
else
{
- user->WriteNumeric(ERR_NOTREGISTERED, ":You may not register as a server (servers have separate ports from clients, change your config)");
+ user->WriteNumeric(ERR_NOTREGISTERED, "SERVER :You may not register as a server (servers have separate ports from clients, change your config)");
}
return CMD_FAILURE;
}
diff --git a/src/coremods/core_user/cmd_mode.cpp b/src/coremods/core_user/cmd_mode.cpp
index f1e857fc1..190983d13 100644
--- a/src/coremods/core_user/cmd_mode.cpp
+++ b/src/coremods/core_user/cmd_mode.cpp
@@ -143,11 +143,16 @@ void CommandMode::DisplayCurrentModes(User* user, User* targetuser, Channel* tar
if (targetuser == user || user->HasPrivPermission("users/auspex"))
{
// Display user's current mode string
- user->WriteNumeric(RPL_UMODEIS, ":+%s", targetuser->FormatModes());
+ // XXX: Use WriteServ() because WriteNumeric() assumes the target (i.e. next word after the number)
+ // is 'user' and puts his nick there which is not what we want
+ user->WriteServ("%03d %s :+%s", RPL_UMODEIS, targetuser->nick.c_str(), targetuser->FormatModes());
if (targetuser->IsOper())
{
ModeHandler* snomask = ServerInstance->Modes->FindMode('s', MODETYPE_USER);
- user->WriteNumeric(RPL_SNOMASKIS, "%s :Server notice mask", snomask->GetUserParameter(user).c_str());
+ std::string snomaskstr = snomask->GetUserParameter(user);
+ // snomaskstr is empty if the snomask mode isn't set, otherwise it begins with a '+'.
+ // In the former case output a "+", not an empty string.
+ user->WriteServ("%03d %s %s%s :Server notice mask", RPL_SNOMASKIS, targetuser->nick.c_str(), (snomaskstr.empty() ? "+" : ""), snomaskstr.c_str());
}
}
else
diff --git a/src/cull_list.cpp b/src/cull_list.cpp
index 5cbe3aef3..73f2def51 100644
--- a/src/cull_list.cpp
+++ b/src/cull_list.cpp
@@ -21,7 +21,9 @@
#include "inspircd.h"
+#ifdef INSPIRCD_ENABLE_RTTI
#include <typeinfo>
+#endif
void CullList::Apply()
{
@@ -46,8 +48,12 @@ void CullList::Apply()
classbase* c = list[i];
if (gone.insert(c).second)
{
+#ifdef INSPIRCD_ENABLE_RTTI
ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "Deleting %s @%p", typeid(*c).name(),
(void*)c);
+#else
+ ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "Deleting @%p", (void*)c);
+#endif
c->cull();
queue.push_back(c);
}
diff --git a/src/inspircd.cpp b/src/inspircd.cpp
index 403ba5355..469539c5b 100644
--- a/src/inspircd.cpp
+++ b/src/inspircd.cpp
@@ -229,7 +229,7 @@ InspIRCd::InspIRCd(int argc, char** argv) :
* THIS MUST MATCH THE ORDER OF DECLARATION OF THE FUNCTORS, e.g. the methods
* themselves within the class.
*/
- OperQuit("operquit", NULL),
+ OperQuit("operquit", ExtensionItem::EXT_USER, NULL),
GenRandom(&HandleGenRandom),
IsChannel(&HandleIsChannel),
IsNick(&HandleIsNick),
diff --git a/src/inspsocket.cpp b/src/inspsocket.cpp
index 645947f56..ee5287e5f 100644
--- a/src/inspsocket.cpp
+++ b/src/inspsocket.cpp
@@ -339,15 +339,17 @@ void StreamSocket::DoWrite()
}
int rv_max = 0;
- iovec* iovecs = new iovec[bufcount];
- for(int i=0; i < bufcount; i++)
+ int rv;
{
- iovecs[i].iov_base = const_cast<char*>(sendq[i].data());
- iovecs[i].iov_len = sendq[i].length();
- rv_max += sendq[i].length();
+ iovec iovecs[MYIOV_MAX];
+ for (int i = 0; i < bufcount; i++)
+ {
+ iovecs[i].iov_base = const_cast<char*>(sendq[i].data());
+ iovecs[i].iov_len = sendq[i].length();
+ rv_max += sendq[i].length();
+ }
+ rv = writev(fd, iovecs, bufcount);
}
- int rv = writev(fd, iovecs, bufcount);
- delete[] iovecs;
if (rv == (int)sendq_len)
{
diff --git a/src/listmode.cpp b/src/listmode.cpp
index 1147a4ef2..35964dfb3 100644
--- a/src/listmode.cpp
+++ b/src/listmode.cpp
@@ -22,7 +22,8 @@
ListModeBase::ListModeBase(Module* Creator, const std::string& Name, char modechar, const std::string &eolstr, unsigned int lnum, unsigned int eolnum, bool autotidy, const std::string &ctag)
: ModeHandler(Creator, Name, modechar, PARAM_ALWAYS, MODETYPE_CHANNEL, MC_LIST),
listnumeric(lnum), endoflistnumeric(eolnum), endofliststring(eolstr), tidy(autotidy),
- configtag(ctag), extItem("listbase_mode_" + name + "_list", Creator)
+ configtag(ctag)
+ , extItem("listbase_mode_" + name + "_list", ExtensionItem::EXT_CHANNEL, Creator)
{
list = true;
}
diff --git a/src/modes/cmode_k.cpp b/src/modes/cmode_k.cpp
index 6738c046e..980b3215a 100644
--- a/src/modes/cmode_k.cpp
+++ b/src/modes/cmode_k.cpp
@@ -52,7 +52,8 @@ ModeAction ModeChannelKey::OnModeChange(User* source, User*, Channel* channel, s
channel->SetMode(this, adding);
if (adding)
{
- parameter.erase(32);
+ if (parameter.length() > maxkeylen)
+ parameter.erase(maxkeylen);
ext.set(channel, parameter);
}
else
diff --git a/src/modules/extra/m_geoip.cpp b/src/modules/extra/m_geoip.cpp
index 3561d1a5d..d21a82149 100644
--- a/src/modules/extra/m_geoip.cpp
+++ b/src/modules/extra/m_geoip.cpp
@@ -46,7 +46,9 @@ class ModuleGeoIP : public Module
}
public:
- ModuleGeoIP() : ext("geoip_cc", this), gi(NULL)
+ ModuleGeoIP()
+ : ext("geoip_cc", ExtensionItem::EXT_USER, this)
+ , gi(NULL)
{
}
diff --git a/src/modules/m_banredirect.cpp b/src/modules/m_banredirect.cpp
index 9833c720d..d3490acc0 100644
--- a/src/modules/m_banredirect.cpp
+++ b/src/modules/m_banredirect.cpp
@@ -50,7 +50,7 @@ class BanRedirect : public ModeWatcher
BanRedirect(Module* parent)
: ModeWatcher(parent, "ban", MODETYPE_CHANNEL)
, ban(parent, "ban")
- , extItem("banredirect", parent)
+ , extItem("banredirect", ExtensionItem::EXT_CHANNEL, parent)
{
}
diff --git a/src/modules/m_blockamsg.cpp b/src/modules/m_blockamsg.cpp
index b456606a8..9614203c3 100644
--- a/src/modules/m_blockamsg.cpp
+++ b/src/modules/m_blockamsg.cpp
@@ -53,7 +53,8 @@ class ModuleBlockAmsg : public Module
SimpleExtItem<BlockedMessage> blockamsg;
public:
- ModuleBlockAmsg() : blockamsg("blockamsg", this)
+ ModuleBlockAmsg()
+ : blockamsg("blockamsg", ExtensionItem::EXT_USER, this)
{
}
diff --git a/src/modules/m_callerid.cpp b/src/modules/m_callerid.cpp
index efbf1a81b..5c6d14a90 100644
--- a/src/modules/m_callerid.cpp
+++ b/src/modules/m_callerid.cpp
@@ -69,7 +69,7 @@ class callerid_data
struct CallerIDExtInfo : public ExtensionItem
{
CallerIDExtInfo(Module* parent)
- : ExtensionItem("callerid_data", parent)
+ : ExtensionItem("callerid_data", ExtensionItem::EXT_USER, parent)
{
}
diff --git a/src/modules/m_cap.cpp b/src/modules/m_cap.cpp
index bc79e59ec..db5d85f0f 100644
--- a/src/modules/m_cap.cpp
+++ b/src/modules/m_cap.cpp
@@ -42,7 +42,7 @@ class CommandCAP : public Command
public:
LocalIntExt reghold;
CommandCAP (Module* mod) : Command(mod, "CAP", 1),
- reghold("CAP_REGHOLD", mod)
+ reghold("CAP_REGHOLD", ExtensionItem::EXT_USER, mod)
{
works_before_reg = true;
}
diff --git a/src/modules/m_cgiirc.cpp b/src/modules/m_cgiirc.cpp
index 791045780..721d6ba08 100644
--- a/src/modules/m_cgiirc.cpp
+++ b/src/modules/m_cgiirc.cpp
@@ -74,7 +74,8 @@ class CommandWebirc : public Command
CGIHostlist Hosts;
CommandWebirc(Module* Creator)
: Command(Creator, "WEBIRC", 4),
- realhost("cgiirc_realhost", Creator), realip("cgiirc_realip", Creator)
+ realhost("cgiirc_realhost", ExtensionItem::EXT_USER, Creator)
+ , realip("cgiirc_realip", ExtensionItem::EXT_USER, Creator)
{
works_before_reg = true;
this->syntax = "password client hostname ip";
@@ -225,7 +226,7 @@ class ModuleCgiIRC : public Module
public:
ModuleCgiIRC()
: cmd(this)
- , waiting("cgiirc-delay", this)
+ , waiting("cgiirc-delay", ExtensionItem::EXT_USER, this)
, DNS(this, "DNS")
{
}
diff --git a/src/modules/m_cloaking.cpp b/src/modules/m_cloaking.cpp
index d0e7313b7..1534043ce 100644
--- a/src/modules/m_cloaking.cpp
+++ b/src/modules/m_cloaking.cpp
@@ -49,7 +49,7 @@ class CloakUser : public ModeHandler
CloakUser(Module* source)
: ModeHandler(source, "cloak", 'x', PARAM_NONE, MODETYPE_USER),
- ext("cloaked_host", source), debounce_ts(0), debounce_count(0)
+ ext("cloaked_host", ExtensionItem::EXT_USER, source), debounce_ts(0), debounce_count(0)
{
}
diff --git a/src/modules/m_conn_join.cpp b/src/modules/m_conn_join.cpp
index d5a095e7f..b22dbdf4d 100644
--- a/src/modules/m_conn_join.cpp
+++ b/src/modules/m_conn_join.cpp
@@ -66,7 +66,8 @@ class ModuleConnJoin : public Module
unsigned int defdelay;
public:
- ModuleConnJoin() : ext("join_timer", this)
+ ModuleConnJoin()
+ : ext("join_timer", ExtensionItem::EXT_USER, this)
{
}
diff --git a/src/modules/m_conn_waitpong.cpp b/src/modules/m_conn_waitpong.cpp
index 496b04c2d..87b6b51f2 100644
--- a/src/modules/m_conn_waitpong.cpp
+++ b/src/modules/m_conn_waitpong.cpp
@@ -32,7 +32,7 @@ class ModuleWaitPong : public Module
public:
ModuleWaitPong()
- : ext("waitpong_pingstr", this)
+ : ext("waitpong_pingstr", ExtensionItem::EXT_USER, this)
{
}
diff --git a/src/modules/m_customtitle.cpp b/src/modules/m_customtitle.cpp
index 3386e8cd7..67eca6dda 100644
--- a/src/modules/m_customtitle.cpp
+++ b/src/modules/m_customtitle.cpp
@@ -28,7 +28,7 @@ class CommandTitle : public Command
public:
StringExtItem ctitle;
CommandTitle(Module* Creator) : Command(Creator,"TITLE", 2),
- ctitle("ctitle", Creator)
+ ctitle("ctitle", ExtensionItem::EXT_USER, Creator)
{
syntax = "<user> <password>";
}
diff --git a/src/modules/m_dccallow.cpp b/src/modules/m_dccallow.cpp
index 7627ba8c7..2b8d1306c 100644
--- a/src/modules/m_dccallow.cpp
+++ b/src/modules/m_dccallow.cpp
@@ -257,7 +257,7 @@ class ModuleDCCAllow : public Module
public:
ModuleDCCAllow()
- : ext("dccallow", this)
+ : ext("dccallow", ExtensionItem::EXT_USER, this)
, cmd(this, ext)
{
}
diff --git a/src/modules/m_delayjoin.cpp b/src/modules/m_delayjoin.cpp
index 7e800572e..e864a8289 100644
--- a/src/modules/m_delayjoin.cpp
+++ b/src/modules/m_delayjoin.cpp
@@ -39,7 +39,9 @@ class ModuleDelayJoin : public Module
DelayJoinMode djm;
public:
LocalIntExt unjoined;
- ModuleDelayJoin() : djm(this), unjoined("delayjoin", this)
+ ModuleDelayJoin()
+ : djm(this)
+ , unjoined("delayjoin", ExtensionItem::EXT_MEMBERSHIP, this)
{
}
diff --git a/src/modules/m_delaymsg.cpp b/src/modules/m_delaymsg.cpp
index 32a0ba96e..f64297e15 100644
--- a/src/modules/m_delaymsg.cpp
+++ b/src/modules/m_delaymsg.cpp
@@ -25,7 +25,7 @@ class DelayMsgMode : public ParamMode<DelayMsgMode, LocalIntExt>
LocalIntExt jointime;
DelayMsgMode(Module* Parent)
: ParamMode<DelayMsgMode, LocalIntExt>(Parent, "delaymsg", 'd')
- , jointime("delaymsg", Parent)
+ , jointime("delaymsg", ExtensionItem::EXT_MEMBERSHIP, Parent)
{
levelrequired = OP_VALUE;
}
diff --git a/src/modules/m_dnsbl.cpp b/src/modules/m_dnsbl.cpp
index 63dda547f..7b38da4bf 100644
--- a/src/modules/m_dnsbl.cpp
+++ b/src/modules/m_dnsbl.cpp
@@ -236,7 +236,12 @@ class ModuleDNSBL : public Module
return DNSBLConfEntry::I_UNKNOWN;
}
public:
- ModuleDNSBL() : DNS(this, "DNS"), nameExt("dnsbl_match", this), countExt("dnsbl_pending", this) { }
+ ModuleDNSBL()
+ : DNS(this, "DNS")
+ , nameExt("dnsbl_match", ExtensionItem::EXT_USER, this)
+ , countExt("dnsbl_pending", ExtensionItem::EXT_USER, this)
+ {
+ }
Version GetVersion() CXX11_OVERRIDE
{
diff --git a/src/modules/m_ident.cpp b/src/modules/m_ident.cpp
index 3e87b8c5a..959e58a47 100644
--- a/src/modules/m_ident.cpp
+++ b/src/modules/m_ident.cpp
@@ -277,7 +277,8 @@ class ModuleIdent : public Module
bool NoLookupPrefix;
SimpleExtItem<IdentRequestSocket, stdalgo::culldeleter> ext;
public:
- ModuleIdent() : ext("ident_socket", this)
+ ModuleIdent()
+ : ext("ident_socket", ExtensionItem::EXT_USER, this)
{
}
diff --git a/src/modules/m_ircv3.cpp b/src/modules/m_ircv3.cpp
index 4eb54d2a6..b1c04cdf5 100644
--- a/src/modules/m_ircv3.cpp
+++ b/src/modules/m_ircv3.cpp
@@ -20,6 +20,26 @@
#include "modules/account.h"
#include "modules/cap.h"
+class WriteNeighboursWithExt : public User::ForEachNeighborHandler
+{
+ const LocalIntExt& ext;
+ const std::string& msg;
+
+ void Execute(LocalUser* user) CXX11_OVERRIDE
+ {
+ if (ext.get(user))
+ user->Write(msg);
+ }
+
+ public:
+ WriteNeighboursWithExt(User* user, const std::string& message, const LocalIntExt& extension)
+ : ext(extension)
+ , msg(message)
+ {
+ user->ForEachNeighbor(*this, false);
+ }
+};
+
class ModuleIRCv3 : public Module
{
GenericCap cap_accountnotify;
@@ -31,44 +51,6 @@ class ModuleIRCv3 : public Module
CUList last_excepts;
- void WriteNeighboursWithExt(User* user, const std::string& line, const LocalIntExt& ext)
- {
- IncludeChanList chans(user->chans.begin(), user->chans.end());
-
- std::map<User*, bool> exceptions;
- FOREACH_MOD(OnBuildNeighborList, (user, chans, exceptions));
-
- // Send it to all local users who were explicitly marked as neighbours by modules and have the required ext
- for (std::map<User*, bool>::const_iterator i = exceptions.begin(); i != exceptions.end(); ++i)
- {
- LocalUser* u = IS_LOCAL(i->first);
- if ((u) && (i->second) && (ext.get(u)))
- u->Write(line);
- }
-
- // Now consider sending it to all other users who has at least a common channel with the user
- std::set<User*> already_sent;
- for (IncludeChanList::const_iterator i = chans.begin(); i != chans.end(); ++i)
- {
- const Channel::MemberMap& userlist = (*i)->chan->GetUsers();
- for (Channel::MemberMap::const_iterator m = userlist.begin(); m != userlist.end(); ++m)
- {
- /*
- * Send the line if the channel member in question meets all of the following criteria:
- * - local
- * - not the user who is doing the action (i.e. whose channels we're iterating)
- * - has the given extension
- * - not on the except list built by modules
- * - we haven't sent the line to the member yet
- *
- */
- LocalUser* member = IS_LOCAL(m->first);
- if ((member) && (member != user) && (ext.get(member)) && (exceptions.find(member) == exceptions.end()) && (already_sent.insert(member).second))
- member->Write(line);
- }
- }
- }
-
public:
ModuleIRCv3() : cap_accountnotify(this, "account-notify"),
cap_awaynotify(this, "away-notify"),
diff --git a/src/modules/m_kicknorejoin.cpp b/src/modules/m_kicknorejoin.cpp
index fdb7b8f24..ebe2d45c9 100644
--- a/src/modules/m_kicknorejoin.cpp
+++ b/src/modules/m_kicknorejoin.cpp
@@ -25,14 +25,58 @@
#include "inspircd.h"
-typedef std::map<std::string, time_t> delaylist;
-
-struct KickRejoinData
+class KickRejoinData
{
- delaylist kicked;
- unsigned int delay;
+ struct KickedUser
+ {
+ std::string uuid;
+ time_t expire;
+
+ KickedUser(User* user, unsigned int Delay)
+ : uuid(user->uuid)
+ , expire(ServerInstance->Time() + Delay)
+ {
+ }
+ };
+
+ typedef std::vector<KickedUser> KickedList;
+
+ mutable KickedList kicked;
+
+ public:
+ const unsigned int delay;
KickRejoinData(unsigned int Delay) : delay(Delay) { }
+
+ bool canjoin(LocalUser* user) const
+ {
+ for (KickedList::iterator i = kicked.begin(); i != kicked.end(); )
+ {
+ KickedUser& rec = *i;
+ if (rec.expire > ServerInstance->Time())
+ {
+ if (rec.uuid == user->uuid)
+ return false;
+ ++i;
+ }
+ else
+ {
+ // Expired record, remove.
+ stdalgo::vector::swaperase(kicked, i);
+ if (kicked.empty())
+ break;
+ }
+ }
+ return true;
+ }
+
+ void add(User* user)
+ {
+ // One user can be in the list multiple times if the user gets kicked, force joins
+ // (skipping OnUserPreJoin) and gets kicked again, but that's okay because canjoin()
+ // works correctly in this case as well
+ kicked.push_back(KickedUser(user, delay));
+ }
};
/** Handles channel mode +J
@@ -79,28 +123,11 @@ public:
{
if (chan)
{
- KickRejoinData* data = kr.ext.get(chan);
- if (data)
+ const KickRejoinData* data = kr.ext.get(chan);
+ if ((data) && (!data->canjoin(user)))
{
- delaylist& kicked = data->kicked;
- for (delaylist::iterator iter = kicked.begin(); iter != kicked.end(); )
- {
- if (iter->second > ServerInstance->Time())
- {
- if (iter->first == user->uuid)
- {
- user->WriteNumeric(ERR_DELAYREJOIN, "%s :You must wait %u seconds after being kicked to rejoin (+J)",
- chan->name.c_str(), data->delay);
- return MOD_RES_DENY;
- }
- ++iter;
- }
- else
- {
- // Expired record, remove.
- kicked.erase(iter++);
- }
- }
+ user->WriteNumeric(ERR_DELAYREJOIN, "%s :You must wait %u seconds after being kicked to rejoin (+J)", chan->name.c_str(), data->delay);
+ return MOD_RES_DENY;
}
}
return MOD_RES_PASSTHRU;
@@ -114,7 +141,7 @@ public:
KickRejoinData* data = kr.ext.get(memb->chan);
if (data)
{
- data->kicked[memb->user->uuid] = ServerInstance->Time() + data->delay;
+ data->add(memb->user);
}
}
diff --git a/src/modules/m_ldapauth.cpp b/src/modules/m_ldapauth.cpp
index eee357ec0..804f6b821 100644
--- a/src/modules/m_ldapauth.cpp
+++ b/src/modules/m_ldapauth.cpp
@@ -307,8 +307,8 @@ class ModuleLDAPAuth : public Module
public:
ModuleLDAPAuth()
: LDAP(this, "LDAP")
- , ldapAuthed("ldapauth", this)
- , ldapVhost("ldapauth_vhost", this)
+ , ldapAuthed("ldapauth", ExtensionItem::EXT_USER, this)
+ , ldapVhost("ldapauth_vhost", ExtensionItem::EXT_USER, this)
{
me = this;
authed = &ldapAuthed;
diff --git a/src/modules/m_mlock.cpp b/src/modules/m_mlock.cpp
index d9c43ec10..9b0fa8dab 100644
--- a/src/modules/m_mlock.cpp
+++ b/src/modules/m_mlock.cpp
@@ -25,7 +25,7 @@ class ModuleMLock : public Module
public:
ModuleMLock()
- : mlock("mlock", this)
+ : mlock("mlock", ExtensionItem::EXT_CHANNEL, this)
{
}
diff --git a/src/modules/m_nicklock.cpp b/src/modules/m_nicklock.cpp
index 9bf16498a..8ac2e8b47 100644
--- a/src/modules/m_nicklock.cpp
+++ b/src/modules/m_nicklock.cpp
@@ -144,7 +144,9 @@ class ModuleNickLock : public Module
CommandNickunlock cmd2;
public:
ModuleNickLock()
- : locked("nick_locked", this), cmd1(this, locked), cmd2(this, locked)
+ : locked("nick_locked", ExtensionItem::EXT_USER, this)
+ , cmd1(this, locked)
+ , cmd2(this, locked)
{
}
diff --git a/src/modules/m_repeat.cpp b/src/modules/m_repeat.cpp
index ca6955040..820ef702f 100644
--- a/src/modules/m_repeat.cpp
+++ b/src/modules/m_repeat.cpp
@@ -122,7 +122,7 @@ class RepeatMode : public ParamMode<RepeatMode, SimpleExtItem<ChannelSettings> >
RepeatMode(Module* Creator)
: ParamMode<RepeatMode, SimpleExtItem<ChannelSettings> >(Creator, "repeat", 'E')
- , MemberInfoExt("repeat_memb", Creator)
+ , MemberInfoExt("repeat_memb", ExtensionItem::EXT_MEMBERSHIP, Creator)
{
}
diff --git a/src/modules/m_sasl.cpp b/src/modules/m_sasl.cpp
index 074362651..0a2c840bd 100644
--- a/src/modules/m_sasl.cpp
+++ b/src/modules/m_sasl.cpp
@@ -249,7 +249,10 @@ class ModuleSASL : public Module
public:
ModuleSASL()
- : authExt("sasl_auth", this), cap(this, "sasl"), auth(this, authExt, cap), sasl(this, authExt)
+ : authExt("sasl_auth", ExtensionItem::EXT_USER, this)
+ , cap(this, "sasl")
+ , auth(this, authExt, cap)
+ , sasl(this, authExt)
{
}
diff --git a/src/modules/m_services_account.cpp b/src/modules/m_services_account.cpp
index 9630128e0..aac0b9ce0 100644
--- a/src/modules/m_services_account.cpp
+++ b/src/modules/m_services_account.cpp
@@ -106,15 +106,13 @@ class AccountExtItemImpl : public AccountExtItem
{
public:
AccountExtItemImpl(Module* mod)
- : AccountExtItem("accountname", mod)
+ : AccountExtItem("accountname", ExtensionItem::EXT_USER, mod)
{
}
void unserialize(SerializeFormat format, Extensible* container, const std::string& value)
{
- User* user = dynamic_cast<User*>(container);
- if (!user)
- return;
+ User* user = static_cast<User*>(container);
StringExtItem::unserialize(format, container, value);
if (!value.empty())
diff --git a/src/modules/m_silence.cpp b/src/modules/m_silence.cpp
index 22de0ac8b..91822b4e4 100644
--- a/src/modules/m_silence.cpp
+++ b/src/modules/m_silence.cpp
@@ -106,7 +106,8 @@ class CommandSilence : public Command
public:
SimpleExtItem<silencelist> ext;
CommandSilence(Module* Creator, unsigned int &max) : Command(Creator, "SILENCE", 0),
- maxsilence(max), ext("silence_list", Creator)
+ maxsilence(max)
+ , ext("silence_list", ExtensionItem::EXT_USER, Creator)
{
allow_empty_last_param = false;
syntax = "{[+|-]<mask> <p|c|i|n|t|a|x>}";
diff --git a/src/modules/m_spanningtree/commandbuilder.h b/src/modules/m_spanningtree/commandbuilder.h
index c2b438deb..26eb4587f 100644
--- a/src/modules/m_spanningtree/commandbuilder.h
+++ b/src/modules/m_spanningtree/commandbuilder.h
@@ -75,6 +75,13 @@ class CmdBuilder
return *this;
}
+ template <typename InputIterator>
+ CmdBuilder& push_raw(InputIterator first, InputIterator last)
+ {
+ content.append(first, last);
+ return *this;
+ }
+
CmdBuilder& push(const std::string& s)
{
content.push_back(' ');
diff --git a/src/modules/m_spanningtree/commands.h b/src/modules/m_spanningtree/commands.h
index 89bd2bfee..1f7456426 100644
--- a/src/modules/m_spanningtree/commands.h
+++ b/src/modules/m_spanningtree/commands.h
@@ -114,6 +114,7 @@ class CommandOpertype : public UserOnlyServerCommand<CommandOpertype>
};
class TreeSocket;
+class FwdFJoinBuilder;
class CommandFJoin : public ServerCommand
{
/** Remove all modes from a channel, including statusmodes (+qaovh etc), simplemodes, parameter modes.
@@ -129,10 +130,11 @@ class CommandFJoin : public ServerCommand
* @param newname The new name of the channel; must be the same or a case change of the current name
*/
static void LowerTS(Channel* chan, time_t TS, const std::string& newname);
- void ProcessModeUUIDPair(const std::string& item, TreeServer* sourceserver, Channel* chan, Modes::ChangeList* modechangelist);
+ void ProcessModeUUIDPair(const std::string& item, TreeServer* sourceserver, Channel* chan, Modes::ChangeList* modechangelist, FwdFJoinBuilder& fwdfjoin);
public:
CommandFJoin(Module* Creator) : ServerCommand(Creator, "FJOIN", 3) { }
CmdResult Handle(User* user, std::vector<std::string>& params);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_LOCALONLY; }
class Builder : public CmdBuilder
{
@@ -140,13 +142,25 @@ class CommandFJoin : public ServerCommand
* a message or not
*/
static const size_t membid_max_digits = 20;
- static const size_t maxline = 480;
+ static const size_t maxline = 510;
std::string::size_type pos;
+ protected:
+ void add(Membership* memb, std::string::const_iterator mbegin, std::string::const_iterator mend);
+ bool has_room(std::string::size_type nummodes) const;
+
public:
- Builder(Channel* chan);
- void add(Membership* memb);
- bool has_room(Membership* memb) const;
+ Builder(Channel* chan, TreeServer* source = Utils->TreeRoot);
+ void add(Membership* memb)
+ {
+ add(memb, memb->modes.begin(), memb->modes.end());
+ }
+
+ bool has_room(Membership* memb) const
+ {
+ return has_room(memb->modes.size());
+ }
+
void clear();
const std::string& finalize();
};
diff --git a/src/modules/m_spanningtree/fjoin.cpp b/src/modules/m_spanningtree/fjoin.cpp
index bfe3592d2..25c1f6678 100644
--- a/src/modules/m_spanningtree/fjoin.cpp
+++ b/src/modules/m_spanningtree/fjoin.cpp
@@ -25,6 +25,22 @@
#include "treeserver.h"
#include "treesocket.h"
+/** FJOIN builder for rebuilding incoming FJOINs and splitting them up into multiple messages if necessary
+ */
+class FwdFJoinBuilder : public CommandFJoin::Builder
+{
+ TreeServer* const sourceserver;
+
+ public:
+ FwdFJoinBuilder(Channel* chan, TreeServer* server)
+ : CommandFJoin::Builder(chan, server)
+ , sourceserver(server)
+ {
+ }
+
+ void add(Membership* memb, std::string::const_iterator mbegin, std::string::const_iterator mend);
+};
+
/** FJOIN, almost identical to TS6 SJOIN, except for nicklist handling. */
CmdResult CommandFJoin::Handle(User* srcuser, std::vector<std::string>& params)
{
@@ -72,6 +88,30 @@ CmdResult CommandFJoin::Handle(User* srcuser, std::vector<std::string>& params)
* <membid> is a positive integer representing the id of the membership.
* If not present (in FJOINs coming from pre-1205 servers), 0 is assumed.
*
+ * Forwarding:
+ * FJOIN messages are forwarded with the new TS and modes. Prefix modes of
+ * members on the losing side are not forwarded.
+ * This is required to only have one server on each side of the network who
+ * decides the fate of a channel during a network merge. Otherwise, if the
+ * clock of a server is slightly off it may make a different decision than
+ * the rest of the network and desync.
+ * The prefix modes are always forwarded as-is, or not at all.
+ * One incoming FJOIN may result in more than one FJOIN being generated
+ * and forwarded mainly due to compatibility reasons with non-InspIRCd
+ * servers that don't handle more than 512 char long lines.
+ *
+ * Forwarding examples:
+ * Existing channel #chan with TS 1000, modes +n.
+ * Incoming: :220 FJOIN #chan 1000 +t :o,220AAAAAB:0
+ * Forwarded: :220 FJOIN #chan 1000 +nt :o,220AAAAAB:0
+ * Merge modes and forward the result. Forward their prefix modes as well.
+ *
+ * Existing channel #chan with TS 1000, modes +nt.
+ * Incoming: :220 FJOIN #CHAN 2000 +i :ov,220AAAAAB:0 o,220AAAAAC:20
+ * Forwarded: :220 FJOIN #chan 1000 +nt :,220AAAAAB:0 ,220AAAAAC:20
+ * Drop their modes, forward our modes and TS, use our channel name
+ * capitalization. Don't forward prefix modes.
+ *
*/
time_t TS = ServerCommand::ExtractTS(params[1]);
@@ -124,15 +164,22 @@ CmdResult CommandFJoin::Handle(User* srcuser, std::vector<std::string>& params)
TreeServer* const sourceserver = TreeServer::Get(srcuser);
+ // Build a new FJOIN for forwarding. Put the correct TS in it and the current modes of the channel
+ // after applying theirs. If they lost, the prefix modes from their message are not forwarded.
+ FwdFJoinBuilder fwdfjoin(chan, sourceserver);
+
/* Now, process every 'modes,uuid' pair */
irc::tokenstream users(params.back());
std::string item;
Modes::ChangeList* modechangelistptr = (apply_other_sides_modes ? &modechangelist : NULL);
while (users.GetToken(item))
{
- ProcessModeUUIDPair(item, sourceserver, chan, modechangelistptr);
+ ProcessModeUUIDPair(item, sourceserver, chan, modechangelistptr, fwdfjoin);
}
+ fwdfjoin.finalize();
+ fwdfjoin.Forward(sourceserver);
+
// Set prefix modes on their users if we lost the FJOIN or had equal TS
if (apply_other_sides_modes)
ServerInstance->Modes->Process(srcuser, chan, NULL, modechangelist, ModeParser::MODE_LOCALONLY);
@@ -140,7 +187,7 @@ CmdResult CommandFJoin::Handle(User* srcuser, std::vector<std::string>& params)
return CMD_SUCCESS;
}
-void CommandFJoin::ProcessModeUUIDPair(const std::string& item, TreeServer* sourceserver, Channel* chan, Modes::ChangeList* modechangelist)
+void CommandFJoin::ProcessModeUUIDPair(const std::string& item, TreeServer* sourceserver, Channel* chan, Modes::ChangeList* modechangelist, FwdFJoinBuilder& fwdfjoin)
{
std::string::size_type comma = item.find(',');
@@ -162,12 +209,13 @@ void CommandFJoin::ProcessModeUUIDPair(const std::string& item, TreeServer* sour
return;
}
+ std::string::const_iterator modeendit = item.begin(); // End of the "ov" mode string
/* Check if the user received at least one mode */
- if ((modechangelist) && (comma > 0) && (comma != std::string::npos))
+ if ((modechangelist) && (comma != std::string::npos))
{
+ modeendit += comma;
/* Iterate through the modes and see if they are valid here, if so, apply */
- std::string::const_iterator commait = item.begin()+comma;
- for (std::string::const_iterator i = item.begin(); i != commait; ++i)
+ for (std::string::const_iterator i = item.begin(); i != modeendit; ++i)
{
ModeHandler* mh = ServerInstance->Modes->FindMode(*i, MODETYPE_CHANNEL);
if (!mh)
@@ -180,7 +228,13 @@ void CommandFJoin::ProcessModeUUIDPair(const std::string& item, TreeServer* sour
Membership* memb = chan->ForceJoin(who, NULL, sourceserver->IsBursting());
if (!memb)
+ {
+ // User was already on the channel, forward because of the modes they potentially got
+ memb = chan->GetUser(who);
+ if (memb)
+ fwdfjoin.add(memb, item.begin(), modeendit);
return;
+ }
// Assign the id to the new Membership
Membership::Id membid = 0;
@@ -188,6 +242,9 @@ void CommandFJoin::ProcessModeUUIDPair(const std::string& item, TreeServer* sour
if (colon != std::string::npos)
membid = Membership::IdFromString(item.substr(colon+1));
memb->id = membid;
+
+ // Add member to fwdfjoin with prefix modes
+ fwdfjoin.add(memb, item.begin(), modeendit);
}
void CommandFJoin::RemoveStatus(Channel* c)
@@ -237,24 +294,24 @@ void CommandFJoin::LowerTS(Channel* chan, time_t TS, const std::string& newname)
chan->topicset = 0;
}
-CommandFJoin::Builder::Builder(Channel* chan)
- : CmdBuilder("FJOIN")
+CommandFJoin::Builder::Builder(Channel* chan, TreeServer* source)
+ : CmdBuilder(source->GetID(), "FJOIN")
{
push(chan->name).push_int(chan->age).push_raw(" +");
pos = str().size();
push_raw(chan->ChanModes(true)).push_raw(" :");
}
-void CommandFJoin::Builder::add(Membership* memb)
+void CommandFJoin::Builder::add(Membership* memb, std::string::const_iterator mbegin, std::string::const_iterator mend)
{
- push_raw(memb->modes).push_raw(',').push_raw(memb->user->uuid);
+ push_raw(mbegin, mend).push_raw(',').push_raw(memb->user->uuid);
push_raw(':').push_raw_int(memb->id);
push_raw(' ');
}
-bool CommandFJoin::Builder::has_room(Membership* memb) const
+bool CommandFJoin::Builder::has_room(std::string::size_type nummodes) const
{
- return ((str().size() + memb->modes.size() + UIDGenerator::UUID_LENGTH + 2 + membid_max_digits + 1) <= maxline);
+ return ((str().size() + nummodes + UIDGenerator::UUID_LENGTH + 2 + membid_max_digits + 1) <= maxline);
}
void CommandFJoin::Builder::clear()
@@ -269,3 +326,20 @@ const std::string& CommandFJoin::Builder::finalize()
content.erase(content.size()-1);
return str();
}
+
+void FwdFJoinBuilder::add(Membership* memb, std::string::const_iterator mbegin, std::string::const_iterator mend)
+{
+ // Pseudoserver compatibility:
+ // Some pseudoservers do not handle lines longer than 512 so we split long FJOINs into multiple messages.
+ // The forwarded FJOIN can end up being longer than the original one if we have more modes set and won, for example.
+
+ // Check if the member fits into the current message. If not, send it and prepare a new one.
+ if (!has_room(std::distance(mbegin, mend)))
+ {
+ finalize();
+ Forward(sourceserver);
+ clear();
+ }
+ // Add the member and their modes exactly as they sent them
+ CommandFJoin::Builder::add(memb, mbegin, mend);
+}
diff --git a/src/modules/m_spanningtree/main.cpp b/src/modules/m_spanningtree/main.cpp
index c21064683..31d822789 100644
--- a/src/modules/m_spanningtree/main.cpp
+++ b/src/modules/m_spanningtree/main.cpp
@@ -153,64 +153,6 @@ std::string ModuleSpanningTree::TimeToStr(time_t secs)
+ ConvToStr(secs) + "s");
}
-void ModuleSpanningTree::DoPingChecks(time_t curtime)
-{
- /*
- * Cancel remote burst mode on any servers which still have it enabled due to latency/lack of data.
- * This prevents lost REMOTECONNECT notices
- */
- long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
-
-restart:
- for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++)
- {
- TreeServer *s = i->second;
-
- // Skip myself
- if (s->IsRoot())
- continue;
-
- // Do not ping servers that are not fully connected yet!
- // Servers which are connected to us have IsLocal() == true and if they're fully connected
- // then Socket->LinkState == CONNECTED. Servers that are linked to another server are always fully connected.
- if (s->IsLocal() && s->GetSocket()->GetLinkState() != CONNECTED)
- continue;
-
- // Now do PING checks on all servers
- // Only ping if this server needs one
- if (curtime >= s->NextPingTime())
- {
- // And if they answered the last
- if (s->AnsweredLastPing())
- {
- // They did, send a ping to them
- s->SetNextPingTime(curtime + Utils->PingFreq);
- s->GetSocket()->WriteLine(CmdBuilder("PING").push(s->GetID()));
- s->LastPingMsec = ts;
- }
- else
- {
- // They didn't answer the last ping, if they are locally connected, get rid of them.
- if (s->IsLocal())
- {
- TreeSocket* sock = s->GetSocket();
- sock->SendError("Ping timeout");
- sock->Close();
- goto restart;
- }
- }
- }
-
- // If warn on ping enabled and not warned and the difference is sufficient and they didn't answer the last ping...
- if ((Utils->PingWarnTime) && (!s->Warned) && (curtime >= s->NextPingTime() - (Utils->PingFreq - Utils->PingWarnTime)) && (!s->AnsweredLastPing()))
- {
- /* The server hasnt responded, send a warning to opers */
- ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", s->GetName().c_str(), Utils->PingWarnTime);
- s->Warned = true;
- }
- }
-}
-
void ModuleSpanningTree::ConnectServer(Autoconnect* a, bool on_timer)
{
if (!a)
@@ -471,7 +413,6 @@ void ModuleSpanningTree::OnUserMessage(User* user, void* dest, int target_type,
void ModuleSpanningTree::OnBackgroundTimer(time_t curtime)
{
AutoConnectServers(curtime);
- DoPingChecks(curtime);
DoConnectTimeout(curtime);
}
diff --git a/src/modules/m_spanningtree/main.h b/src/modules/m_spanningtree/main.h
index c81eb2d0b..13c743f73 100644
--- a/src/modules/m_spanningtree/main.h
+++ b/src/modules/m_spanningtree/main.h
@@ -103,10 +103,6 @@ class ModuleSpanningTree : public Module
*/
ModResult HandleRemoteWhois(const std::vector<std::string>& parameters, User* user);
- /** Ping all local servers
- */
- void DoPingChecks(time_t curtime);
-
/** Connect a server locally
*/
void ConnectServer(Link* x, Autoconnect* y = NULL);
diff --git a/src/modules/m_spanningtree/metadata.cpp b/src/modules/m_spanningtree/metadata.cpp
index 13ccabc35..f758754b4 100644
--- a/src/modules/m_spanningtree/metadata.cpp
+++ b/src/modules/m_spanningtree/metadata.cpp
@@ -49,7 +49,7 @@ CmdResult CommandMetadata::Handle(User* srcuser, std::vector<std::string>& param
std::string value = params.size() < 4 ? "" : params[3];
ExtensionItem* item = ServerInstance->Extensions.GetItem(params[2]);
- if (item)
+ if ((item) && (item->type == ExtensionItem::EXT_CHANNEL))
item->unserialize(FORMAT_NETWORK, c, value);
FOREACH_MOD(OnDecodeMetaData, (c,params[2],value));
}
@@ -61,7 +61,7 @@ CmdResult CommandMetadata::Handle(User* srcuser, std::vector<std::string>& param
ExtensionItem* item = ServerInstance->Extensions.GetItem(params[1]);
std::string value = params.size() < 3 ? "" : params[2];
- if (item)
+ if ((item) && (item->type == ExtensionItem::EXT_USER))
item->unserialize(FORMAT_NETWORK, u, value);
FOREACH_MOD(OnDecodeMetaData, (u,params[1],value));
}
diff --git a/src/modules/m_spanningtree/pingtimer.cpp b/src/modules/m_spanningtree/pingtimer.cpp
new file mode 100644
index 000000000..1c96259bf
--- /dev/null
+++ b/src/modules/m_spanningtree/pingtimer.cpp
@@ -0,0 +1,102 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+#include "pingtimer.h"
+#include "treeserver.h"
+#include "commandbuilder.h"
+
+PingTimer::PingTimer(TreeServer* ts)
+ : Timer(Utils->PingFreq)
+ , server(ts)
+ , state(PS_SENDPING)
+{
+}
+
+PingTimer::State PingTimer::TickInternal()
+{
+ // Timer expired, take next action based on what happened last time
+ if (state == PS_SENDPING)
+ {
+ // Last ping was answered, send next ping
+ server->GetSocket()->WriteLine(CmdBuilder("PING").push(server->GetID()));
+ LastPingMsec = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
+ // Warn next unless warnings are disabled. If they are, jump straight to timeout.
+ if (Utils->PingWarnTime)
+ return PS_WARN;
+ else
+ return PS_TIMEOUT;
+ }
+ else if (state == PS_WARN)
+ {
+ // No pong arrived in PingWarnTime seconds, send a warning to opers
+ ServerInstance->SNO->WriteToSnoMask('l', "Server \002%s\002 has not responded to PING for %d seconds, high latency.", server->GetName().c_str(), GetInterval());
+ return PS_TIMEOUT;
+ }
+ else // PS_TIMEOUT
+ {
+ // They didn't answer the last ping, if they are locally connected, get rid of them
+ if (server->IsLocal())
+ {
+ TreeSocket* sock = server->GetSocket();
+ sock->SendError("Ping timeout");
+ sock->Close();
+ }
+
+ // If the server is non-locally connected, don't do anything until we get a PONG.
+ // This is to avoid pinging the server and warning opers more than once.
+ // If they do answer eventually, we will move to the PS_SENDPING state and ping them again.
+ return PS_IDLE;
+ }
+}
+
+void PingTimer::SetState(State newstate)
+{
+ state = newstate;
+
+ // Set when should the next Tick() happen based on the state
+ if (state == PS_SENDPING)
+ SetInterval(Utils->PingFreq);
+ else if (state == PS_WARN)
+ SetInterval(Utils->PingWarnTime);
+ else if (state == PS_TIMEOUT)
+ SetInterval(Utils->PingFreq - Utils->PingWarnTime);
+
+ // If state == PS_IDLE, do not set the timer, see above why
+}
+
+bool PingTimer::Tick(time_t currtime)
+{
+ if (server->IsDead())
+ return false;
+
+ SetState(TickInternal());
+ return false;
+}
+
+void PingTimer::OnPong()
+{
+ // Calculate RTT
+ long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
+ server->rtt = ts - LastPingMsec;
+
+ // Change state to send ping next, also reschedules the timer appropriately
+ SetState(PS_SENDPING);
+}
diff --git a/src/modules/m_spanningtree/pingtimer.h b/src/modules/m_spanningtree/pingtimer.h
new file mode 100644
index 000000000..753558689
--- /dev/null
+++ b/src/modules/m_spanningtree/pingtimer.h
@@ -0,0 +1,77 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+class TreeServer;
+
+/** Handles PINGing servers and killing them on timeout
+ */
+class PingTimer : public Timer
+{
+ enum State
+ {
+ /** Send PING next */
+ PS_SENDPING,
+ /** Warn opers next */
+ PS_WARN,
+ /** Kill the server next due to ping timeout */
+ PS_TIMEOUT,
+ /** Do nothing */
+ PS_IDLE
+ };
+
+ /** Server the timer is interacting with
+ */
+ TreeServer* const server;
+
+ /** What to do when the timer ticks next
+ */
+ State state;
+
+ /** Last ping time in milliseconds, used to calculate round trip time
+ */
+ unsigned long LastPingMsec;
+
+ /** Update internal state and reschedule timer according to the new state
+ * @param newstate State to change to
+ */
+ void SetState(State newstate);
+
+ /** Process timer tick event
+ * @return State to change to
+ */
+ State TickInternal();
+
+ /** Called by the TimerManager when the timer expires
+ * @param currtime Time now
+ * @return Always false, we reschedule ourselves instead
+ */
+ bool Tick(time_t currtime) CXX11_OVERRIDE;
+
+ public:
+ /** Construct the timer. This doesn't schedule the timer.
+ * @param server TreeServer to interact with
+ */
+ PingTimer(TreeServer* server);
+
+ /** Register a PONG from the server
+ */
+ void OnPong();
+};
diff --git a/src/modules/m_spanningtree/pong.cpp b/src/modules/m_spanningtree/pong.cpp
index a7dc64f83..5d97f2af2 100644
--- a/src/modules/m_spanningtree/pong.cpp
+++ b/src/modules/m_spanningtree/pong.cpp
@@ -35,9 +35,7 @@ CmdResult CommandPong::HandleServer(TreeServer* server, std::vector<std::string>
if (params[0] == ServerInstance->Config->GetSID())
{
// PONG for us
- long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
- server->rtt = ts - server->LastPingMsec;
- server->SetPingFlag();
+ server->OnPong();
}
return CMD_SUCCESS;
}
diff --git a/src/modules/m_spanningtree/treeserver.cpp b/src/modules/m_spanningtree/treeserver.cpp
index 98d7c8754..e004f897e 100644
--- a/src/modules/m_spanningtree/treeserver.cpp
+++ b/src/modules/m_spanningtree/treeserver.cpp
@@ -38,8 +38,9 @@ TreeServer::TreeServer()
, VersionString(ServerInstance->GetVersionString())
, fullversion(ServerInstance->GetVersionString(true))
, Socket(NULL), sid(ServerInstance->Config->GetSID()), behind_bursting(0), isdead(false)
+ , pingtimer(this)
, ServerUser(ServerInstance->FakeClient)
- , age(ServerInstance->Time()), Warned(false), UserCount(ServerInstance->Users.GetLocalUsers().size())
+ , age(ServerInstance->Time()), UserCount(ServerInstance->Users.GetLocalUsers().size())
, OperCount(0), rtt(0), StartBurst(0), Hidden(false)
{
AddHashEntry();
@@ -52,13 +53,14 @@ TreeServer::TreeServer()
TreeServer::TreeServer(const std::string& Name, const std::string& Desc, const std::string& id, TreeServer* Above, TreeSocket* Sock, bool Hide)
: Server(Name, Desc)
, Parent(Above), Socket(Sock), sid(id), behind_bursting(Parent->behind_bursting), isdead(false)
+ , pingtimer(this)
, ServerUser(new FakeUser(id, this))
- , age(ServerInstance->Time()), Warned(false), UserCount(0), OperCount(0), rtt(0), StartBurst(0), Hidden(Hide)
+ , age(ServerInstance->Time()), UserCount(0), OperCount(0), rtt(0), StartBurst(0), Hidden(Hide)
{
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "New server %s behind_bursting %u", GetName().c_str(), behind_bursting);
CheckULine();
- SetNextPingTime(ServerInstance->Time() + Utils->PingFreq);
- SetPingFlag();
+
+ ServerInstance->Timers.AddTimer(&pingtimer);
/* find the 'route' for this server (e.g. the one directly connected
* to the local server, which we can use to reach it)
@@ -112,7 +114,7 @@ TreeServer::TreeServer(const std::string& Name, const std::string& Desc, const s
*/
this->AddHashEntry();
- Parent->AddChild(this);
+ Parent->Children.push_back(this);
}
void TreeServer::BeginBurst(unsigned long startms)
@@ -127,11 +129,6 @@ void TreeServer::BeginBurst(unsigned long startms)
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Server %s started bursting at time %lu behind_bursting %u", sid.c_str(), startms, behind_bursting);
}
-const std::string& TreeServer::GetID()
-{
- return sid;
-}
-
void TreeServer::FinishBurstInternal()
{
// Check is needed because 1202 protocol servers don't send the bursting state of a server, so servers
@@ -140,11 +137,6 @@ void TreeServer::FinishBurstInternal()
behind_bursting--;
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "FinishBurstInternal() %s behind_bursting %u", GetName().c_str(), behind_bursting);
- if (!IsBehindBursting())
- {
- SetNextPingTime(ServerInstance->Time() + Utils->PingFreq);
- SetPingFlag();
- }
for (ChildServers::const_iterator i = Children.begin(); i != Children.end(); ++i)
{
TreeServer* child = *i;
@@ -168,7 +160,7 @@ void TreeServer::FinishBurst()
void TreeServer::SQuitChild(TreeServer* server, const std::string& reason)
{
DelServerEvent(Utils->Creator, server->GetName());
- DelChild(server);
+ stdalgo::erase(Children, server);
if (IsRoot())
{
@@ -266,65 +258,6 @@ void TreeServer::AddHashEntry()
Utils->sidlist[sid] = this;
}
-/** These accessors etc should be pretty self-
- * explanitory.
- */
-TreeServer* TreeServer::GetRoute()
-{
- return Route;
-}
-
-const std::string& TreeServer::GetVersion()
-{
- return VersionString;
-}
-
-void TreeServer::SetNextPingTime(time_t t)
-{
- this->NextPing = t;
- LastPingWasGood = false;
-}
-
-time_t TreeServer::NextPingTime()
-{
- return NextPing;
-}
-
-bool TreeServer::AnsweredLastPing()
-{
- return LastPingWasGood;
-}
-
-void TreeServer::SetPingFlag()
-{
- LastPingWasGood = true;
-}
-
-TreeSocket* TreeServer::GetSocket()
-{
- return Socket;
-}
-
-TreeServer* TreeServer::GetParent()
-{
- return Parent;
-}
-
-void TreeServer::SetVersion(const std::string &Version)
-{
- VersionString = Version;
-}
-
-void TreeServer::AddChild(TreeServer* Child)
-{
- Children.push_back(Child);
-}
-
-bool TreeServer::DelChild(TreeServer* Child)
-{
- return stdalgo::erase(Children, Child);
-}
-
CullResult TreeServer::cull()
{
// Recursively cull all servers that are under us in the tree
diff --git a/src/modules/m_spanningtree/treeserver.h b/src/modules/m_spanningtree/treeserver.h
index f06e0bafa..1a0203ba0 100644
--- a/src/modules/m_spanningtree/treeserver.h
+++ b/src/modules/m_spanningtree/treeserver.h
@@ -22,6 +22,7 @@
#pragma once
#include "treesocket.h"
+#include "pingtimer.h"
/** Each server in the tree is represented by one class of
* type TreeServer. A locally connected TreeServer can
@@ -49,8 +50,6 @@ class TreeServer : public Server
std::string fullversion;
TreeSocket* Socket; /* Socket used to communicate with this server */
- time_t NextPing; /* After this time, the server should be PINGed*/
- bool LastPingWasGood; /* True if the server responded to the last PING with a PONG */
std::string sid; /* Server ID */
/** Counter counting how many servers are bursting in front of this server, including
@@ -64,6 +63,10 @@ class TreeServer : public Server
*/
bool isdead;
+ /** Timer handling PINGing the server and killing it on timeout
+ */
+ PingTimer pingtimer;
+
/** This method is used to add this TreeServer to the
* hash maps. It is only called by the constructors.
*/
@@ -82,8 +85,6 @@ class TreeServer : public Server
FakeUser* const ServerUser; /* User representing this server */
const time_t age;
- bool Warned; /* True if we've warned opers about high latency on this server */
-
unsigned int UserCount; /* How many users are on this server? [note: doesn't care about +i] */
unsigned int OperCount; /* How many opers are on this server? */
@@ -119,7 +120,7 @@ class TreeServer : public Server
* The 'route' is defined as the locally-
* connected server which can be used to reach this server.
*/
- TreeServer* GetRoute();
+ TreeServer* GetRoute() const { return Route; }
/** Returns true if this server is the tree root (i.e.: us)
*/
@@ -136,25 +137,13 @@ class TreeServer : public Server
/** Get server version string
*/
- const std::string& GetVersion();
+ const std::string& GetVersion() const { return VersionString; }
/** Get the full version string of this server
* @return The full version string of this server, including patch version and other info
*/
const std::string& GetFullVersion() const { return fullversion; }
- /** Set time we are next due to ping this server
- */
- void SetNextPingTime(time_t t);
-
- /** Get the time we are next due to ping this server
- */
- time_t NextPingTime();
-
- /** Last ping time in milliseconds, used to calculate round trip time
- */
- unsigned long LastPingMsec;
-
/** Round trip time of last ping
*/
unsigned long rtt;
@@ -167,27 +156,19 @@ class TreeServer : public Server
*/
bool Hidden;
- /** True if the server answered their last ping
- */
- bool AnsweredLastPing();
-
- /** Set the server as responding to its last ping
- */
- void SetPingFlag();
-
/** Get the TreeSocket pointer for local servers.
* For remote servers, this returns NULL.
*/
- TreeSocket* GetSocket();
+ TreeSocket* GetSocket() const { return Socket; }
/** Get the parent server.
* For the root node, this returns NULL.
*/
- TreeServer* GetParent();
+ TreeServer* GetParent() const { return Parent; }
/** Set the server version string
*/
- void SetVersion(const std::string &Version);
+ void SetVersion(const std::string& verstr) { VersionString = verstr; }
/** Set the full version string
* @param verstr The version string to set
@@ -204,17 +185,9 @@ class TreeServer : public Server
*/
const ChildServers& GetChildren() const { return Children; }
- /** Add a child server
- */
- void AddChild(TreeServer* Child);
-
- /** Delete a child server, return false if it didn't exist.
- */
- bool DelChild(TreeServer* Child);
-
/** Get server ID
*/
- const std::string& GetID();
+ const std::string& GetID() const { return sid; }
/** Marks a server as having finished bursting and performs appropriate actions.
*/
@@ -242,6 +215,10 @@ class TreeServer : public Server
*/
void BeginBurst(unsigned long startms = 0);
+ /** Register a PONG from the server
+ */
+ void OnPong() { pingtimer.OnPong(); }
+
CullResult cull();
/** Destructor, deletes ServerUser unless IsRoot()
diff --git a/src/modules/m_spanningtree/treesocket.h b/src/modules/m_spanningtree/treesocket.h
index 6dc584537..4887623c1 100644
--- a/src/modules/m_spanningtree/treesocket.h
+++ b/src/modules/m_spanningtree/treesocket.h
@@ -174,7 +174,7 @@ class TreeSocket : public BufferedSocket
/** Get link state
*/
- ServerState GetLinkState();
+ ServerState GetLinkState() const { return LinkState; }
/** Get challenge set in our CAPAB for challenge/response
*/
diff --git a/src/modules/m_spanningtree/treesocket1.cpp b/src/modules/m_spanningtree/treesocket1.cpp
index d2fec0118..025bd1e61 100644
--- a/src/modules/m_spanningtree/treesocket1.cpp
+++ b/src/modules/m_spanningtree/treesocket1.cpp
@@ -69,11 +69,6 @@ TreeSocket::TreeSocket(int newfd, ListenSocket* via, irc::sockets::sockaddrs* cl
Utils->timeoutlist[this] = std::pair<std::string, int>(linkID, 30);
}
-ServerState TreeSocket::GetLinkState()
-{
- return this->LinkState;
-}
-
void TreeSocket::CleanNegotiationInfo()
{
// connect is good, reset the autoconnect block (if used)
diff --git a/src/modules/m_sqlauth.cpp b/src/modules/m_sqlauth.cpp
index 8a0b80ce1..1a5b68dd9 100644
--- a/src/modules/m_sqlauth.cpp
+++ b/src/modules/m_sqlauth.cpp
@@ -78,7 +78,9 @@ class ModuleSQLAuth : public Module
bool verbose;
public:
- ModuleSQLAuth() : pendingExt("sqlauth-wait", this), SQL(this, "SQL")
+ ModuleSQLAuth()
+ : pendingExt("sqlauth-wait", ExtensionItem::EXT_USER, this)
+ , SQL(this, "SQL")
{
}
diff --git a/src/modules/m_sslinfo.cpp b/src/modules/m_sslinfo.cpp
index c354e4e0e..f861f1236 100644
--- a/src/modules/m_sslinfo.cpp
+++ b/src/modules/m_sslinfo.cpp
@@ -22,7 +22,11 @@
class SSLCertExt : public ExtensionItem {
public:
- SSLCertExt(Module* parent) : ExtensionItem("ssl_cert", parent) {}
+ SSLCertExt(Module* parent)
+ : ExtensionItem("ssl_cert", ExtensionItem::EXT_USER, parent)
+ {
+ }
+
ssl_cert* get(const Extensible* item) const
{
return static_cast<ssl_cert*>(get_raw(item));
diff --git a/src/modules/m_swhois.cpp b/src/modules/m_swhois.cpp
index 4eb2a9cda..81abde6f7 100644
--- a/src/modules/m_swhois.cpp
+++ b/src/modules/m_swhois.cpp
@@ -31,7 +31,9 @@ class CommandSwhois : public Command
{
public:
StringExtItem swhois;
- CommandSwhois(Module* Creator) : Command(Creator,"SWHOIS", 2,2), swhois("swhois", Creator)
+ CommandSwhois(Module* Creator)
+ : Command(Creator, "SWHOIS", 2, 2)
+ , swhois("swhois", ExtensionItem::EXT_USER, Creator)
{
flags_needed = 'o'; syntax = "<nick> :<swhois>";
TRANSLATE2(TR_NICK, TR_TEXT);
diff --git a/src/modules/m_topiclock.cpp b/src/modules/m_topiclock.cpp
index 42ed6e4c1..6053bc849 100644
--- a/src/modules/m_topiclock.cpp
+++ b/src/modules/m_topiclock.cpp
@@ -90,7 +90,7 @@ class FlagExtItem : public ExtensionItem
{
public:
FlagExtItem(const std::string& key, Module* owner)
- : ExtensionItem(key, owner)
+ : ExtensionItem(key, ExtensionItem::EXT_CHANNEL, owner)
{
}
diff --git a/src/modules/m_watch.cpp b/src/modules/m_watch.cpp
index 9cb31a6d8..d0e42af6f 100644
--- a/src/modules/m_watch.cpp
+++ b/src/modules/m_watch.cpp
@@ -247,7 +247,7 @@ class CommandWatch : public Command
return CMD_SUCCESS;
}
- CommandWatch(Module* parent, unsigned int &maxwatch) : Command(parent,"WATCH", 0), MAX_WATCH(maxwatch), ext("watchlist", parent)
+ CommandWatch(Module* parent, unsigned int &maxwatch) : Command(parent,"WATCH", 0), MAX_WATCH(maxwatch), ext("watchlist", ExtensionItem::EXT_USER, parent)
{
syntax = "[C|L|S]|[+|-<nick>]";
}
diff --git a/src/usermanager.cpp b/src/usermanager.cpp
index 52cb4989f..1966c9b47 100644
--- a/src/usermanager.cpp
+++ b/src/usermanager.cpp
@@ -24,6 +24,30 @@
#include "xline.h"
#include "iohook.h"
+namespace
+{
+ class WriteCommonQuit : public User::ForEachNeighborHandler
+ {
+ std::string line;
+ std::string operline;
+
+ void Execute(LocalUser* user) CXX11_OVERRIDE
+ {
+ user->Write(user->IsOper() ? operline : line);
+ }
+
+ public:
+ WriteCommonQuit(User* user, const std::string& msg, const std::string& opermsg)
+ : line(":" + user->GetFullHost() + " QUIT :")
+ , operline(line)
+ {
+ line += msg;
+ operline += opermsg;
+ user->ForEachNeighbor(*this, false);
+ }
+ };
+}
+
UserManager::UserManager()
: unregistered_count(0)
{
@@ -112,7 +136,7 @@ void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs
/* user banned */
ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Positive hit for " + New->GetIPString());
if (!ServerInstance->Config->XLineMessage.empty())
- New->WriteNotice("*** " + ServerInstance->Config->XLineMessage);
+ New->WriteNumeric(ERR_YOUREBANNEDCREEP, ":" + ServerInstance->Config->XLineMessage);
this->QuitUser(New, b->Reason);
return;
}
@@ -180,7 +204,7 @@ void UserManager::QuitUser(User* user, const std::string& quitreason, const std:
if (user->registered == REG_ALL)
{
FOREACH_MOD(OnUserQuit, (user, reason, *operreason));
- user->WriteCommonQuit(reason, *operreason);
+ WriteCommonQuit(user, reason, *operreason);
}
else
unregistered_count--;
diff --git a/src/users.cpp b/src/users.cpp
index 34986a183..12243c64b 100644
--- a/src/users.cpp
+++ b/src/users.cpp
@@ -845,11 +845,27 @@ void User::WriteFrom(User *user, const char* text, ...)
this->WriteFrom(user, textbuffer);
}
-void User::WriteCommon(const char* text, ...)
+namespace
{
- if (this->registered != REG_ALL || quitting)
- return;
+ class WriteCommonRawHandler : public User::ForEachNeighborHandler
+ {
+ const std::string& msg;
+ void Execute(LocalUser* user) CXX11_OVERRIDE
+ {
+ user->Write(msg);
+ }
+
+ public:
+ WriteCommonRawHandler(const std::string& message)
+ : msg(message)
+ {
+ }
+ };
+}
+
+void User::WriteCommon(const char* text, ...)
+{
std::string textbuffer;
VAFORMAT(textbuffer, text, text);
textbuffer = ":" + this->GetFullHost() + " " + textbuffer;
@@ -858,79 +874,58 @@ void User::WriteCommon(const char* text, ...)
void User::WriteCommonRaw(const std::string &line, bool include_self)
{
- if (this->registered != REG_ALL || quitting)
- return;
-
- LocalUser::already_sent_id++;
-
- IncludeChanList include_c(chans.begin(), chans.end());
- std::map<User*,bool> exceptions;
-
- exceptions[this] = include_self;
-
- FOREACH_MOD(OnBuildNeighborList, (this, include_c, exceptions));
-
- for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
- {
- LocalUser* u = IS_LOCAL(i->first);
- if (u && !u->quitting)
- {
- u->already_sent = LocalUser::already_sent_id;
- if (i->second)
- u->Write(line);
- }
- }
- for (IncludeChanList::const_iterator v = include_c.begin(); v != include_c.end(); ++v)
- {
- Channel* c = (*v)->chan;
- const Channel::MemberMap& ulist = c->GetUsers();
- for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
- {
- LocalUser* u = IS_LOCAL(i->first);
- if (u && u->already_sent != LocalUser::already_sent_id)
- {
- u->already_sent = LocalUser::already_sent_id;
- u->Write(line);
- }
- }
- }
+ WriteCommonRawHandler handler(line);
+ ForEachNeighbor(handler, include_self);
}
-void User::WriteCommonQuit(const std::string &normal_text, const std::string &oper_text)
+void User::ForEachNeighbor(ForEachNeighborHandler& handler, bool include_self)
{
- if (this->registered != REG_ALL)
- return;
+ // The basic logic for visiting the neighbors of a user is to iterate the channel list of the user
+ // and visit all users on those channels. Because two users may share more than one common channel,
+ // we must skip users that we have already visited.
+ // To do this, we make use of a global counter and an integral 'already_sent' field in LocalUser.
+ // The global counter is incremented every time we do something for each neighbor of a user. Then,
+ // before visiting a member we examine user->already_sent. If it's equal to the current counter, we
+ // skip the member. Otherwise, we set it to the current counter and visit the member.
- already_sent_t uniq_id = ++LocalUser::already_sent_id;
-
- const std::string normalMessage = ":" + this->GetFullHost() + " QUIT :" + normal_text;
- const std::string operMessage = ":" + this->GetFullHost() + " QUIT :" + oper_text;
-
- IncludeChanList include_c(chans.begin(), chans.end());
- std::map<User*,bool> exceptions;
+ // Ask modules to build a list of exceptions.
+ // Mods may also exclude entire channels by erasing them from include_chans.
+ IncludeChanList include_chans(chans.begin(), chans.end());
+ std::map<User*, bool> exceptions;
+ exceptions[this] = include_self;
+ FOREACH_MOD(OnBuildNeighborList, (this, include_chans, exceptions));
- FOREACH_MOD(OnBuildNeighborList, (this, include_c, exceptions));
+ // Get next id, guaranteed to differ from the already_sent field of all users
+ const already_sent_t newid = ++LocalUser::already_sent_id;
- for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
+ // Handle exceptions first
+ for (std::map<User*, bool>::const_iterator i = exceptions.begin(); i != exceptions.end(); ++i)
{
- LocalUser* u = IS_LOCAL(i->first);
- if (u && !u->quitting)
+ LocalUser* curr = IS_LOCAL(i->first);
+ if (curr)
{
- u->already_sent = uniq_id;
- if (i->second)
- u->Write(u->IsOper() ? operMessage : normalMessage);
+ // Mark as visited to ensure we won't visit again if there is a common channel
+ curr->already_sent = newid;
+ // Always treat quitting users as excluded
+ if ((i->second) && (!curr->quitting))
+ handler.Execute(curr);
}
}
- for (IncludeChanList::const_iterator v = include_c.begin(); v != include_c.end(); ++v)
+
+ // Now consider the real neighbors
+ for (IncludeChanList::const_iterator i = include_chans.begin(); i != include_chans.end(); ++i)
{
- const Channel::MemberMap& ulist = (*v)->chan->GetUsers();
- for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); i++)
+ Channel* chan = (*i)->chan;
+ const Channel::MemberMap& userlist = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator j = userlist.begin(); j != userlist.end(); ++j)
{
- LocalUser* u = IS_LOCAL(i->first);
- if (u && (u->already_sent != uniq_id))
+ LocalUser* curr = IS_LOCAL(j->first);
+ // User not yet visited?
+ if ((curr) && (curr->already_sent != newid))
{
- u->already_sent = uniq_id;
- u->Write(u->IsOper() ? operMessage : normalMessage);
+ // Mark as visited and execute function
+ curr->already_sent = newid;
+ handler.Execute(curr);
}
}
}
diff --git a/src/xline.cpp b/src/xline.cpp
index 13124a392..dedf8c7a9 100644
--- a/src/xline.cpp
+++ b/src/xline.cpp
@@ -531,7 +531,7 @@ void XLine::DefaultApply(User* u, const std::string &line, bool bancache)
const std::string banReason = line + "-Lined: " + reason;
if (!ServerInstance->Config->XLineMessage.empty())
- u->WriteNotice("*** " + ServerInstance->Config->XLineMessage);
+ u->WriteNumeric(ERR_YOUREBANNEDCREEP, ":" + ServerInstance->Config->XLineMessage);
if (ServerInstance->Config->HideBans)
ServerInstance->Users->QuitUser(u, line + "-Lined", &banReason);