]> git.netwichtig.de Git - user/henk/code/inspircd.git/commitdiff
Merge pull request #976 from SaberUK/master+fix-xline-db
authorAttila Molnar <attilamolnar@hush.com>
Thu, 5 Feb 2015 16:37:49 +0000 (17:37 +0100)
committerAttila Molnar <attilamolnar@hush.com>
Thu, 5 Feb 2015 16:37:49 +0000 (17:37 +0100)
Fix xline reasons being truncated in m_xline_db.

64 files changed:
include/builtinmodes.h
include/ctables.h
include/extensible.h
include/modules/cap.h
include/numerics.h
include/parammode.h
include/users.h
make/template/main.mk
src/base.cpp
src/channels.cpp
src/command_parse.cpp
src/coremods/core_hostname_lookup.cpp
src/coremods/core_stub.cpp
src/coremods/core_user/cmd_mode.cpp
src/cull_list.cpp
src/inspircd.cpp
src/inspsocket.cpp
src/listmode.cpp
src/modes/cmode_k.cpp
src/modules/extra/m_geoip.cpp
src/modules/m_banredirect.cpp
src/modules/m_blockamsg.cpp
src/modules/m_callerid.cpp
src/modules/m_cap.cpp
src/modules/m_cgiirc.cpp
src/modules/m_cloaking.cpp
src/modules/m_conn_join.cpp
src/modules/m_conn_waitpong.cpp
src/modules/m_customtitle.cpp
src/modules/m_dccallow.cpp
src/modules/m_delayjoin.cpp
src/modules/m_delaymsg.cpp
src/modules/m_dnsbl.cpp
src/modules/m_ident.cpp
src/modules/m_ircv3.cpp
src/modules/m_kicknorejoin.cpp
src/modules/m_ldapauth.cpp
src/modules/m_mlock.cpp
src/modules/m_nicklock.cpp
src/modules/m_repeat.cpp
src/modules/m_sasl.cpp
src/modules/m_services_account.cpp
src/modules/m_silence.cpp
src/modules/m_spanningtree/commandbuilder.h
src/modules/m_spanningtree/commands.h
src/modules/m_spanningtree/fjoin.cpp
src/modules/m_spanningtree/main.cpp
src/modules/m_spanningtree/main.h
src/modules/m_spanningtree/metadata.cpp
src/modules/m_spanningtree/pingtimer.cpp [new file with mode: 0644]
src/modules/m_spanningtree/pingtimer.h [new file with mode: 0644]
src/modules/m_spanningtree/pong.cpp
src/modules/m_spanningtree/treeserver.cpp
src/modules/m_spanningtree/treeserver.h
src/modules/m_spanningtree/treesocket.h
src/modules/m_spanningtree/treesocket1.cpp
src/modules/m_sqlauth.cpp
src/modules/m_sslinfo.cpp
src/modules/m_swhois.cpp
src/modules/m_topiclock.cpp
src/modules/m_watch.cpp
src/usermanager.cpp
src/users.cpp
src/xline.cpp

