]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - include/modules.h
Avoid calling methods on NULL pointers wherever possible.
[user/henk/code/inspircd.git] / include / modules.h
index ca345dbc15d590bec3519be0b17b904c4013fbc6..cd0d5aad01389b10d40e6620e85cf68de813a15d 100644 (file)
@@ -1,18 +1,30 @@
-/*       +------------------------------------+
- *       | Inspire Internet Relay Chat Daemon |
- *       +------------------------------------+
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
  *
- *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
- * See: http://wiki.inspircd.org/Credits
+ *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2006-2007, 2009 Dennis Friis <peavey@inspircd.org>
+ *   Copyright (C) 2003-2008 Craig Edwards <craigedwards@brainbox.cc>
+ *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *   Copyright (C) 2006-2007 Oliver Lupton <oliverlupton@gmail.com>
+ *   Copyright (C) 2003 randomdan <???@???>
  *
- * This program is free but copyrighted software; see
- *         the file COPYING for details.
+ * 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/>.
  */
 
-#ifndef __MODULES_H
-#define __MODULES_H
+
+#ifndef MODULES_H
+#define MODULES_H
 
 #include "dynamic.h"
 #include "base.h"
 #include "mode.h"
 #include "dns.h"
 
-class XLine;
-
 /** Used to define a set of behavior bits for a module
  */
 enum ModuleFlags {
        VF_NONE = 0,            // module is not special at all
        VF_STATIC = 1,          // module is static, cannot be /unloadmodule'd
        VF_VENDOR = 2,          // module is a vendor module (came in the original tarball, not 3rd party)
-       VF_SERVICEPROVIDER = 4, // module provides a service to other modules (can be a dependency)
-       VF_COMMON = 8,          // module needs to be common on all servers in a network to link
-       VF_OPTCOMMON = 16,      // module should be common on all servers for unsurprising behavior
-       VF_CORE = 32            // module is a core command, can be assumed loaded on all servers
-};
-
-/** Used with SendToMode()
- */
-enum WriteModeFlags {
-       WM_AND = 1,
-       WM_OR = 2
+       VF_COMMON = 4,          // module needs to be common on all servers in a network to link
+       VF_OPTCOMMON = 8,       // module should be common on all servers for unsurprising behavior
+       VF_CORE = 16            // module is a core command, can be assumed loaded on all servers
 };
 
 /** Used to represent an event type, for user, channel or server
@@ -74,26 +76,26 @@ struct ModResult {
        int res;
        ModResult() : res(0) {}
        explicit ModResult(int r) : res(r) {}
-       bool operator==(const ModResult& r) const
+       inline bool operator==(const ModResult& r) const
        {
                return res == r.res;
        }
-       bool operator!=(const ModResult& r) const
+       inline bool operator!=(const ModResult& r) const
        {
                return res != r.res;
        }
-       bool operator!() const
+       inline bool operator!() const
        {
                return !res;
        }
-       bool check(bool def) const
+       inline bool check(bool def) const
        {
                return (res == 1 || (res == 0 && def));
        }
        /**
         * Merges two results, preferring ALLOW to DENY
         */
-       ModResult operator+(const ModResult& r) const
+       inline ModResult operator+(const ModResult& r) const
        {
                if (res == r.res || r.res == 0)
                        return *this;
@@ -104,36 +106,17 @@ struct ModResult {
        }
 };
 
-/** If you change the module API, change this value. */
-#define API_VERSION 13000
-
-class ServerConfig;
-
-/* Forward-delacare module for ModuleMessage etc
- */
-class Module;
-class InspIRCd;
-
-/** Low level definition of a FileReader classes file cache area -
- * a text file seperated into lines.
- */
-typedef std::deque<std::string> file_cache;
-
-/** A set of strings.
- */
-typedef file_cache string_list;
-
-/** Holds a list of 'published features' for modules.
- */
-typedef std::map<std::string,Module*> featurelist;
-
-/** Holds a list of modules which implement an interface
+/** InspIRCd major version.
+ * 1.2 -> 102; 2.1 -> 201; 2.12 -> 212
  */
-typedef std::deque<Module*> modulelist;
-
-/** Holds a list of all modules which implement interfaces, by interface name
+#define INSPIRCD_VERSION_MAJ 200
+/** InspIRCd API version.
+ * If you change any API elements, increment this value. This counter should be
+ * reset whenever the major version is changed. Modules can use these two values
+ * and numerical comparisons in preprocessor macros if they wish to support
+ * multiple versions of InspIRCd in one file.
  */
-typedef std::map<std::string, std::pair<int, modulelist> > interfacelist;
+#define INSPIRCD_VERSION_API 9
 
 /**
  * This #define allows us to call a method in all
@@ -202,66 +185,31 @@ do { \
        WHILE_EACH_HOOK(n); \
 } while (0)
 
-/** Represents a non-local user.
- * (in fact, any FD less than -1 does)
- */
-#define FD_MAGIC_NUMBER -42
-/** Represents a fake user (i.e. a server)
- */
-#define FD_FAKEUSER_NUMBER -7
-
-/* Useful macros */
-
-/** Is a local user */
-#define IS_LOCAL(x) (x->GetFd() > -1)
-/** Is a remote user */
-#define IS_REMOTE(x) (x->GetFd() < 0)
-/** Is a fake user */
-#define IS_SERVER(x) (x->GetFd() == FD_FAKEUSER_NUMBER)
-/** Is a module created user */
-#define IS_MODULE_CREATED(x) (x->GetFd() == FD_MAGIC_NUMBER)
-/** Is an oper */
-#define IS_OPER(x) (!x->oper.empty())
-/** Is away */
-#define IS_AWAY(x) (!x->awaymsg.empty())
-
 /** Holds a module's Version information.
- *  The four members (set by the constructor only) indicate details as to the version number
- *  of a module. A class of type Version is returned by the GetVersion method of the Module class.
- *  The flags and API values represent the module flags and API version of the module.
- *  The API version of a module must match the API version of the core exactly for the module to
- *  load successfully.
+ * The members (set by the constructor only) indicate details as to the version number
+ * of a module. A class of type Version is returned by the GetVersion method of the Module class.
  */
