Merge branch 'development'
[NeonServV5.git] / src / modules.c
index 0c2034e3bfdf9032ae79e346a844d8302b0473bb..f48c41a0b2b0eda7a55987eda7522398057f807f 100644 (file)
@@ -1,4 +1,4 @@
-/* modules.c - NeonServ v5.3
+/* modules.c - NeonServ v5.6
  * Copyright (C) 2011-2012  Philipp Kreil (pk910)
  * 
  * This program is free software: you can redistribute it and/or modify
  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
  */
 #include "modules.h"
+#ifndef WIN32
+#include <dlfcn.h>
+#endif
 
-/* 000-011 */ #include "main.h"
+/* 000-001 */ #include "main.h"
+/* 002-004 */ #include "tools.h"
+#ifdef ENABLE_MUTEX_DEBUG
+/* 005-006 */ #include "mutexDebug.h"
+#endif
+/* 007-009 */ /* main.h */
+/* 010     */ #include "log.h"
+/* 011     */ /* main.h */
 /* 012     */ #include "BanNode.h"
 /* 013-019 */ #include "bots.h"
 /* 020-025 */ #include "ChanNode.h"
 /* 099-102 */ #include "memoryDebug.h"
 #endif
 /* 103-106 */ #include "memoryInfo.h"
-/* 107-125 */ #include "modcmd.h"
+/* 107-122 */ #include "modcmd.h"
+/* 123     */ /* deprecated */
+/* 124-125 */ /* modcmd.h */
 /* 126-136 */ #include "ModeNode.h"
 /* 137-142 */ #include "mysqlConn.h"
 /* 143-149 */ #include "timeq.h"
-/* 150-169 */ #include "tools.h"
-/* 170-178 */ #include "UserNode.h"
-/* 179     */ #include "version.h"
-/* 180-182 */ #include "WHOHandler.h"
+/* 150-169 */ /* tools.h */
+/* 170-180 */ #include "UserNode.h"
+/* 181-183 */ #include "WHOHandler.h"
+/* 184-188 */ #include "version.h"
+/* 189     */ /* modules.h */
+/* 190     */ /* UserNode.h */
+/* 191-193 */ #include "ModuleFunctions.h"
+/* 194     */ /* bots.h */
+/* 195-196 */ /* version.h */
+/* 197-198 */ /* IRCEvents.h */
+
+#define Function void *
 