index 96838ce5e4e7307fe668afb32ff3dcd60e510046..62ccaf62d18dd95fd9c2b42983fffdc09892722a 100644 (file)
@@ -39,6 +39,7 @@ class ModeChannelBan : public ListModeBase
  */
 class ModeChannelKey : public ParamMode<ModeChannelKey, LocalStringExt>
 {
+       static const std::string::size_type maxkeylen = 32;
  public:
        ModeChannelKey();
        ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
index a69f5c86f65e14c3fc18ae7abdd75b4deee4905c..abf65f56127122f2e742b76592b3b65fdd8f23fe 100644 (file)
@@ -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<std::string>& parameters)
-       {
-               return ROUTE_LOCALONLY;
-       }
+       virtual RouteDescriptor GetRouting(User* user, const std::vector<std::string>& 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.
index 86e0d6b07fe2bbc8af02c65be4080890ca84fa95..a2c1043779f2a388f3990bc4a187843b6389f984 100644 (file)
@@ -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 <typename T, typename Del = stdalgo::defaultdeleter<T> >
 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<std::string>
 {
  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<std::string>
 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;
index 1b33e05bbcde649b9ef593861967e851fb6ed60e..cc1cb8d8cc5c3d6c25770363d590c80af3341caa 100644 (file)
@@ -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)
        {
        }
 
index 2418730d27afd7abf630cf86a9b556c3b4d7d73e..0447df35308a3ab1a8202a6028926e974ea892f4 100644 (file)
@@ -144,6 +144,7 @@ enum Numerics
        ERR_NOTREGISTERED               = 451,
        ERR_NEEDMOREPARAMS              = 461,
        ERR_ALREADYREGISTERED           = 462,
+       ERR_YOUREBANNEDCREEP            = 465,
        ERR_UNKNOWNMODE                 = 472,
 
        /*
index b0005262e4185abccb6946cf98f29568ff981c82..b00082bd63778eafa93e67e381b324c9c5163492 100644 (file)
@@ -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)
        {
        }
 
index ceee4396bece7ef01ffe2479dfc139a5a73ebe06..fa8f610bc915c9c4b57af2f56573bf6e13bbbc0d 100644 (file)
@@ -248,6 +248,19 @@ class CoreExport User : public Extensible
        std::bitset<ModeParser::MODEID_MAX> 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<Membership> 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
index 521888beadedae4bbaad4dbeadceaeefcb52a016..39e2b1c239d688a824d8e09086cf77178f4ae799 100644 (file)
@@ -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
index c131f4dae25ae440eef1414524c266ea1f4e2c25..67b136ec8c89047ec700cbad341f44fc789aca98 100644 (file)
@@ -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)
 {
 }
 
index fdf0f76e1abdbb4f20539b63749cda5546d3cfb6..948538ff460d15162048800ceea8c77a218f5954 100644 (file)
@@ -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);
 }
index 793569d5bedc75b9d25dc84731869611cca0c548..c93dac65fd386c05bff39ae57307c87dde5117e0 100644 (file)
@@ -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);
index 2c9de3c86db777b48db3178746f07cf6e8034846..11cc5bbba8e0fdfaf8e0be034977af517c08d785 100644 (file)
@@ -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;
index bb6590261fbf5299a280077631f94dbbaaa1a782..28adb9e6a0c9eab8c16e9665f6dd5a822f6f546f 100644 (file)
@@ -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;
        }
index f1e857fc13b9e6eee8c36fac64d34eae1d73c262..190983d13047b28d9f2cd6568d3d9ae933148d2b 100644 (file)
@@ -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
index 5cbe3aef3575bd3d27e23098b8a175a06056dc5a..73f2def51c009822b517e1b5baef2fb81f48b98a 100644 (file)
@@ -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);
                }
index 403ba535502281b6672ec5c21d624a908cf13b36..469539c5b3b525aaab2a8143f1868f293c13b0d7 100644 (file)
@@ -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),
index 645947f56e925c2a37157e9f76932d3c4f96d416..ee5287e5f33888e0cd24b46f0ce68979cf2d33f9 100644 (file)
@@ -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)
                        {
index 1147a4ef2a7d6adfdb8de59bd5d7168264d5b90f..35964dfb3396d9f893f78e63aa2d682d0c39e53d 100644 (file)
@@ -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;
 }
index 6738c046e6b056471c6b1e95ad6c246a74909087..980b3215a4e504fad6dc65577476a882c404686e 100644 (file)
@@ -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
index 3561d1a5da2f21d18bbb61c4acf072b88f4145dc..d21a82149b423cbbe84fca796fff24f5dbddff5f 100644 (file)
@@ -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)
        {
        }
 
index 9833c720d13e78b71b45f94790db48f6a5c0eca0..d3490acc062aab9eeb569ff4d4ebf9c4fd791916 100644 (file)
@@ -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)
        {
        }
 
index b456606a8ff997103f8ea684169d3473a6e1b06a..9614203c350c04a8f3d9f950c9a8085c4d3bb405 100644 (file)
@@ -53,7 +53,8 @@ class ModuleBlockAmsg : public Module
        SimpleExtItem<BlockedMessage> blockamsg;
 
  public:
-       ModuleBlockAmsg() : blockamsg("blockamsg", this)
+       ModuleBlockAmsg()
+               : blockamsg("blockamsg", ExtensionItem::EXT_USER, this)
        {
        }
 
index efbf1a81b690609b9277030cde7eb242e42c4a89..5c6d14a908555034f6198a4907952cc6137d8101 100644 (file)
@@ -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)
        {
        }
 
index bc79e59ecae6e832b096013a1e17fac190fd3f5e..db5d85f0f09dc92f001d9cfb962176a6965470a1 100644 (file)
@@ -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;
        }
index 79104578084d5b13b549e57c6076ac680d352d2d..721d6ba081a693b0237d326b7aab742cd7416e0e 100644 (file)
@@ -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")
        {
        }
index d0e7313b7c946bfdefbb26dde925e536a20267e2..1534043ce9cff081b20c6047615a49528e0e91ff 100644 (file)
@@ -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)
        {
        }
 
index d5a095e7f89721df6e99f5492ebe76d04fe8921d..b22dbdf4dc69bee8b5078b725b8b452f2a0c4592 100644 (file)
@@ -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)
        {
        }
 
index 496b04c2d4cb7d0b403af607e79e2013bd56c750..87b6b51f21e1bf064c5cf11e6959cfbe83b1c603 100644 (file)
@@ -32,7 +32,7 @@ class ModuleWaitPong : public Module
 
  public:
        ModuleWaitPong()
-        : ext("waitpong_pingstr", this)
+               : ext("waitpong_pingstr", ExtensionItem::EXT_USER, this)
        {
        }
 
index 3386e8cd7ca35a38e5c349b2a97ea59c8ae517ab..67eca6dda8d0c596e7886494572b3a473b989dd5 100644 (file)
@@ -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>";
        }
index 7627ba8c7d66d32c21e619531eea4ce259bb379b..2b8d1306cd5f544265f4106063eb895a03db2742 100644 (file)
@@ -257,7 +257,7 @@ class ModuleDCCAllow : public Module
 
  public:
        ModuleDCCAllow()
-               : ext("dccallow", this)
+               : ext("dccallow", ExtensionItem::EXT_USER, this)
                , cmd(this, ext)
        {
        }
index 7e800572ef0ed4bb59ff4d1901178fe73cc2e51b..e864a8289c75e0f8d6be6b174fc7f33f4c65524c 100644 (file)
@@ -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)
        {
        }
 
index 32a0ba96ee03d9bdba01e44167fae735482d3fe6..f64297e15b8c678ce29d0969b8583400175e2ebf 100644 (file)
@@ -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;
        }
index 63dda547fbadba3447925d09b3f7551f2bb9cdf2..7b38da4bfe56cd7651ad44d2ff02d387b198ebf3 100644 (file)
@@ -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
        {
index 3e87b8c5a3f0479d536aa63d529769ce41268427..959e58a479d03c9d02e61c92cc74dec5a8b2075b 100644 (file)
@@ -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)
        {
        }
 
index 4eb54d2a6f7af6413999b3968868ff23740a6d0a..b1c04cdf55359fce13df2fce2236f9a166b9818f 100644 (file)
 #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"),
index fdb7b8f2455fdcb8f8b5e913edaba0574f314705..ebe2d45c990176dc7941913229411de2ebb8ab62 100644 (file)
 
 #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);
                }
        }
 
index eee357ec0796dc2e7d290b5044e127340831e79d..804f6b82122ed85b7022ce88db180f6e9d7bca71 100644 (file)
@@ -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;
index d9c43ec1087c6b0026a21c85b4b4d0493754638f..9b0fa8dab0b8438a49fe8e1b83f608bc936ea3ba 100644 (file)
@@ -25,7 +25,7 @@ class ModuleMLock : public Module
 
  public:
        ModuleMLock()
-               : mlock("mlock", this)
+               : mlock("mlock", ExtensionItem::EXT_CHANNEL, this)
        {
        }
 
index 9bf16498ac548b80870d31778f7181ef63201797..8ac2e8b4703baf39dd5300a21fb5c13d94c5d9ac 100644 (file)
@@ -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)
        {
        }
 
index ca6955040bb5af0d85fc63efe60a9093463cdc0f..820ef702fd895e1bdd7a742f9e4b934dd94440fa 100644 (file)
@@ -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)
        {
        }
 
index 074362651925cc1b5fb8737ec3086bb68b3196dc..0a2c840bd0205eb0905ff9484ff08c3eb772815a 100644 (file)
@@ -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)
        {
        }
 
index 9630128e0b1716865df4ba028d9c263e4d4355d7..aac0b9ce079d7f3fff6264a8d8d57512e16d1780 100644 (file)
@@ -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())
index 22de0ac8b6de76cf1ca9e6c4d2eb0626aaea6009..91822b4e4bf054e078075caf834a8a214bc3e176 100644 (file)
@@ -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>}";
index c2b438deb54ab6d2d062fa5bbbbb6ef3196a4408..26eb4587f4e27accefd910d5a017b3e732af5921 100644 (file)
@@ -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(' ');
index 89bd2bfee3d7839dbc8a942ec1b926ddbfc1540d..1f7456426e3d6a9df87aa35a2ba956468e234470 100644 (file)
@@ -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();
        };
index bfe3592d2ae03599df8039fc5364bc83ec9dfe18..25c1f66781f5716b1eabbbbe790f0fd8e5622cf1 100644 (file)
 #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);
+}
index c21064683057dff28d8c9f3767616e034d55b37d..31d8227894205f00edd71f48fcad0ee23b4e623c 100644 (file)
@@ -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);
 }
 
index c81eb2d0bc023a9a97411c671b769ea78ee376c4..13c743f731387cd4ecdabd8851cd981c85120643 100644 (file)
@@ -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);
index 13ccabc353584754e189164f4373fed2b31b8ab3..f758754b475b311e106b317691659b39f08fb6b7 100644 (file)
@@ -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 (file)
index 0000000..1c96259
--- /dev/null
@@ -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 (file)
index 0000000..7535586
--- /dev/null
@@ -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();
+};
index a7dc64f83b4c2d011ede3a12e0d30a380aed7625..5d97f2af2e8508bb8609c2d1ffdb16288ff256b1 100644 (file)
@@ -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;
 }
index 98d7c87545b6a3229a81f051f8b1e24d1e0fc475..e004f897ec55bc831d47a992b8a4fdea4cd28871 100644 (file)
@@ -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
index f06e0bafa8307f3ca23177b2c6ae48bf22f9d9c4..1a0203ba09384b9ba989de4818141cb5ba760be7 100644 (file)
@@ -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()
index 6dc584537cc97669145d16aabe3afce8f42cea77..4887623c125f1e4aaff4f77a73f6305b152a06b0 100644 (file)
@@ -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
         */
index d2fec0118a005f33a884e3eb97a4a161523b2607..025bd1e611fd633a304e271dfc7d977bf6ee85d5 100644 (file)
@@ -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)
index 8a0b80ce1f9e38c4a102492ef1cb244d05237cff..1a5b68dd9206ee0f0d1e6a967bca418d4ecfaa6d 100644 (file)
@@ -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")
        {
        }
 