-class CoreExport Version : public classbase
+class CoreExport Version
 {
  public:
        /** Module description
         */
        const std::string description;
-       /** Version information.
-        */
-       const std::string version;
 
-       /** Flags and API version
+       /** Flags
         */
-       const int Flags, API;
+       const int Flags;
 
-       /** Initialize version class
-        */
-       Version(const std::string &desc, int flags,
-               int api_ver = API_VERSION, const std::string& src_rev = VERSION " r" REVISION);
-};
+       /** Server linking description string */
+       const std::string link_data;
 
-/** The ModuleMessage class is the base class of Request and Event
- * This class is used to represent a basic data structure which is passed
- * between modules for safe inter-module communications.
- */
-class CoreExport ModuleMessage : public Extensible
-{
- public:
-       /** Destructor
-        */
-       virtual ~ModuleMessage() {};
+       /** Simple module version */
+       Version(const std::string &desc, int flags = VF_NONE);
+
+       /** Complex version information, including linking compatability data */
+       Version(const std::string &desc, int flags, const std::string& linkdata);
+
+       virtual ~Version() {}
 };
 
 /** The Request class is a unicast message directed at a given module.
@@ -269,60 +217,33 @@ class CoreExport ModuleMessage : public Extensible
  * using the Send() method, which will call the given module's OnRequest
  * method with this class as its parameter.
  */
-class CoreExport Request : public ModuleMessage
+class CoreExport Request : public classbase
 {
- protected:
-       /** This member holds a pointer to arbitary data set by the emitter of the message
-        */
-       char* data;
+ public:
        /** This should be a null-terminated string identifying the type of request,
         * all modules should define this and use it to determine the nature of the
         * request before they attempt to cast the Request in any way.
         */
-       const char* id;
+       const char* const id;
        /** This is a pointer to the sender of the message, which can be used to
         * directly trigger events, or to create a reply.
         */
-       Module* source;
+       ModuleRef source;
        /** The single destination of the Request
         */
-       Module* dest;
- public:
-       /** Create a new Request
-        * This is for the 'old' way of casting whatever the data is
-        * to char* and hoping you get the right thing at the other end.
-        * This is slowly being depreciated in favor of the 'new' way.
-        */
-       Request(char* anydata, Module* src, Module* dst);
+       ModuleRef dest;
+
        /** Create a new Request
         * This is for the 'new' way of defining a subclass
         * of Request and defining it in a common header,
         * passing an object of your Request subclass through
         * as a Request* and using the ID string to determine
-        * what to cast it back to and the other end. This is
-        * much safer as there are no casts not confirmed by
-        * the ID string, and all casts are child->parent and
-        * can be checked at runtime with dynamic_cast<>()
+        * what to cast it back to and the other end.
         */
        Request(Module* src, Module* dst, const char* idstr);
-       /** Fetch the Request data
-        */
-       char* GetData();
-       /** Fetch the ID string
-        */
-       const char* GetId();
-       /** Fetch the request source
-        */
-       Module* GetSource();
-       /** Fetch the request destination (should be 'this' in the receiving module)
-        */
-       Module* GetDest();
        /** Send the Request.
-        * Upon returning the result will be arbitary data returned by the module you
-        * sent the request to. It is up to your module to know what this data is and
-        * how to deal with it.
         */
-       const char* Send();
+       void Send();
 };
 
 
@@ -331,45 +252,71 @@ class CoreExport Request : public ModuleMessage
  * using the Send() method, which will trigger the OnEvent method in
  * all modules passing the object as its parameter.
  */
-class CoreExport Event : public ModuleMessage
+class CoreExport Event : public classbase
 {
- protected:
-       /** This member holds a pointer to arbitary data set by the emitter of the message
-        */
-       char* data;
+ public:
        /** This is a pointer to the sender of the message, which can be used to
         * directly trigger events, or to create a reply.
         */
-       Module* source;
+       ModuleRef source;
        /** The event identifier.
         * This is arbitary text which should be used to distinguish
         * one type of event from another.
         */
-       std::string id;
- public:
+       const std::string id;
+
        /** Create a new Event
         */
-       Event(char* anydata, Module* src, const std::string &eventid);
-       /** Get the Event data
-        */
-       char* GetData();
-       /** Get the event Source
-        */
-       Module* GetSource();
-       /** Get the event ID.
-        * Use this to determine the event type for safe casting of the data
-        */
-       std::string GetEventID();
+       Event(Module* src, const std::string &eventid);
        /** Send the Event.
         * The return result of an Event::Send() will always be NULL as
         * no replies are expected.
         */
-       char* Send();
+       void Send();
+};
+
+class CoreExport DataProvider : public ServiceProvider
+{
+ public:
+       DataProvider(Module* Creator, const std::string& Name)
+               : ServiceProvider(Creator, Name, SERVICE_DATA) {}
 };
 
-/** Priority types which can be returned from Module::Prioritize()
+class CoreExport dynamic_reference_base : public interfacebase
+{
+ private:
+       std::string name;
+ protected:
+       DataProvider* value;
+ public:
+       ModuleRef creator;
+       dynamic_reference_base(Module* Creator, const std::string& Name);
+       ~dynamic_reference_base();
+       inline void ClearCache() { value = NULL; }
+       inline const std::string& GetProvider() { return name; }
+       void SetProvider(const std::string& newname);
+       void lookup();
+       operator bool();
+       static void reset_all();
+};
+
+template<typename T>
+class dynamic_reference : public dynamic_reference_base
+{
+ public:
+       dynamic_reference(Module* Creator, const std::string& Name)
+               : dynamic_reference_base(Creator, Name) {}
+       inline T* operator->()
+       {
+               if (!value)
+                       lookup();
+               return static_cast<T*>(value);
+       }
+};
+
+/** Priority types which can be used by Module::Prioritize()
  */
-enum Priority { PRIORITY_FIRST, PRIORITY_DONTCARE, PRIORITY_LAST, PRIORITY_BEFORE, PRIORITY_AFTER };
+enum Priority { PRIORITY_FIRST, PRIORITY_LAST, PRIORITY_BEFORE, PRIORITY_AFTER };
 
 /** Implementation-specific flags which may be set in Module::Implements()
  */