-void **global_functions = {
-/* 000 */ getStartTime,
-/* 001 */ getRunningThreads,
-/* 002 */ exit_daemon,
-/* 003 */ stricmp,
-/* 004 */ stricmplen,
-/* 005 */ restart_process,
-/* 006 */ cleanup,
-/* 007 */ restart_bot,
-/* 008 */ stop_bot,
-/* 009 */ reload_config,
-/* 010 */ putlog,
+void *global_functions[] = {
+/* 000 */ (Function) getStartTime,
+/* 001 */ (Function) getRunningThreads,
+/* 002 */ (Function) getCurrentSecondsOfDay,
+/* 003 */ (Function) stricmp,
+/* 004 */ (Function) stricmplen,
+#ifdef ENABLE_MUTEX_DEBUG
+/* 005 */ (Function) xmutex,
+/* 006 */ (Function) mutex_debug,
+#else
+/* 005 */ (Function) NULL,
+/* 006 */ (Function) NULL,
+#endif
+/* 007 */ (Function) restart_bot,
+/* 008 */ (Function) stop_bot,
+/* 009 */ (Function) reload_config,
+/* 010 */ (Function) printf_log,
 #ifdef HAVE_THREADS
-/* 011 */ getCurrentThreadID,
+/* 011 */ (Function) getCurrentThreadID,
 #else
-/* 011 */ NULL,
+/* 011 */ (Function) NULL,
 #endif
-/* 012 */ getMatchingChannelBan,
-/* 013 */ getChannelBot,
-/* 014 */ requestOp,
-/* 015 */ channel_ban_timeout,
-/* 016 */ general_event_privctcp,
-/* 017 */ set_bot_alias,
-/* 018 */ resolve_botid,
-/* 019 */ resolve_botalias,
-/* 020 */ is_valid_chan,
-/* 021 */ getAllChans,
-/* 022 */ getChanByName,
-/* 023 */ getChannelCount,
-/* 024 */ getChanUserCount,
-/* 025 */ getChanBanCount,
-/* 026 */ isUserOnChan,
-/* 027 */ getChanUser,
-/* 028 */ getChannelUsers,
-/* 029 */ getUserChannels,
-/* 030 */ create_socket,
-/* 031 */ connect_socket,
-/* 032 */ close_socket,
-/* 033 */ disconnect_socket,
-/* 034 */ write_socket,
-/* 035 */ putsock,
-/* 036 */ getBots,
-/* 037 */ get_int_field,
-/* 038 */ get_string_field,
-/* 039 */ _loadUserSettings,
-/* 040 */ isGodMode,
-/* 041 */ getChanDefault,
-/* 042 */ getChannelAccess,
-/* 043 */ checkChannelAccess,
-/* 044 */ _loadChannelSettings,
-/* 045 */ isUserProtected,
-/* 046 */ getBanAffectingMask,
-/* 047 */ renameAccount,
-/* 048 */ deleteUser,
-/* 049 */ logEvent,
-/* 050 */ lookup_authname,
-/* 051 */ bind_join,
-/* 052 */ unbind_join,
-/* 053 */ bind_nick,
-/* 054 */ unbind_nick,
-/* 055 */ bind_part,
-/* 056 */ unbind_part,
-/* 057 */ bind_quit,
-/* 058 */ unbind_quit,
-/* 059 */ bind_kick,
-/* 060 */ unbind_kick,
-/* 061 */ bind_topic,
-/* 062 */ unbind_topic,
-/* 063 */ bind_mode,
-/* 064 */ unbind_mode,
-/* 065 */ bind_chanmsg,
-/* 066 */ unbind_chanmsg,
-/* 067 */ bind_privmsg,
-/* 068 */ unbind_privmsg,
-/* 069 */ bind_channotice,
-/* 070 */ unbind_channotice,
-/* 071 */ bind_privnotice,
-/* 072 */ unbind_privnotice,
-/* 073 */ bind_chanctcp,
-/* 074 */ unbind_chanctcp,
-/* 075 */ bind_privctcp,
-/* 076 */ unbind_privctcp,
-/* 077 */ bind_invite,
-/* 078 */ unbind_invite,
-/* 079 */ bind_raw,
-/* 080 */ unbind_raw,
-/* 081 */ bind_bot_ready,
-/* 082 */ unbind_bot_ready,
-/* 083 */ bind_registered,
-/* 084 */ unbind_registered,
-/* 085 */ bind_freeuser,
-/* 086 */ unbind_freeuser,
-/* 087 */ bind_freechan,
-/* 088 */ unbind_freechan,
-/* 089 */ reply,
-/* 090 */ merge_argv,
-/* 091 */ merge_argv_char,
-/* 092 */ get_language_by_tag,
-/* 093 */ get_language_by_name,
-/* 094 */ get_default_language,
-/* 095 */ load_language,
-/* 096 */ register_default_language_table,
-/* 097 */ get_language_string,
-/* 098 */ build_language_string,
+/* 012 */ (Function) getMatchingChannelBan,
+/* 013 */ (Function) getChannelBot,
+/* 014 */ (Function) requestOp,
+/* 015 */ (Function) channel_ban_timeout,
+/* 016 */ (Function) general_event_privctcp,
+/* 017 */ (Function) set_bot_alias,
+/* 018 */ (Function) resolve_botid,
+/* 019 */ (Function) resolve_botalias,
+/* 020 */ (Function) is_valid_chan,
+/* 021 */ (Function) getAllChans,
+/* 022 */ (Function) getChanByName,
+/* 023 */ (Function) getChannelCount,
+/* 024 */ (Function) getChanUserCount,
+/* 025 */ (Function) getChanBanCount,
+/* 026 */ (Function) isUserOnChan,
+/* 027 */ (Function) getChanUser,
+/* 028 */ (Function) getChannelUsers,
+/* 029 */ (Function) getUserChannels,
+/* 030 */ (Function) create_socket,
+/* 031 */ (Function) connect_socket,
+/* 032 */ (Function) close_socket,
+/* 033 */ (Function) destroy_socket,
+/* 034 */ (Function) write_socket,
+/* 035 */ (Function) putsock,
+/* 036 */ (Function) getBots,
+/* 037 */ (Function) get_int_field,
+/* 038 */ (Function) get_string_field,
+/* 039 */ (Function) _loadUserSettings,
+/* 040 */ (Function) isGodMode,
+/* 041 */ (Function) getChanDefault,
+/* 042 */ (Function) getChannelAccess,
+/* 043 */ (Function) checkChannelAccess,
+/* 044 */ (Function) _loadChannelSettings,
+/* 045 */ (Function) isUserProtected,
+/* 046 */ (Function) getBanAffectingMask,
+/* 047 */ (Function) renameAccount,
+/* 048 */ (Function) deleteUser,
+/* 049 */ (Function) logEvent,
+/* 050 */ (Function) lookup_authname,
+/* 051 */ (Function) bind_join,
+/* 052 */ (Function) unbind_join,
+/* 053 */ (Function) bind_nick,
+/* 054 */ (Function) unbind_nick,
+/* 055 */ (Function) bind_part,
+/* 056 */ (Function) unbind_part,
+/* 057 */ (Function) bind_reload,
+/* 058 */ (Function) unbind_reload,
+/* 059 */ (Function) bind_kick,
+/* 060 */ (Function) unbind_kick,
+/* 061 */ (Function) bind_topic,
+/* 062 */ (Function) unbind_topic,
+/* 063 */ (Function) bind_mode,
+/* 064 */ (Function) unbind_mode,
+/* 065 */ (Function) bind_chanmsg,
+/* 066 */ (Function) unbind_chanmsg,
+/* 067 */ (Function) bind_privmsg,
+/* 068 */ (Function) unbind_privmsg,
+/* 069 */ (Function) bind_channotice,
+/* 070 */ (Function) unbind_channotice,
+/* 071 */ (Function) bind_privnotice,
+/* 072 */ (Function) unbind_privnotice,
+/* 073 */ (Function) bind_chanctcp,
+/* 074 */ (Function) unbind_chanctcp,
+/* 075 */ (Function) bind_privctcp,
+/* 076 */ (Function) unbind_privctcp,
+/* 077 */ (Function) bind_invite,
+/* 078 */ (Function) unbind_invite,
+/* 079 */ (Function) bind_raw,
+/* 080 */ (Function) unbind_raw,
+/* 081 */ (Function) bind_bot_ready,
+/* 082 */ (Function) unbind_bot_ready,
+/* 083 */ (Function) bind_registered,
+/* 084 */ (Function) unbind_registered,
+/* 085 */ (Function) bind_freeuser,
+/* 086 */ (Function) unbind_freeuser,
+/* 087 */ (Function) bind_freechan,
+/* 088 */ (Function) unbind_freechan,
+/* 089 */ (Function) reply,
+/* 090 */ (Function) merge_argv,
+/* 091 */ (Function) merge_argv_char,
+/* 092 */ (Function) get_language_by_tag,
+/* 093 */ (Function) get_language_by_name,
+/* 094 */ (Function) get_default_language,
+/* 095 */ (Function) load_language,
+/* 096 */ (Function) register_default_language_table,
+/* 097 */ (Function) get_language_string,
+/* 098 */ (Function) build_language_string,
 #ifdef ENABLE_MEMORY_DEBUG
-/* 099 */ xmalloc,
-/* 100 */ xcalloc,
-/* 101 */ xstrdup,
-/* 102 */ xfree,
+/* 099 */ (Function) xmalloc,
+/* 100 */ (Function) xcalloc,
+/* 101 */ (Function) xstrdup,
+/* 102 */ (Function) xfree,
 #else
-/* 099 */ NULL,
-/* 100 */ NULL,
-/* 101 */ NULL,
-/* 102 */ NULL,
+/* 099 */ (Function) NULL,
+/* 100 */ (Function) NULL,
+/* 101 */ (Function) NULL,
+/* 102 */ (Function) NULL,
 #endif
-/* 103 */ getMemoryInfoFiles,
-/* 104 */ freeMemoryInfoFiles,
-/* 105 */ getMemoryInfoLines,
-/* 106 */ freeMemoryInfoLines,
-/* 107 */ get_botwise_prefered_bot,
-/* 108 */ register_command,
-/* 109 */ set_trigger_callback,
-/* 110 */ flush_trigger_cache,
-/* 111 */ changeBotwiseChannelTrigger,
-/* 112 */ bind_botwise_cmd_to_function,
-/* 113 */ bind_botwise_cmd_to_command,
-/* 114 */ unbind_botwise_cmd,
-/* 115 */ unbind_botwise_allcmd,
-/* 116 */ bind_botwise_set_parameters,
-/* 117 */ bind_botwise_set_global_access,
-/* 118 */ bind_botwise_set_channel_access,
-/* 119 */ bind_botwise_set_bind_flags,
-/* 120 */ find_botwise_cmd_binding,
-/* 121 */ bind_botwise_unbound_required_functions,
-/* 122 */ find_cmd_function,
-/* 123 */ getTextBot,
-/* 124 */ register_command_alias,
-/* 125 */ getAllBinds,
-/* 126 */ createModeNode,
-/* 127 */ freeModeNode,
-/* 128 */ isModeSet,
-/* 129 */ isModeAffected,
-/* 130 */ getModeValue,
-/* 131 */ getModeType,
-/* 132 */ parseModes,
-/* 133 */ parseModeString,
-/* 134 */ parseMode,
-/* 135 */ getModeString,
-/* 136 */ getFullModeString,
-/* 137 */ mysql_use,
-/* 138 */ mysql_free,
-/* 139 */ printf_mysql_query,
-/* 140 */ printf_long_mysql_query,
-/* 141 */ escape_string,
-/* 142 */ get_mysql_conn,
-/* 143 */ timeq_add,
-/* 144 */ timeq_uadd,
-/* 145 */ timeq_add_name,
-/* 146 */ timeq_uadd_name,
-/* 147 */ timeq_del,
-/* 148 */ timeq_del_name,
-/* 149 */ timeq_name_exists,
-/* 150 */ match,
-/* 151 */ table_init,
-/* 152 */ table_add,
-/* 153 */ table_change,
-/* 154 */ table_change_field,
-/* 155 */ table_set_bold,
-/* 156 */ table_end,
-/* 157 */ table_free,
-/* 158 */ timeToStr,
-/* 159 */ strToTime,
-/* 160 */ initModeBuffer,
-/* 161 */ modeBufferSet,
-/* 162 */ flushModeBuffer,
-/* 163 */ freeModeBuffer,
-/* 164 */ is_ircmask,
-/* 165 */ generate_banmask,
-/* 166 */ make_banmask,
-/* 167 */ isFakeHost,
-/* 168 */ mask_match,
-/* 169 */ crc32,
-/* 170 */ is_valid_nick,
-/* 171 */ getUserByNick,
-/* 172 */ getUserByMask,
-/* 173 */ countUsersWithHost,
-/* 174 */ getAuthFakehost,
-/* 175 */ searchUserByNick,
-/* 176 */ getAllUsers,
-/* 177 */ getUsersWithAuth,
-/* 178 */ getUserCount,
-/* 179 */ get_userlist,
-/* 180 */ _get_userlist_with_invisible,
-/* 181 */ get_userauth,
-/* 182 */ &compilation,
-/* 183 */ &creation,
-/* 184 */ &revision,
-/* 185 */ &codelines,
-/* 186 */ &patchlevel
+/* 103 */ (Function) getMemoryInfoFiles,
+/* 104 */ (Function) freeMemoryInfoFiles,
+/* 105 */ (Function) getMemoryInfoLines,
+/* 106 */ (Function) freeMemoryInfoLines,
+/* 107 */ (Function) get_botwise_prefered_bot,
+/* 108 */ (Function) register_command,
+/* 109 */ (Function) set_trigger_callback,
+/* 110 */ (Function) flush_trigger_cache,
+/* 111 */ (Function) changeBotwiseChannelTrigger,
+/* 112 */ (Function) bind_botwise_cmd_to_function,
+/* 113 */ (Function) bind_botwise_cmd_to_command,
+/* 114 */ (Function) unbind_botwise_cmd,
+/* 115 */ (Function) unbind_botwise_allcmd,
+/* 116 */ (Function) bind_botwise_set_parameters,
+/* 117 */ (Function) bind_botwise_set_global_access,
+/* 118 */ (Function) bind_botwise_set_channel_access,
+/* 119 */ (Function) bind_botwise_set_bind_flags,
+/* 120 */ (Function) find_botwise_cmd_binding,
+/* 121 */ (Function) bind_botwise_unbound_required_functions,
+/* 122 */ (Function) find_cmd_function,
+/* 123 */ (Function) NULL, /* deprecated */
+/* 124 */ (Function) register_command_alias,
+/* 125 */ (Function) getAllBinds,
+/* 126 */ (Function) createModeNode,
+/* 127 */ (Function) freeModeNode,
+/* 128 */ (Function) isModeSet,
+/* 129 */ (Function) isModeAffected,
+/* 130 */ (Function) getModeValue,
+/* 131 */ (Function) getModeType,
+/* 132 */ (Function) parseModes,
+/* 133 */ (Function) parseModeString,
+/* 134 */ (Function) parseMode,
+/* 135 */ (Function) getModeString,
+/* 136 */ (Function) getFullModeString,
+/* 137 */ (Function) mysql_use,
+/* 138 */ (Function) mysql_free,
+/* 139 */ (Function) printf_mysql_query,
+/* 140 */ (Function) printf_long_mysql_query,
+/* 141 */ (Function) escape_string,
+/* 142 */ (Function) get_mysql_conn,
+/* 143 */ (Function) timeq_add,
+/* 144 */ (Function) timeq_uadd,
+/* 145 */ (Function) timeq_add_name,
+/* 146 */ (Function) timeq_uadd_name,
+/* 147 */ (Function) timeq_del,
+/* 148 */ (Function) timeq_del_name,
+/* 149 */ (Function) timeq_name_exists,
+/* 150 */ (Function) match,
+/* 151 */ (Function) table_init,
+/* 152 */ (Function) table_add,
+/* 153 */ (Function) table_change,
+/* 154 */ (Function) table_change_field,
+/* 155 */ (Function) table_set_bold,
+/* 156 */ (Function) table_end,
+/* 157 */ (Function) table_free,
+/* 158 */ (Function) timeToStr,
+/* 159 */ (Function) strToTime,
+/* 160 */ (Function) initModeBuffer,
+/* 161 */ (Function) modeBufferSet,
+/* 162 */ (Function) flushModeBuffer,
+/* 163 */ (Function) freeModeBuffer,
+/* 164 */ (Function) is_ircmask,
+/* 165 */ (Function) generate_banmask,
+/* 166 */ (Function) make_banmask,
+/* 167 */ (Function) isFakeHost,
+/* 168 */ (Function) mask_match,
+/* 169 */ (Function) crc32,
+/* 170 */ (Function) is_valid_nick,
+/* 171 */ (Function) getUserByNick,
+/* 172 */ (Function) getUserByMask,
+/* 173 */ (Function) countUsersWithHost,
+/* 174 */ (Function) getAuthFakehost,
+/* 175 */ (Function) searchUserByNick,
+/* 176 */ (Function) getAllUsers,
+/* 177 */ (Function) getUsersWithAuth,
+/* 178 */ (Function) getUserCount,
+/* 179 */ (Function) createTempUser,
+/* 180 */ (Function) createTempUserMask,
+/* 181 */ (Function) get_userlist,
+/* 182 */ (Function) _get_userlist_with_invisible,
+/* 183 */ (Function) get_userauth,
+/* 184 */ (Function) get_compilation,
+/* 185 */ (Function) get_creation,
+/* 186 */ (Function) get_revision,
+/* 187 */ (Function) get_codelines,
+/* 188 */ (Function) get_patchlevel,
+/* 189 */ (Function) get_module_name,
+/* 190 */ (Function) isUserModeSet,
+/* 191 */ (Function) module_global_cmd_register_neonbackup,
+/* 192 */ (Function) module_global_cmd_unregister_neonbackup,
+/* 193 */ (Function) module_neonbackup_recover_chan,
+/* 194 */ (Function) requestInvite,
+/* 195 */ (Function) is_stable_revision,
+/* 196 */ (Function) get_dev_revision,
+/* 197 */ (Function) bind_freeclient,
+/* 198 */ (Function) unbind_freeclient
 };
 
-#define MODINFO_STATE_STARTED 0x01
-
-struct ModuleInfo {
-    char *name;
-    int module_id;
-    #ifndef WIN32
-    void *module;
-    #else
-    HMODULE module;
-    #endif
-    int state;
-    void *startfunc;
-    void *loopfunc;
-    void *stopfunc;
-    struct ModuleInfo *next;
-}
-
 static int module_id_counter = 1;
 static struct ModuleInfo *modules = NULL;
 
 static void unregister_module_hooks(int module_id);
 
-int loadModule(char *name) {
+void loadModules() {
+    char **modulelist = get_all_fieldnames("modules");
+    if(!modulelist) return;
+    int i = 0;
+    char tmp[MAXLEN];
+    struct ModuleInfo *modinfo;
+    while(modulelist[i]) {
+        sprintf(tmp, "modules.%s.enabled", modulelist[i]);
+        if(get_int_field(tmp)) {
+            modinfo = loadModule(modulelist[i]);
+            sprintf(tmp, "modules.%s.protected", modulelist[i]);
+            if(get_int_field(tmp))
+                modinfo->state |= MODINFO_STATE_PROTECTED;
+        }
+        i++;
+    }
+    free(modulelist);
+    start_modules();
+}
+
+struct ModuleInfo *loadModule(char *name) {
+    struct ModuleInfo *modinfo;
+    for(modinfo = modules; modinfo; modinfo = modinfo->next) {
+        if(!stricmp(modinfo->name, name)) return NULL;
+    }
     char fname[256];
     #ifndef WIN32
     sprintf(fname, "%s.so", name);
     void* module = dlopen(fname, RTLD_LAZY);
     if(!module) {
-        putlog(LOGLEVEL_ERROR, "Error loading module '%s': %s not found.", name, fname);
-        return 0;
+        sprintf(fname, "./%s.so", name);
+        module = dlopen(fname, RTLD_LAZY);
+    }
+    if(!module) {
+        sprintf(fname, ".libs/%s.so", name);
+        module = dlopen(fname, RTLD_LAZY);
+    }
+    if(!module) {
+        printf_log("main", LOG_ERROR, "Error loading module '%s': %s not found.\n", name, fname);
+        return NULL;
     }
     void* initfunc = dlsym(module, "init_module");
     void* startfunc = dlsym(module, "start_module");
-    void* loopfunc = dlsym(module, "loop_module");
     void* stopfunc = dlsym(module, "stop_module");
     void* modversion = dlsym(module, "modversion");
     #else
     sprintf(fname, "%s.dll", name);
     HMODULE module = LoadLibrary(fname);
     if(!module) {
-        putlog(LOGLEVEL_ERROR, "Error loading module '%s': %s not found.", name, fname);
-        return 0;
+        printf_log("main", LOG_ERROR, "Error loading module '%s': %s not found.\n", name, fname);
+        return NULL;
     }
     FARPROC initfunc = GetProcAddress(module, "init_module");
     FARPROC startfunc = GetProcAddress(module, "start_module");
-    FARPROC loopfunc = GetProcAddress(module, "loop_module");
     FARPROC stopfunc = GetProcAddress(module, "stop_module");
     FARPROC modversion = GetProcAddress(module, "modversion");
     #endif
-    if(!startfunc || !loopfunc || !stopfunc || !modversion) {
-        putlog(LOGLEVEL_ERROR, "Error loading module '%s': required symbols not found.", name);
-        return 0;
+    if(!startfunc || !stopfunc || !modversion) {
+        printf_log("main", LOG_ERROR, "Error loading module '%s': required symbols not found.\n", name);
+        return NULL;
     }
     int version = ((int (*)(void)) modversion)();
     if(version != MODULE_VERSION) {
-        putlog(LOGLEVEL_ERROR, "Error loading module '%s': version mismatch ('%d' main code, '%d' module)", name, MODULE_VERSION, version);
-        return 0;
+        printf_log("main", LOG_ERROR, "Error loading module '%s': version mismatch ('%d' main code, '%d' module)\n", name, MODULE_VERSION, version);
+        return NULL;
     }
     //start module
     int errid;
     int module_id = module_id_counter++;
     if((errid = ((int (*)(void **, int)) initfunc)(global_functions, module_id))) {
-        putlog(LOGLEVEL_ERROR, "Error loading module '%s': module reported error (errid: %d)", name, errid);
-        return 0;
+        printf_log("main", LOG_ERROR, "Error loading module '%s': module reported error (errid: %d)\n", name, errid);
+        return NULL;
     }
-    struct ModuleInfo *modinfo = malloc(sizeof(*modinfo));
+    modinfo = malloc(sizeof(*modinfo));
     if(!modinfo) {
         unregister_module_hooks(module_id);
-        return 0;
+        return NULL;
     }
     modinfo->name = strdup(name);
     modinfo->module_id = module_id;
     modinfo->module = module;
     modinfo->startfunc = startfunc;
-    modinfo->loopfunc = loopfunc;
     modinfo->stopfunc = stopfunc;
+    modinfo->state = 0;
     modinfo->next = modules;
     modules = modinfo;
-    return 1;
+    scan_module(modinfo);
+    return modinfo;
 }
 
 #ifndef WIN32
@@ -334,46 +385,86 @@ static void closemodule(HMODULE module) {
 }
 #endif
 
-int reload_module(char *name) {
-    struct ModuleInfo *old_modinfo, *old_prev = NULL;
-    for(old_modinfo = modules; old_modinfo; old_modinfo = old_modinfo->next) {
-        if(old_prev)
-            old_prev->next = old_modinfo->next;
-        else
-            modules = old_modinfo->next;
-        unregister_module_hooks(old_modinfo->module_id);
-        ((void (*)(void)) old_modinfo->stopfunc)(MODSTATE_RELOAD);
-        closemodule(old_modinfo->module);
-        free(old_modinfo->name);
-        free(old_modinfo);
-        break;
-    } else
-        old_prev = old_modinfo;
+int ext_load_module(char *name) {
     if(!loadModule(name)) return 0;
     struct ModuleInfo *modinfo;
     for(modinfo = modules; modinfo; modinfo = modinfo->next) {
         if(!(modinfo->state & MODINFO_STATE_STARTED)) {
             modinfo->state |= MODINFO_STATE_STARTED;
-            ((void (*)(void)) modinfo->startfunc)(MODSTATE_STARTSTOP);
+            ((void (*)(int)) modinfo->startfunc)(MODSTATE_STARTSTOP);
         } else
-            ((void (*)(void)) modinfo->startfunc)(MODSTATE_REBIND);
+            ((void (*)(int)) modinfo->startfunc)(MODSTATE_REBIND);
     }
+    return 1;
 }
 
-void start_modules() {
+int ext_unload_module(char *name) {
+    struct ModuleInfo *old_modinfo, *old_prev = NULL;
+    for(old_modinfo = modules; old_modinfo; old_modinfo = old_modinfo->next) {
+        if(!stricmp(old_modinfo->name, name)) {
+            if(old_modinfo->state & MODINFO_STATE_PROTECTED) {
+                return 0;
+            }
+            if(old_prev)
+                old_prev->next = old_modinfo->next;
+            else
+                modules = old_modinfo->next;
+            unregister_module_hooks(old_modinfo->module_id);
+            ((void (*)(int)) old_modinfo->stopfunc)(MODSTATE_STARTSTOP);
+            closemodule(old_modinfo->module);
+            free_module_functions(old_modinfo);
+            free(old_modinfo->name);
+            free(old_modinfo);
+            return 1;
+        } else
+            old_prev = old_modinfo;
+    }
+    return 0;
+}
+
+int ext_reload_module(char *name) {
+    char libname[256];
+    struct ModuleInfo *old_modinfo, *old_prev = NULL;
+    for(old_modinfo = modules; old_modinfo; old_modinfo = old_modinfo->next) {
+        if(!stricmp(old_modinfo->name, name)) {
+            strcpy(libname, old_modinfo->name);
+            if(old_prev)
+                old_prev->next = old_modinfo->next;
+            else
+                modules = old_modinfo->next;
+            unregister_module_hooks(old_modinfo->module_id);
+            ((void (*)(int)) old_modinfo->stopfunc)(MODSTATE_RELOAD);
+            closemodule(old_modinfo->module);
+            free_module_functions(old_modinfo);
+            free(old_modinfo->name);
+            free(old_modinfo);
+            break;
+        } else
+            old_prev = old_modinfo;
+    }
+    if(!loadModule(libname)) return 0;
     struct ModuleInfo *modinfo;
     for(modinfo = modules; modinfo; modinfo = modinfo->next) {
         if(!(modinfo->state & MODINFO_STATE_STARTED)) {
             modinfo->state |= MODINFO_STATE_STARTED;
-            ((void (*)(void)) modinfo->startfunc)(MODSTATE_STARTSTOP);
-        }
+            ((void (*)(int)) modinfo->startfunc)(MODSTATE_RELOAD);
+        } else
+            ((void (*)(int)) modinfo->startfunc)(MODSTATE_REBIND);
     }
+    return 1;
 }
 
-void loop_modules() {
+struct ModuleInfo *ext_get_modules(struct ModuleInfo *last) {
+    return (last ? last->next : modules);
+}
+
+void start_modules() {
     struct ModuleInfo *modinfo;
     for(modinfo = modules; modinfo; modinfo = modinfo->next) {
-        ((void (*)(void)) modinfo->loopfunc)();
+        if(!(modinfo->state & MODINFO_STATE_STARTED)) {
+            modinfo->state |= MODINFO_STATE_STARTED;
+            ((void (*)(int)) modinfo->startfunc)(MODSTATE_STARTSTOP);
+        }
     }
 }
 
@@ -384,6 +475,7 @@ void stop_modules() {
         unregister_module_hooks(modinfo->module_id);
         ((void (*)(int)) modinfo->stopfunc)(MODSTATE_STARTSTOP);
         closemodule(modinfo->module);
+        free_module_functions(modinfo);
         free(modinfo->name);
         free(modinfo);
     }
@@ -394,6 +486,7 @@ static void unregister_module_hooks(int module_id) {
     unregister_module_commands(module_id);
     unregister_module_events(module_id);
     unregister_module_timers(module_id);
+    
 }
 
 int module_loaded(int module_id) {
@@ -405,3 +498,15 @@ int module_loaded(int module_id) {
     }
     return 0;
 }
+
+char *get_module_name(int module_id) {
+    if(!module_id) return NULL;
+    struct ModuleInfo *modinfo;
+    for(modinfo = modules; modinfo; modinfo = modinfo->next) {
+        if(modinfo->module_id == module_id)
+            return modinfo->name;
+    }
+    return NULL;
+}
+
+