index c354e4e0ecfcc4794f6b972574cabafc0e615d01..f861f123654d86fb53c9bef7724632002fc8e846 100644 (file)
 
 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));
index 4eb2a9cdab774a3e9c3071c50c13e056cbeca9f1..81abde6f77c2e82aeb004750fe3fdf91be01e377 100644 (file)
@@ -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);
index 42ed6e4c1ced14aa3606be15b009a08e77ee77eb..6053bc849cca9c6ad1c011dca25c9d5801c62653 100644 (file)
@@ -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)
        {
        }
 
index 9cb31a6d88e3460bbc7fc042ce851d3bc2fe3cfd..d0e42af6f8cfbb1014fe1e66a8cb736ac227edb7 100644 (file)
@@ -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>]";
        }
index 52cb4989fbbaf199ff0eee1aa1718d27337e4aae..1966c9b4729f86193e8c8fa87694a3bf9395c936 100644 (file)
 #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--;
index 34986a1833073f4629fccff7b383871acc55185f..12243c64b074df57ab7442f4b5e8a782d79c5858 100644 (file)
@@ -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);
                        }
                }
        }
index 13124a39228ed4b63d06747e727a70c54f3194a6..dedf8c7a9c917a4c838ee31acce445d0eab3a57a 100644 (file)
@@ -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);