@@ -380,29 +327,27 @@ enum Implementation
        I_OnSendSnotice, I_OnUserPreJoin, I_OnUserPreKick, I_OnUserKick, I_OnOper, I_OnInfo, I_OnWhois,
        I_OnUserPreInvite, I_OnUserInvite, I_OnUserPreMessage, I_OnUserPreNotice, I_OnUserPreNick,
        I_OnUserMessage, I_OnUserNotice, I_OnMode, I_OnGetServerDescription, I_OnSyncUser,
-       I_OnSyncChannel, I_OnDecodeMetaData, I_OnWallops,
-       I_OnChangeHost, I_OnChangeName, I_OnAddLine, I_OnDelLine, I_OnExpireLine, I_OnCleanup,
+       I_OnSyncChannel, I_OnDecodeMetaData, I_OnWallops, I_OnAcceptConnection, I_OnUserInit,
+       I_OnChangeHost, I_OnChangeName, I_OnAddLine, I_OnDelLine, I_OnExpireLine,
        I_OnUserPostNick, I_OnPreMode, I_On005Numeric, I_OnKill, I_OnRemoteKill, I_OnLoadModule,
        I_OnUnloadModule, I_OnBackgroundTimer, I_OnPreCommand, I_OnCheckReady, I_OnCheckInvite,
        I_OnRawMode, I_OnCheckKey, I_OnCheckLimit, I_OnCheckBan, I_OnCheckChannelBan, I_OnExtBanCheck,
-       I_OnStats, I_OnChangeLocalUserHost, I_OnChangeLocalUserGecos, I_OnPreTopicChange,
-       I_OnPostTopicChange, I_OnEvent, I_OnRequest, I_OnGlobalOper, I_OnPostConnect, I_OnAddBan,
+       I_OnStats, I_OnChangeLocalUserHost, I_OnPreTopicChange,
+       I_OnPostTopicChange, I_OnEvent, I_OnGlobalOper, I_OnPostConnect, I_OnAddBan,
        I_OnDelBan, I_OnChangeLocalUserGECOS, I_OnUserRegister, I_OnChannelPreDelete, I_OnChannelDelete,
-       I_OnPostOper, I_OnSyncNetwork, I_OnSetAway, I_OnUserList, I_OnPostCommand, I_OnPostJoin,
-       I_OnWhoisLine, I_OnBuildNeighborList, I_OnGarbageCollect,
+       I_OnPostOper, I_OnSyncNetwork, I_OnSetAway, I_OnPostCommand, I_OnPostJoin,
+       I_OnWhoisLine, I_OnBuildNeighborList, I_OnGarbageCollect, I_OnSetConnectClass,
        I_OnText, I_OnPassCompare, I_OnRunTestSuite, I_OnNamesListItem, I_OnNumeric, I_OnHookIO,
-       I_OnPreRehash, I_OnModuleRehash, I_OnSendWhoLine, I_OnChangeIdent,
+       I_OnPreRehash, I_OnModuleRehash, I_OnSendWhoLine, I_OnChangeIdent, I_OnSetUserIP,
        I_END
 };
 
-class ConfigReader;
-
 /** Base class for all InspIRCd modules
  *  This class is the base class for InspIRCd modules. All modules must inherit from this class,
  *  its methods will be called when irc server events occur. class inherited from module must be
  *  instantiated by the ModuleFactory class (see relevent section) for the module to be initialised.
  */
-class CoreExport Module : public Extensible
+class CoreExport Module : public classbase, public usecountbase
 {
  public:
        /** File that this module was loaded from
@@ -410,19 +355,28 @@ class CoreExport Module : public Extensible
        std::string ModuleSourceFile;
        /** Reference to the dlopen() value
         */
-       DLLFactory* ModuleDLLFactory;
+       DLLManager* ModuleDLLManager;
+
+       /** If true, this module will be unloaded soon, further unload attempts will fail
+        * Value is used by the ModuleManager internally, you should not modify it
+        */
+       bool dying;
 
        /** Default constructor.
-        * Creates a module class.
-        * @param Me An instance of the InspIRCd class which will be saved into ServerInstance for your use
-        * \exception ModuleException Throwing this class, or any class derived from ModuleException, causes loading of the module to abort.
+        * Creates a module class. Don't do any type of hook registration or checks
+        * for other modules here; do that in init().
         */
        Module();
 
+       /** Module setup
+        * \exception ModuleException Throwing this class, or any class derived from ModuleException, causes loading of the module to abort.
+        */
+       virtual void init() {}
+
        /** Clean up prior to destruction
         * If you override, you must call this AFTER your module's cleanup
         */
-       virtual bool cull();
+       virtual CullResult cull();
 
        /** Default destructor.
         * destroys a module class
@@ -443,7 +397,7 @@ class CoreExport Module : public Extensible
         * The details of the connecting user are available to you in the parameter User *user
         * @param user The user who is connecting
         */
-       virtual void OnUserConnect(User* user);
+       virtual void OnUserConnect(LocalUser* user);
 
        /** Called when a user quits.
         * The details of the exiting user are available to you in the parameter User *user
@@ -461,7 +415,7 @@ class CoreExport Module : public Extensible
         * which might assign resources to user, such as dns lookups, objects and sockets.
         * @param user The user who is disconnecting
         */
-       virtual void OnUserDisconnect(User* user);
+       virtual void OnUserDisconnect(LocalUser* user);
 
        /** Called whenever a channel is about to be deleted
         * @param chan The channel being deleted
@@ -481,6 +435,7 @@ class CoreExport Module : public Extensible
         * @param sync This is set to true if the JOIN is the result of a network sync and the remote user is being introduced
         * to a channel due to the network sync.
         * @param created This is true if the join created the channel
+        * @param except_list A list of users not to send to.
         */
        virtual void OnUserJoin(Membership* memb, bool sync, bool created, CUList& except_list);
 
@@ -489,13 +444,14 @@ class CoreExport Module : public Extensible
         * seen the join.
         * @param memb The channel membership created
         */
-       virtual void OnPostJoin(Membership*);
+       virtual void OnPostJoin(Membership* memb);
 
        /** Called when a user parts a channel.
         * The details of the leaving user are available to you in the parameter User *user,
         * and the details of the channel they have left is available in the variable Channel *channel
         * @param memb The channel membership being destroyed
         * @param partmessage The part message, or an empty string (may be modified)
+        * @param except_list A list of users to not send to.
         */
        virtual void OnUserPart(Membership* memb, std::string &partmessage, CUList& except_list);
 
@@ -565,8 +521,7 @@ class CoreExport Module : public Extensible
         * output to be sent to the user by the core. If you do this you must produce your own numerics,
         * notices etc.
         * @param source The user issuing the kick
-        * @param user The user being kicked
-        * @param chan The channel the user is being kicked from
+        * @param memb The channel membership of the user who is being kicked.
         * @param reason The kick reason
         * @return 1 to prevent the kick, 0 to continue normally, -1 to explicitly allow the kick regardless of normal operation
         */
@@ -576,9 +531,9 @@ class CoreExport Module : public Extensible
         * If this method is called, the kick is already underway and cannot be prevented, so
         * to prevent a kick, please use Module::OnUserPreKick instead of this method.
         * @param source The user issuing the kick
-        * @param user The user being kicked
-        * @param chan The channel the user is being kicked from
+        * @param memb The channel membership of the user who was kicked.
         * @param reason The kick reason
+        * @param except_list A list of users to not send to.
         */
        virtual void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& except_list);
 
