From: Attila Molnar Date: Thu, 5 Feb 2015 16:37:49 +0000 (+0100) Subject: Merge pull request #976 from SaberUK/master+fix-xline-db X-Git-Url: https://git.netwichtig.de/gitweb/?a=commitdiff_plain;h=0ec19b7ac91eedc83b31c3da733e237bfe28fc48;hp=5be578ce5e84c3d71bf2a1ac97fe6793bb124177;p=user%2Fhenk%2Fcode%2Finspircd.git Merge pull request #976 from SaberUK/master+fix-xline-db Fix xline reasons being truncated in m_xline_db. --- diff --git a/include/builtinmodes.h b/include/builtinmodes.h index 96838ce5e..62ccaf62d 100644 --- a/include/builtinmodes.h +++ b/include/builtinmodes.h @@ -39,6 +39,7 @@ class ModeChannelBan : public ListModeBase */ class ModeChannelKey : public ParamMode { + static const std::string::size_type maxkeylen = 32; public: ModeChannelKey(); ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding); diff --git a/include/ctables.h b/include/ctables.h index a69f5c86f..abf65f561 100644 --- a/include/ctables.h +++ b/include/ctables.h @@ -163,36 +163,16 @@ class CoreExport CommandBase : public ServiceProvider * @param maxpara Maximum number of parameters this command may have - extra parameters * will be tossed into one last space-seperated param. */ - CommandBase(Module* me, const std::string &cmd, int minpara = 0, int maxpara = 0) : - ServiceProvider(me, 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(Module* me, const std::string& cmd, unsigned int minpara = 0, unsigned int maxpara = 0); - virtual RouteDescriptor GetRouting(User* user, const std::vector& parameters) - { - return ROUTE_LOCALONLY; - } + virtual RouteDescriptor GetRouting(User* user, const std::vector& parameters); /** Encode a parameter for server->server transmission. * Used for parameters for which the translation type is TR_CUSTOM. * @param parameter The parameter to encode. Can be modified in place. * @param index The parameter index (0 == first parameter). */ - virtual void EncodeParameter(std::string& parameter, int index) - { - } - - /** Decode a parameter from server->server transmission. - * Not currently used in this version of InspIRCd. - * Used for parameters for which the translation type is TR_CUSTOM. - * @param parameter The parameter to decode. Can be modified in place. - * @param index The parameter index (0 == first parameter). - */ - virtual void DecodeParameter(std::string& parameter, int index) - { - } + virtual void EncodeParameter(std::string& parameter, int index); /** Disable or enable this command. * @param setting True to disable the command. @@ -229,11 +209,7 @@ class CoreExport Command : public CommandBase */ bool force_manual_route; - Command(Module* me, const std::string& cmd, unsigned int minpara = 0, unsigned int maxpara = 0) - : CommandBase(me, cmd, minpara, maxpara) - , force_manual_route(false) - { - } + Command(Module* me, const std::string& cmd, unsigned int minpara = 0, unsigned int maxpara = 0); /** Handle the command from a user. * @param parameters The parameters for the command. diff --git a/include/extensible.h b/include/extensible.h index 86e0d6b07..a2c104377 100644 --- a/include/extensible.h +++ b/include/extensible.h @@ -38,7 +38,20 @@ enum SerializeFormat class CoreExport ExtensionItem : public ServiceProvider, public usecountbase { public: - ExtensionItem(const std::string& key, Module* owner); + /** Extensible subclasses + */ + enum ExtensibleType + { + EXT_USER, + EXT_CHANNEL, + EXT_MEMBERSHIP + }; + + /** Type (subclass) of Extensible that this ExtensionItem is valid for + */ + const ExtensibleType type; + + ExtensionItem(const std::string& key, ExtensibleType exttype, Module* owner); virtual ~ExtensionItem(); /** Serialize this item into a string * @@ -119,7 +132,7 @@ class CoreExport ExtensionManager class CoreExport LocalExtItem : public ExtensionItem { public: - LocalExtItem(const std::string& key, Module* owner); + LocalExtItem(const std::string& key, ExtensibleType exttype, Module* owner); virtual ~LocalExtItem(); virtual std::string serialize(SerializeFormat format, const Extensible* container, void* item) const; virtual void unserialize(SerializeFormat format, Extensible* container, const std::string& value); @@ -130,7 +143,8 @@ template > class SimpleExtItem : public LocalExtItem { public: - SimpleExtItem(const std::string& Key, Module* parent) : LocalExtItem(Key, parent) + SimpleExtItem(const std::string& Key, ExtensibleType exttype, Module* parent) + : LocalExtItem(Key, exttype, parent) { } @@ -175,7 +189,7 @@ class SimpleExtItem : public LocalExtItem class CoreExport LocalStringExt : public SimpleExtItem { public: - LocalStringExt(const std::string& key, Module* owner); + LocalStringExt(const std::string& key, ExtensibleType exttype, Module* owner); virtual ~LocalStringExt(); std::string serialize(SerializeFormat format, const Extensible* container, void* item) const; }; @@ -183,7 +197,7 @@ class CoreExport LocalStringExt : public SimpleExtItem class CoreExport LocalIntExt : public LocalExtItem { public: - LocalIntExt(const std::string& key, Module* owner); + LocalIntExt(const std::string& key, ExtensibleType exttype, Module* owner); virtual ~LocalIntExt(); std::string serialize(SerializeFormat format, const Extensible* container, void* item) const; intptr_t get(const Extensible* container) const; @@ -195,7 +209,7 @@ class CoreExport LocalIntExt : public LocalExtItem class CoreExport StringExtItem : public ExtensionItem { public: - StringExtItem(const std::string& key, Module* owner); + StringExtItem(const std::string& key, ExtensibleType exttype, Module* owner); virtual ~StringExtItem(); std::string* get(const Extensible* container) const; std::string serialize(SerializeFormat format, const Extensible* container, void* item) const; diff --git a/include/modules/cap.h b/include/modules/cap.h index 1b33e05bb..cc1cb8d8c 100644 --- a/include/modules/cap.h +++ b/include/modules/cap.h @@ -43,7 +43,9 @@ class GenericCap public: LocalIntExt ext; const std::string cap; - GenericCap(Module* parent, const std::string &Cap) : ext("cap_" + Cap, parent), cap(Cap) + GenericCap(Module* parent, const std::string& Cap) + : ext("cap_" + Cap, ExtensionItem::EXT_USER, parent) + , cap(Cap) { } diff --git a/include/numerics.h b/include/numerics.h index 2418730d2..0447df353 100644 --- a/include/numerics.h +++ b/include/numerics.h @@ -144,6 +144,7 @@ enum Numerics ERR_NOTREGISTERED = 451, ERR_NEEDMOREPARAMS = 461, ERR_ALREADYREGISTERED = 462, + ERR_YOUREBANNEDCREEP = 465, ERR_UNKNOWNMODE = 472, /* diff --git a/include/parammode.h b/include/parammode.h index b0005262e..b00082bd6 100644 --- a/include/parammode.h +++ b/include/parammode.h @@ -56,7 +56,7 @@ class ParamMode : public ParamModeBase */ ParamMode(Module* Creator, const std::string& Name, char modeletter, ParamSpec ps = PARAM_SETONLY) : ParamModeBase(Creator, Name, modeletter, ps) - , ext("parammode_" + Name, Creator) + , ext("parammode_" + Name, ExtensionItem::EXT_CHANNEL, Creator) { } diff --git a/include/users.h b/include/users.h index ceee4396b..fa8f610bc 100644 --- a/include/users.h +++ b/include/users.h @@ -248,6 +248,19 @@ class CoreExport User : public Extensible std::bitset modes; public: + /** To execute a function for each local neighbor of a user, inherit from this class and + * pass an instance of it to User::ForEachNeighbor(). + */ + class ForEachNeighborHandler + { + public: + /** Method to execute for each local neighbor of a user. + * Derived classes must implement this. + * @param user Current neighbor + */ + virtual void Execute(LocalUser* user) = 0; + }; + /** List of Memberships for this user */ typedef insp::intrusive_list ChanList; @@ -535,12 +548,15 @@ class CoreExport User : public Extensible */ void WriteCommon(const char* text, ...) CUSTOM_PRINTF(2, 3); - /** Write a quit message to all common users, as in User::WriteCommonExcept but with a specific - * quit message for opers only. - * @param normal_text Normal user quit message - * @param oper_text Oper only quit message + /** Execute a function once for each local neighbor of this user. By default, the neighbors of a user are the users + * who have at least one common channel with the user. Modules are allowed to alter the set of neighbors freely. + * This function is used for example to send something conditionally to neighbors, or to send different messages + * to different users depending on their oper status. + * @param handler Function object to call, inherited from ForEachNeighborHandler. + * @param include_self True to include this user in the set of neighbors, false otherwise. + * Modules may override this. Has no effect if this user is not local. */ - void WriteCommonQuit(const std::string &normal_text, const std::string &oper_text); + void ForEachNeighbor(ForEachNeighborHandler& handler, bool include_self = true); /** Dump text to a user target, splitting it appropriately to fit * @param linePrefix text to prefix each complete line with diff --git a/make/template/main.mk b/make/template/main.mk index 521888bea..39e2b1c23 100644 --- a/make/template/main.mk +++ b/make/template/main.mk @@ -93,7 +93,7 @@ INSTMODE_LIB = 0640 DBGOK=0 @IFEQ $(D) 0 - CORECXXFLAGS += -O2 + CORECXXFLAGS += -fno-rtti -O2 @IFEQ $(COMPILER) GCC CORECXXFLAGS += -g1 @ENDIF @@ -101,12 +101,12 @@ DBGOK=0 DBGOK=1 @ENDIF @IFEQ $(D) 1 - CORECXXFLAGS += -O0 -g3 -Werror + CORECXXFLAGS += -O0 -g3 -Werror -DINSPIRCD_ENABLE_RTTI HEADER = debug-header DBGOK=1 @ENDIF @IFEQ $(D) 2 - CORECXXFLAGS += -O2 -g3 + CORECXXFLAGS += -fno-rtti -O2 -g3 HEADER = debug-header DBGOK=1 @ENDIF 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 +#ifdef INSPIRCD_ENABLE_RTTI #include +#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(Key, Owner) { } +LocalStringExt::LocalStringExt(const std::string& Key, ExtensibleType exttype, Module* Owner) + : SimpleExtItem(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& 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 +#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(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(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 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 = " "; } 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 LocalIntExt jointime; DelayMsgMode(Module* Parent) : ParamMode(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 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 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::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 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 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 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(Module* Creator) : ParamMode >(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(container); - if (!user) - return; + User* user = static_cast(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 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 = "{[+|-] }"; 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 + 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 }; 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& params); + RouteDescriptor GetRouting(User* user, const std::vector& 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& params) { @@ -72,6 +88,30 @@ CmdResult CommandFJoin::Handle(User* srcuser, std::vector& params) * 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& 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& 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& 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& 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& 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 + * + * This file is part of InspIRCd. InspIRCd is free software: you can + * redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "inspircd.h" + +#include "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 + * + * 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 . + */ + + +#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 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(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(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 = " :"; 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]|[+|-]"; } 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 exceptions; - - exceptions[this] = include_self; - - FOREACH_MOD(OnBuildNeighborList, (this, include_c, exceptions)); - - for (std::map::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 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 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::iterator i = exceptions.begin(); i != exceptions.end(); ++i) + // Handle exceptions first + for (std::map::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);