@@ -712,6 +667,7 @@ class CoreExport Module : public Extensible
         * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL)
         * @param text the text being sent by the user
         * @param status The status being used, e.g. PRIVMSG @#chan has status== '@', 0 to send to everyone.
+        * @param exempt_list A list of users to not send to.
         */
        virtual void OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list);
 
@@ -723,6 +679,7 @@ class CoreExport Module : public Extensible
         * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL)
         * @param text the text being sent by the user
         * @param status The status being used, e.g. NOTICE @#chan has status== '@', 0 to send to everyone.
+        * @param exempt_list A list of users to not send to.
         */
        virtual void OnUserNotice(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list);
 
@@ -737,6 +694,7 @@ class CoreExport Module : public Extensible
         * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL)
         * @param text the text being sent by the user
         * @param status The status being used, e.g. NOTICE @#chan has status== '@', 0 to send to everyone.
+        * @param exempt_list A list of users not to send to. For channel messages, this will usually contain just the sender.
         */
        virtual void OnText(User* user, void* dest, int target_type, const std::string &text, char status, CUList &exempt_list);
 
@@ -806,7 +764,6 @@ class CoreExport Module : public Extensible
 
        /** Allows module data, sent via ProtoSendMetaData, to be decoded again by a receiving module.
         * Please see src/modules/m_swhois.cpp for a working example of how to use this method call.
-        * @param target_type The type of item to decode data for, TYPE_USER or TYPE_CHANNEL
         * @param target The Channel* or User* that data should be added to
         * @param extname The extension name which is being sent
         * @param extdata The extension data, encoded at the other end by an identical module through OnSyncChannelMetaData or OnSyncUserMetaData
@@ -838,7 +795,6 @@ class CoreExport Module : public Extensible
         * More documentation to follow soon. Please see src/modules/m_swhois.cpp for example of
         * how to use this function.
         * @param opaque An opaque pointer set by the protocol module, should not be modified!
-        * @param target_type The type of item to decode data for, TYPE_USER or TYPE_CHANNEL
         * @param target The Channel* or User* that metadata should be sent for
         * @param extname The extension name to send metadata for
         * @param extdata Encoded data for this extension name, which will be encoded at the oppsite end by an identical module using OnDecodeMetaData
@@ -868,7 +824,7 @@ class CoreExport Module : public Extensible
        /** Called whenever a user's IDENT is changed.
         * This event triggers after the name has been set.
         * @param user The user who's IDENT is being changed
-        * @param gecos The new IDENT being set on the user
+        * @param ident The new IDENT being set on the user
         */
        virtual void OnChangeIdent(User* user, const std::string &ident);
 
@@ -952,6 +908,7 @@ class CoreExport Module : public Extensible
         * @param source The user sending the KILL
         * @param dest The user being killed
         * @param reason The kill reason
+        * @param operreason The oper kill reason
         */
        virtual void OnRemoteKill(User* source, User* dest, const std::string &reason, const std::string &operreason);
 
@@ -965,9 +922,8 @@ class CoreExport Module : public Extensible
         * absolutely neccessary (e.g. a module that extends the features of another
         * module).
         * @param mod A pointer to the new module
-        * @param name The new module's filename
         */
-       virtual void OnLoadModule(Module* mod,const std::string &name);
+       virtual void OnLoadModule(Module* mod);
 
        /** Called whenever a module is unloaded.
         * mod will contain a pointer to the module, and string will contain its name,
@@ -979,9 +935,8 @@ class CoreExport Module : public Extensible
         * absolutely neccessary (e.g. a module that extends the features of another
         * module).
         * @param mod Pointer to the module being unloaded (still valid)
-        * @param name The filename of the module being unloaded
         */
-       virtual void OnUnloadModule(Module* mod,const std::string &name);
+       virtual void OnUnloadModule(Module* mod);
 
        /** Called once every five seconds for background processing.
         * This timer can be used to control timed features. Its period is not accurate
@@ -1002,14 +957,13 @@ class CoreExport Module : public Extensible
         * method returns, it will be passed an invalid pointer to the user object and crash!)
         * @param command The command being executed
         * @param parameters An array of array of characters containing the parameters for the command
-        * @param pcnt The nuimber of parameters passed to the command
         * @param user the user issuing the command
         * @param validated True if the command has passed all checks, e.g. it is recognised, has enough parameters, the user has permission to execute it, etc.
         * You should only change the parameter list and command string if validated == false (e.g. before the command lookup occurs).
         * @param original_line The entire original line as passed to the parser from the user
         * @return 1 to block the command, 0 to allow
         */
-       virtual ModResult OnPreCommand(std::string &command, std::vector<std::string>& parameters, User *user, bool validated, const std::string &original_line);
+       virtual ModResult OnPreCommand(std::string &command, std::vector<std::string>& parameters, LocalUser *user, bool validated, const std::string &original_line);
 
        /** Called after any command has been executed.
         * This event occurs for all registered commands, wether they are registered in the core,
@@ -1018,12 +972,16 @@ class CoreExport Module : public Extensible
         * provided.
         * @param command The command being executed
         * @param parameters An array of array of characters containing the parameters for the command
-        * @param pcnt The nuimber of parameters passed to the command
         * @param user the user issuing the command
         * @param result The return code given by the command handler, one of CMD_SUCCESS or CMD_FAILURE
         * @param original_line The entire original line as passed to the parser from the user
         */
-       virtual void OnPostCommand(const std::string &command, const std::vector<std::string>& parameters, User *user, CmdResult result, const std::string &original_line);
+       virtual void OnPostCommand(const std::string &command, const std::vector<std::string>& parameters, LocalUser *user, CmdResult result, const std::string &original_line);
+
+       /** Called when a user is first connecting, prior to starting DNS lookups, checking initial
+        * connect class, or accepting any commands.
+        */
+       virtual void OnUserInit(LocalUser* user);
 
        /** Called to check if a user who is connecting can now be allowed to register
         * If any modules return false for this function, the user is held in the waiting
@@ -1035,7 +993,7 @@ class CoreExport Module : public Extensible
         * @param user The user to check
         * @return true to indicate readiness, false if otherwise
         */
-       virtual ModResult OnCheckReady(User* user);
+       virtual ModResult OnCheckReady(LocalUser* user);
 
        /** Called whenever a user is about to register their connection (e.g. before the user
         * is sent the MOTD etc). Modules can use this method if they are performing a function
@@ -1046,7 +1004,7 @@ class CoreExport Module : public Extensible
         * @param user The user registering
         * @return 1 to indicate user quit, 0 to continue
         */
-       virtual ModResult OnUserRegister(User* user);
+       virtual ModResult OnUserRegister(LocalUser* user);
 
        /** Called whenever a user joins a channel, to determine if invite checks should go ahead or not.
         * This method will always be called for each join, wether or not the channel is actually +i, and
@@ -1079,6 +1037,7 @@ class CoreExport Module : public Extensible
         * return 1 to explicitly allow the join to go ahead or 0 to ignore the event.
         * @param user The user joining the channel
         * @param chan The channel being joined
+        * @param keygiven The key given on joining the channel.
         * @return 1 to explicitly allow the join, 0 to proceed as normal
         */
        virtual ModResult OnCheckKey(User* user, Channel* chan, const std::string &keygiven);
@@ -1135,7 +1094,7 @@ class CoreExport Module : public Extensible
         * @param newhost The new hostname
         * @return 1 to deny the host change, 0 to allow
         */
-       virtual ModResult OnChangeLocalUserHost(User* user, const std::string &newhost);
+       virtual ModResult OnChangeLocalUserHost(LocalUser* user, const std::string &newhost);
 
        /** Called whenever a change of a local users GECOS (fullname field) is attempted.
         * return 1 to deny the name change, or 0 to allow it.
@@ -1143,7 +1102,7 @@ class CoreExport Module : public Extensible
         * @param newhost The new GECOS
         * @return 1 to deny the GECOS change, 0 to allow
         */
-       virtual ModResult OnChangeLocalUserGECOS(User* user, const std::string &newhost);
+       virtual ModResult OnChangeLocalUserGECOS(LocalUser* user, const std::string &newhost);
 
        /** Called before a topic is changed.
         * Return 1 to deny the topic change, 0 to check details on the change, -1 to let it through with no checks
@@ -1151,7 +1110,7 @@ class CoreExport Module : public Extensible
         * @param user The user changing the topic
         * @param chan The channels who's topic is being changed
         * @param topic The actual topic text
-        * @param 1 to block the topic change, 0 to allow
+        * @return 1 to block the topic change, 0 to allow
         */
        virtual ModResult OnPreTopicChange(User* user, Channel* chan, const std::string &topic);
 
@@ -1163,28 +1122,23 @@ class CoreExport Module : public Extensible
         */
        virtual void OnPostTopicChange(User* user, Channel* chan, const std::string &topic);
 
-       /** Called whenever an Event class is sent to all module by another module.
-        * Please see the documentation of Event::Send() for further information. The Event sent can
-        * always be assumed to be non-NULL, you should *always* check the value of Event::GetEventID()
-        * before doing anything to the event data, and you should *not* change the event data in any way!
+       /** Called whenever an Event class is sent to all modules by another module.
+        * You should *always* check the value of Event::id to determine the event type.
         * @param event The Event class being received
         */
-       virtual void OnEvent(Event* event);
+       virtual void OnEvent(Event& event);
 
        /** Called whenever a Request class is sent to your module by another module.
-        * Please see the documentation of Request::Send() for further information. The Request sent
-        * can always be assumed to be non-NULL, you should not change the request object or its data.
-        * Your method may return arbitary data in the char* result which the requesting module
-        * may be able to use for pre-determined purposes (e.g. the results of an SQL query, etc).
+        * The value of Request::id should be used to determine the type of request.
         * @param request The Request class being received
         */
-       virtual const char* OnRequest(Request* request);
+       virtual void OnRequest(Request& request);
 
        /** Called whenever a password check is to be made. Replaces the old OldOperCompare API.
         * The password field (from the config file) is in 'password' and is to be compared against
         * 'input'. This method allows for encryption of passwords (oper, connect:allow, die/restart, etc).
         * You should return a nonzero value to override the normal comparison, or zero to pass it on.
-        * @param ex The object that's causing the authentication (User* for <oper> <connect:allow> etc, Server* for <link>).
+        * @param ex The object that's causing the authentication (User* for \<oper> \<connect:allow> etc, Server* for \<link>).
         * @param password The password from the configuration file (the password="" value).
         * @param input The password entered by the user or whoever.
         * @param hashtype The hash value from the config
@@ -1227,53 +1181,61 @@ class CoreExport Module : public Extensible
        virtual ModResult OnDelBan(User* source, Channel* channel,const std::string &banmask);
 
        /** Called to install an I/O hook on an event handler
-        * @param user The item to possibly install the I/O hook on
-        * @param via The port that <user> connected on
+        * @param user The socket to possibly install the I/O hook on
+        * @param via The port that the user connected on
         */
-       virtual void OnHookIO(StreamSocket*, ListenSocketBase* via);
+       virtual void OnHookIO(StreamSocket* user, ListenSocket* via);
+
+       /** Called when a port accepts a connection
+        * Return MOD_RES_ACCEPT if you have used the file descriptor.
+        * @param fd The file descriptor returned from accept()
+        * @param sock The socket connection for the new user
+        * @param client The client IP address and port
+        * @param server The server IP address and port
+        */
+       virtual ModResult OnAcceptConnection(int fd, ListenSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
 
        /** Called immediately after any connection is accepted. This is intended for raw socket
         * processing (e.g. modules which wrap the tcp connection within another library) and provides
         * no information relating to a user record as the connection has not been assigned yet.
         * There are no return values from this call as all modules get an opportunity if required to
         * process the connection.
-        * @param fd The file descriptor returned from accept()
+        * @param sock The socket in question
         * @param client The client IP address and port
         * @param server The server IP address and port
-        * @param localport The local port number the user connected to
         */
-       virtual void OnStreamSocketAccept(StreamSocket*, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
+       virtual void OnStreamSocketAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
 
        /**
         * Called when a hooked stream has data to write, or when the socket
         * engine returns it as writable
-        * @param socket The socket in question
+        * @param sock The socket in question
         * @param sendq Data to send to the socket
         * @return 1 if the sendq has been completely emptied, 0 if there is
         *  still data to send, and -1 if there was an error
         */
-       virtual int OnStreamSocketWrite(StreamSocket*, std::string& sendq);
+       virtual int OnStreamSocketWrite(StreamSocket* sock, std::string& sendq);
 
        /** Called immediately before any socket is closed. When this event is called, shutdown()
         * has not yet been called on the socket.
-        * @param fd The file descriptor of the socket prior to close()
+        * @param sock The socket in question
         */
-       virtual void OnStreamSocketClose(StreamSocket*);
+       virtual void OnStreamSocketClose(StreamSocket* sock);
 
        /** Called immediately upon connection of an outbound BufferedSocket which has been hooked
         * by a module.
-        * @param fd The file descriptor of the socket immediately after connect()
+        * @param sock The socket in question
         */
-       virtual void OnStreamSocketConnect(StreamSocket*);
+       virtual void OnStreamSocketConnect(StreamSocket* sock);
 
        /**
         * Called when the stream socket has data to read
-        * @param socket The socket that is ready
+        * @param sock The socket that is ready
         * @param recvq The receive queue that new data should be appended to
         * @return 1 if new data has been read, 0 if no new data is ready (but the
         *  socket is still connected), -1 if there was an error or close
         */
-       virtual int OnStreamSocketRead(StreamSocket*, std::string& recvq);
+       virtual int OnStreamSocketRead(StreamSocket* sock, std::string& recvq);
 
        /** Called whenever a user sets away or returns from being away.
         * The away message is available as a parameter, but should not be modified.
@@ -1285,15 +1247,6 @@ class CoreExport Module : public Extensible
         */
        virtual ModResult OnSetAway(User* user, const std::string &awaymsg);
 
-       /** Called whenever a NAMES list is requested.
-        * You can produce the nameslist yourself, overriding the current list,
-        * and if you do you must return 1. If you do not handle the names list,
-        * return 0.
-        * @param The user requesting the NAMES list
-        * @param Ptr The channel the NAMES list is requested for
-        */
-       virtual ModResult OnUserList(User* user, Channel* Ptr);
-
        /** Called whenever a line of WHOIS output is sent to a user.
         * You may change the numeric and the text of the output by changing
         * the values numeric and text, but you cannot change the user the
@@ -1314,6 +1267,12 @@ class CoreExport Module : public Extensible
         */
        virtual void OnGarbageCollect();
 
+       /** Called when a user's connect class is being matched
+        * @return MOD_RES_ALLOW to force the class to match, MOD_RES_DENY to forbid it, or
+        * MOD_RES_PASSTHRU to allow normal matching (by host/port).
+        */
+       virtual ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass);
+
        /** Add test suite hooks here. These are used for testing functionality of a module
         * via the --testsuite debugging parameter.
         */
@@ -1329,11 +1288,17 @@ class CoreExport Module : public Extensible
 
        /** Called whenever a result from /WHO is about to be returned
         * @param source The user running the /WHO query
+        * @param params The parameters to the /WHO query
         * @param user The user that this line of the query is about
-        * @param channel The channel being queried (or NULL if not a channel query)
         * @param line The raw line to send; modifiable, if empty no line will be returned.
         */
-       virtual void OnSendWhoLine(User* source, User* user, Channel* channel, std::string& line);
+       virtual void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, std::string& line);
+
+       /** Called whenever a local user's IP is set for the first time, or when a local user's IP changes due to
+        * a module like m_cgiirc changing it.
+        * @param user The user whose IP is being set
+        */
+       virtual void OnSetUserIP(LocalUser* user);
 };
 
 
@@ -1350,7 +1315,7 @@ class CoreExport Module : public Extensible
  * Constructing the class using one parameter allows you to specify a path to your own configuration
  * file, otherwise, inspircd.conf is read.
  */
-class CoreExport ConfigReader : public classbase
+class CoreExport ConfigReader : public interfacebase
 {
   protected:
        /** Error code
@@ -1419,6 +1384,7 @@ class CoreExport ConfigReader : public classbase
         * A call to GetError() resets the error flag back to 0.
         */
        long GetError();
+
        /** Counts the number of times a given tag appears in the config file.
         * This method counts the number of times a tag appears in a config file, for use where
         * there are several tags of the same kind, e.g. with opers and connect types. It can be
@@ -1426,13 +1392,6 @@ class CoreExport ConfigReader : public classbase
         * multiple instance tag.
         */
        int Enumerate(const std::string &tag);
-
-       /** Returns the number of items within a tag.
-        * For example if the tag was &lt;test tag="blah" data="foo"&gt; then this
-        * function would return 2. Spaces and newlines both qualify as valid seperators
-        * between values.
-        */
-       int EnumerateValues(const std::string &tag, int index);
 };
 
 
@@ -1446,7 +1405,7 @@ class CoreExport FileReader : public classbase
 {
        /** The file contents
         */
-       file_cache fc;
+       std::vector<std::string> fc;
 
        /** Content size in bytes
         */
@@ -1520,21 +1479,13 @@ typedef IntModuleList::iterator EventHandlerIter;
 /** ModuleManager takes care of all things module-related
  * in the core.
  */
-class CoreExport ModuleManager : public classbase
+class CoreExport ModuleManager
 {
  private:
        /** Holds a string describing the last module error to occur
         */
        std::string LastModuleError;
 
-       /** The feature names published by various modules
-        */
-       featurelist Features;
-
-       /** The interface names published by various modules
-        */
-       interfacelist Interfaces;
-
        /** Total number of modules loaded into the ircd
         */
        int ModCount;
@@ -1549,6 +1500,9 @@ class CoreExport ModuleManager : public classbase
                PRIO_STATE_AGAIN,
                PRIO_STATE_LAST
        } prioritizationState;
+
+       /** Internal unload module hook */
+       bool CanUnload(Module*);
  public:
 
        /** Event handler hooks.
@@ -1556,6 +1510,9 @@ class CoreExport ModuleManager : public classbase
         */
        IntModuleList EventHandlers[I_END];
 
+       /** List of data services keyed by name */
+       std::multimap<std::string, ServiceProvider*> DataProviders;
+
        /** Simple, bog-standard, boring constructor.
         */
        ModuleManager();
@@ -1576,16 +1533,17 @@ class CoreExport ModuleManager : public classbase
         * PRIO_FIRST to set the event to be first called, PRIO_LAST to
         * set it to be the last called, or PRIO_BEFORE and PRIORITY_AFTER
         * to set it to be before or after one or more other modules.
-        * @param modules If PRIO_BEFORE or PRIORITY_AFTER is set in parameter 's',
-        * then this contains a list of one or more modules your module must be
-        * placed before or after. Your module will be placed before the highest
-        * priority module in this list for PRIO_BEFORE, or after the lowest
-        * priority module in this list for PRIORITY_AFTER.
-        * @param sz The number of modules being passed for PRIO_BEFORE and PRIORITY_AFTER.
-        * Defaults to 1, as most of the time you will only want to prioritize your module
-        * to be before or after one other module.
+        * @param which If PRIO_BEFORE or PRIORITY_AFTER is set in parameter 's',
+        * then this contains a the module that your module must be placed before
+        * or after.
         */
-       bool SetPriority(Module* mod, Implementation i, Priority s, Module** modules = NULL, size_t sz = 1);
+       bool SetPriority(Module* mod, Implementation i, Priority s, Module* which = NULL);
+
+       /** Backwards compat interface */
+       inline bool SetPriority(Module* mod, Implementation i, Priority s, Module** dptr)
+       {
+               return SetPriority(mod, i, s, *dptr);
+       }
 
        /** Change the priority of all events in a module.
         * @param mod The module to set the priority of
@@ -1611,13 +1569,14 @@ class CoreExport ModuleManager : public classbase
         * automatically detatch your module from all events it is attached to.
         * @param i Event type to detach
         * @param mod Module to detach event from
-        * @param Detach true if the event was detached
+        * @return True if the event was detached
         */
        bool Detach(Implementation i, Module* mod);
 
        /** Attach an array of events to a module
         * @param i Event types (array) to attach
         * @param mod Module to attach events to
+        * @param sz The size of the implementation array
         */
        void Attach(Implementation* i, Module* mod, size_t sz);
 
@@ -1633,19 +1592,29 @@ class CoreExport ModuleManager : public classbase
 
        /** Load a given module file
         * @param filename The file to load
+        * @param defer Defer module init (loading many modules)
         * @return True if the module was found and loaded
         */
-       bool Load(const char* filename);
+       bool Load(const std::string& filename, bool defer = false);
+
+       /** Unload a given module file. Note that the module will not be
+        * completely gone until the cull list has finished processing.
+        *
+        * @return true on success; if false, LastError will give a reason
+        */
+       bool Unload(Module* module);
 
-       /** Unload a given module file
-        * @param filename The file to unload
-        * @return True if the module was unloaded
+       /** Run an asynchronous reload of the given module. When the reload is
+        * complete, the callback will be run with true if the reload succeeded
+        * and false if it did not.
         */
-       bool Unload(const char* filename);
+       void Reload(Module* module, HandlerBase1<void, bool>* callback);
 
        /** Called by the InspIRCd constructor to load all modules from the config file.
         */
        void LoadAll();
+       void UnloadAll();
+       void DoSafeUnload(Module*);
 
        /** Get the total number of currently loaded modules
         * @return The number of loaded modules
@@ -1662,116 +1631,27 @@ class CoreExport ModuleManager : public classbase
         */
        Module* Find(const std::string &name);
 
-       /** Publish a 'feature'.
-        * There are two ways for a module to find another module it depends on.
-        * Either by name, using InspIRCd::FindModule, or by feature, using this
-        * function. A feature is an arbitary string which identifies something this
-        * module can do. For example, if your module provides SSL support, but other
-        * modules provide SSL support too, all the modules supporting SSL should
-        * publish an identical 'SSL' feature. This way, any module requiring use
-        * of SSL functions can just look up the 'SSL' feature using FindFeature,
-        * then use the module pointer they are given.
-        * @param FeatureName The case sensitive feature name to make available
-        * @param Mod a pointer to your module class
-        * @returns True on success, false if the feature is already published by
-        * another module.
-        */
-       bool PublishFeature(const std::string &FeatureName, Module* Mod);
-
-       /** Publish a module to an 'interface'.
-        * Modules which implement the same interface (the same way of communicating
-        * with other modules) can publish themselves to an interface, using this
-        * method. When they do so, they become part of a list of related or
-        * compatible modules, and a third module may then query for that list
-        * and know that all modules within that list offer the same API.
-        * A prime example of this is the hashing modules, which all accept the
-        * same types of Request class. Consider this to be similar to PublishFeature,
-        * except for that multiple modules may publish the same 'feature'.
-        * @param InterfaceName The case sensitive interface name to make available
-        * @param Mod a pointer to your module class
-        * @returns True on success, false on failure (there are currently no failure
-        * cases)
-        */
-       bool PublishInterface(const std::string &InterfaceName, Module* Mod);
-
-       /** Return a pair saying how many other modules are currently using the
-        * interfaces provided by module m.
-        * @param m The module to count usage for
-        * @return A pair, where the first value is the number of uses of the interface,
-        * and the second value is the interface name being used.
-        */
-       std::pair<int,std::string> GetInterfaceInstanceCount(Module* m);
-
-       /** Mark your module as using an interface.
-        * If you mark your module as using an interface, then that interface
-        * module may not unload until your module has unloaded first.
-        * This can be used to prevent crashes by ensuring code you depend on
-        * is always in memory while your module is active.
-        * @param InterfaceName The interface to use
-        */
-       void UseInterface(const std::string &InterfaceName);
-
-       /** Mark your module as finished with an interface.
-        * If you used UseInterface() above, you should use this method when
-        * your module is finished with the interface (usually in its destructor)
-        * to allow the modules which implement the given interface to be unloaded.
-        * @param InterfaceName The interface you are finished with using.
-        */
-       void DoneWithInterface(const std::string &InterfaceName);
-
-       /** Unpublish a 'feature'.
-        * When your module exits, it must call this method for every feature it
-        * is providing so that the feature table is cleaned up.
-        * @param FeatureName the feature to remove
-        */
-       bool UnpublishFeature(const std::string &FeatureName);
-
-       /** Unpublish your module from an interface
-        * When your module exits, it must call this method for every interface
-        * it is part of so that the interfaces table is cleaned up. Only when
-        * the last item is deleted from an interface does the interface get
-        * removed.
-        * @param InterfaceName the interface to be removed from
-        * @param Mod The module to remove from the interface list
-        */
-       bool UnpublishInterface(const std::string &InterfaceName, Module* Mod);
-
-       /** Find a 'feature'.
-        * There are two ways for a module to find another module it depends on.
-        * Either by name, using InspIRCd::FindModule, or by feature, using the
-        * InspIRCd::PublishFeature method. A feature is an arbitary string which
-        * identifies something this module can do. For example, if your module
-        * provides SSL support, but other modules provide SSL support too, all
-        * the modules supporting SSL should publish an identical 'SSL' feature.
-        * To find a module capable of providing the feature you want, simply
-        * call this method with the feature name you are looking for.
-        * @param FeatureName The feature name you wish to obtain the module for
-        * @returns A pointer to a valid module class on success, NULL on failure.
-        */
-       Module* FindFeature(const std::string &FeatureName);
-
-       /** Find an 'interface'.
-        * An interface is a list of modules which all implement the same API.
-        * @param InterfaceName The Interface you wish to obtain the module
-        * list of.
-        * @return A pointer to a deque of Module*, or NULL if the interface
-        * does not exist.
-        */
-       modulelist* FindInterface(const std::string &InterfaceName);
-
-       /** Determine if a module has published the named interface.
-        * This could be used in, for example, OnLoadModule to pick up other modules that can be used.
-        * @param mod The module to check.
-        * @param InterfaceName the interface you want to check for
-        * @return True if the module provides the interface, false otherwise.
-        */
-       bool ModuleHasInterface(Module* mod, const std::string& InterfaceName);
-
-       /** Given a pointer to a Module, return its filename
-        * @param m The module pointer to identify
-        * @return The module name or an empty string
-        */
-       const std::string& GetModuleName(Module* m);
+       /** Register a service provided by a module */
+       void AddService(ServiceProvider&);
+
+       /** Unregister a service provided by a module */
+       void DelService(ServiceProvider&);
+
+       inline void AddServices(ServiceProvider** list, int count)
+       {
+               for(int i=0; i < count; i++)
+                       AddService(*list[i]);
+       }
+
+       /** Find a service by name.
+        * If multiple modules provide a given service, the first one loaded will be chosen.
+        */
+       ServiceProvider* FindService(ServiceType Type, const std::string& name);
+
+       template<typename T> inline T* FindDataService(const std::string& name)
+       {
+               return static_cast<T*>(FindService(SERVICE_DATA, name));
+       }
 
        /** Return a list of all modules matching the given filter
         * @param filter This int is a bitmask of flags set in Module::Flags,
@@ -1782,14 +1662,49 @@ class CoreExport ModuleManager : public classbase
        const std::vector<std::string> GetAllModuleNames(int filter);
 };
 
+/** Do not mess with these functions unless you know the C preprocessor
+ * well enough to explain why they are needed. The order is important.
+ */
+#define MODULE_INIT_STR MODULE_INIT_STR_FN_2(MODULE_INIT_SYM)
+#define MODULE_INIT_STR_FN_2(x) MODULE_INIT_STR_FN_1(x)
+#define MODULE_INIT_STR_FN_1(x) #x
+#define MODULE_INIT_SYM MODULE_INIT_SYM_FN_2(INSPIRCD_VERSION_MAJ, INSPIRCD_VERSION_API)
+#define MODULE_INIT_SYM_FN_2(x,y) MODULE_INIT_SYM_FN_1(x,y)
+#define MODULE_INIT_SYM_FN_1(x,y) inspircd_module_ ## x ## _ ## y
+
+#ifdef PURE_STATIC
+
+struct AllCommandList {
+       typedef Command* (*fn)(Module*);
+       AllCommandList(fn cmd);
+};
+#define COMMAND_INIT(x) static Command* MK_ ## x(Module* m) { return new x(m); } \
+       static const AllCommandList PREP_ ## x(&MK_ ## x);
+
+struct AllModuleList {
+       typedef Module* (*fn)();
+       fn init;
+       std::string name;
+       AllModuleList(fn mod, const std::string& Name);
+};
+
+#define MODULE_INIT(x) static Module* MK_ ## x() { return new x; } \
+       static const AllModuleList PREP_ ## x(&MK_ ## x, MODNAMESTR);
+
+#define MODNAMESTR MODNAMESTR_FN_2(MODNAME)
+#define MODNAMESTR_FN_2(x) MODNAMESTR_FN_1(x)
+#define MODNAMESTR_FN_1(x) #x
+
+#else
+
 /** This definition is used as shorthand for the various classes
  * and functions needed to make a module loadable by the OS.
  * It defines the class factory and external init_module function.
  */
-#ifdef WINDOWS
+#ifdef _WIN32
 
 #define MODULE_INIT(y) \
-       extern "C" DllExport Module * init_module() \
+       extern "C" DllExport Module * MODULE_INIT_SYM() \
        { \
                return new y; \
        } \
@@ -1807,12 +1722,15 @@ class CoreExport ModuleManager : public classbase
 #else
 
 #define MODULE_INIT(y) \
-       extern "C" DllExport Module * init_module() \
+       extern "C" DllExport Module * MODULE_INIT_SYM() \
        { \
                return new y; \
-       }
+       } \
+       extern "C" const char inspircd_src_version[] = VERSION " r" REVISION;
 #endif
 
 #define COMMAND_INIT(c) MODULE_INIT(CommandModule<c>)
 
 #endif
+
+#endif