+void Module::OnBufferFlushed(User*) { }
+void Module::OnText(User*, void*, int, const std::string&, char, CUList&) { }
+void Module::OnRunTestSuite() { }
+void Module::OnNamesListItem(User*, User*, Channel*, std::string&, std::string&) { }
+int Module::OnNumeric(User*, unsigned int, const std::string&) { return 0; }
+void Module::OnHookUserIO(User*, const std::string&) { }
+
+ModuleManager::ModuleManager(InspIRCd* Ins) : ModCount(0), Instance(Ins)
+{
+}
+
+ModuleManager::~ModuleManager()
+{
+}
+
+bool ModuleManager::Attach(Implementation i, Module* mod)
+{
+ if (std::find(EventHandlers[i].begin(), EventHandlers[i].end(), mod) != EventHandlers[i].end())
+ return false;
+
+ EventHandlers[i].push_back(mod);
+ return true;
+}
+
+bool ModuleManager::Detach(Implementation i, Module* mod)
+{
+ EventHandlerIter x = std::find(EventHandlers[i].begin(), EventHandlers[i].end(), mod);
+
+ if (x == EventHandlers[i].end())
+ return false;
+
+ EventHandlers[i].erase(x);
+ return true;
+}
+
+void ModuleManager::Attach(Implementation* i, Module* mod, size_t sz)
+{
+ for (size_t n = 0; n < sz; ++n)
+ Attach(i[n], mod);
+}
+
+void ModuleManager::DetachAll(Module* mod)
+{
+ for (size_t n = I_BEGIN + 1; n != I_END; ++n)
+ Detach((Implementation)n, mod);
+}
+
+bool ModuleManager::SetPriority(Module* mod, PriorityState s)
+{
+ for (size_t n = I_BEGIN + 1; n != I_END; ++n)
+ SetPriority(mod, (Implementation)n, s);
+
+ return true;
+}
+
+bool ModuleManager::SetPriority(Module* mod, Implementation i, PriorityState s, Module** modules, size_t sz)
+{
+ /** To change the priority of a module, we first find its position in the vector,
+ * then we find the position of the other modules in the vector that this module
+ * wants to be before/after. We pick off either the first or last of these depending
+ * on which they want, and we make sure our module is *at least* before or after
+ * the first or last of this subset, depending again on the type of priority.
+ */
+ size_t swap_pos = 0;
+ size_t source = 0;
+ bool swap = true;
+ bool found = false;
+
+ /* Locate our module. This is O(n) but it only occurs on module load so we're
+ * not too bothered about it
+ */
+ for (size_t x = 0; x != EventHandlers[i].size(); ++x)
+ {
+ if (EventHandlers[i][x] == mod)
+ {
+ source = x;
+ found = true;
+ break;
+ }
+ }
+
+ /* Eh? this module doesnt exist, probably trying to set priority on an event
+ * theyre not attached to.
+ */
+ if (!found)
+ return false;
+
+ switch (s)
+ {
+ /* Dummy value */
+ case PRIO_DONTCARE:
+ swap = false;
+ break;
+ /* Module wants to be first, sod everything else */
+ case PRIO_FIRST:
+ swap_pos = 0;
+ break;
+ /* Module is submissive and wants to be last... awww. */
+ case PRIO_LAST:
+ if (EventHandlers[i].empty())
+ swap_pos = 0;
+ else
+ swap_pos = EventHandlers[i].size() - 1;
+ break;
+ /* Place this module after a set of other modules */
+ case PRIO_AFTER:
+ {
+ /* Find the latest possible position */
+ swap_pos = 0;
+ swap = false;
+ for (size_t x = 0; x != EventHandlers[i].size(); ++x)
+ {
+ for (size_t n = 0; n < sz; ++n)
+ {
+ if ((modules[n]) && (EventHandlers[i][x] == modules[n]) && (x >= swap_pos) && (source <= swap_pos))
+ {
+ swap_pos = x;
+ swap = true;
+ }
+ }
+ }
+ }
+ break;
+ /* Place this module before a set of other modules */
+ case PRIO_BEFORE:
+ {
+ swap_pos = EventHandlers[i].size() - 1;
+ swap = false;
+ for (size_t x = 0; x != EventHandlers[i].size(); ++x)
+ {
+ for (size_t n = 0; n < sz; ++n)
+ {
+ if ((modules[n]) && (EventHandlers[i][x] == modules[n]) && (x <= swap_pos) && (source >= swap_pos))
+ {
+ swap = true;
+ swap_pos = x;
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ /* Do we need to swap? */
+ if (swap && (swap_pos != source))
+ {
+ /* Suggestion from Phoenix, "shuffle" the modules to better retain call order */
+ int incrmnt = 1;
+
+ if (source > swap_pos)
+ incrmnt = -1;
+
+ for (unsigned int j = source; j != swap_pos; j += incrmnt)
+ {
+ if (( j + incrmnt > EventHandlers[i].size() - 1) || (j + incrmnt < 0))
+ continue;
+
+ std::swap(EventHandlers[i][j], EventHandlers[i][j+incrmnt]);
+ }
+ }
+
+ return true;
+}
+
+std::string& ModuleManager::LastError()
+{
+ return LastModuleError;
+}
+
+bool ModuleManager::Load(const char* filename)
+{
+ /* Do we have a glob pattern in the filename?
+ * The user wants to load multiple modules which
+ * match the pattern.
+ */
+ if (strchr(filename,'*') || (strchr(filename,'?')))
+ {
+ int n_match = 0;
+ DIR* library = opendir(Instance->Config->ModPath);
+ if (library)
+ {
+ /* Try and locate and load all modules matching the pattern */
+ dirent* entry = NULL;
+ while (0 != (entry = readdir(library)))
+ {
+ if (Instance->MatchText(entry->d_name, filename))
+ {
+ if (!this->Load(entry->d_name))
+ n_match++;
+ }
+ }
+ closedir(library);
+ }
+ /* Loadmodule will now return false if any one of the modules failed
+ * to load (but wont abort when it encounters a bad one) and when 1 or
+ * more modules were actually loaded.
+ */
+ return (n_match > 0);
+ }
+
+ char modfile[MAXBUF];
+ snprintf(modfile,MAXBUF,"%s/%s",Instance->Config->ModPath,filename);
+ std::string filename_str = filename;
+
+ if (!ServerConfig::DirValid(modfile))
+ {
+ LastModuleError = "Module " + std::string(filename) + " is not in the module directory that i am configured to look in (is "+Instance->Config->ModPath+" really a symlink?)";
+ Instance->Logs->Log("MODULE", DEFAULT, LastModuleError);
+ return false;
+ }
+
+ if (!ServerConfig::FileExists(modfile))
+ {
+ LastModuleError = "Module file could not be found: " + filename_str;
+ Instance->Logs->Log("MODULE", DEFAULT, LastModuleError);
+ return false;
+ }
+
+ if (Modules.find(filename_str) != Modules.end())
+ {
+ LastModuleError = "Module " + filename_str + " is already loaded, cannot load a module twice!";
+ Instance->Logs->Log("MODULE", DEFAULT, LastModuleError);
+ return false;
+ }
+
+ Module* newmod = NULL;
+ ircd_module* newhandle = NULL;
+
+ try
+ {
+ /* This will throw a CoreException if there's a problem loading
+ * the module file or getting a pointer to the init_module symbol.
+ */
+ newhandle = new ircd_module(Instance, modfile, "init_module");
+ newmod = newhandle->CallInit();
+
+ if (newmod)
+ {
+ Version v = newmod->GetVersion();
+
+ if (v.API != API_VERSION)
+ {
+ DetachAll(newmod);
+ delete newmod;
+ delete newhandle;
+ LastModuleError = "Unable to load " + filename_str + ": Incorrect module API version: " + ConvToStr(v.API) + " (our version: " + ConvToStr(API_VERSION) + ")";
+ Instance->Logs->Log("MODULE", DEFAULT, LastModuleError);
+ return false;
+ }
+ else
+ {
+ Instance->Logs->Log("MODULE", DEFAULT,"New module introduced: %s (API version %d, Module version %d.%d.%d.%d)%s", filename, v.API, v.Major, v.Minor, v.Revision, v.Build, (!(v.Flags & VF_VENDOR) ? " [3rd Party]" : " [Vendor]"));
+ }
+
+ Modules[filename_str] = std::make_pair(newhandle, newmod);
+ }
+ else
+ {
+ delete newhandle;
+ LastModuleError = "Unable to load " + filename_str + ": Probably missing init_module() entrypoint, but dlsym() didn't notice a problem";
+ Instance->Logs->Log("MODULE", DEFAULT, LastModuleError);
+ return false;
+ }
+ }
+ /** XXX: Is there anything we can do about this mess? -- Brain */
+ catch (LoadModuleException& modexcept)
+ {
+ DetachAll(newmod);
+ if (newmod)
+ delete newmod;
+ if (newhandle)
+ delete newhandle;
+ LastModuleError = "Unable to load " + filename_str + ": Error when loading: " + modexcept.GetReason();
+ Instance->Logs->Log("MODULE", DEFAULT, LastModuleError);
+ return false;
+ }
+ catch (FindSymbolException& modexcept)
+ {
+ DetachAll(newmod);
+ if (newmod)
+ delete newmod;
+ if (newhandle)
+ delete newhandle;
+ LastModuleError = "Unable to load " + filename_str + ": Error finding symbol: " + modexcept.GetReason();
+ Instance->Logs->Log("MODULE", DEFAULT, LastModuleError);
+ return false;
+ }
+ catch (CoreException& modexcept)
+ {
+ DetachAll(newmod);
+ if (newmod)
+ delete newmod;
+ if (newhandle)
+ delete newhandle;
+ LastModuleError = "Unable to load " + filename_str + ": " + modexcept.GetReason();
+ Instance->Logs->Log("MODULE", DEFAULT, LastModuleError);
+ return false;
+ }
+
+ this->ModCount++;
+ FOREACH_MOD_I(Instance,I_OnLoadModule,OnLoadModule(newmod, filename_str));
+
+ /* We give every module a chance to re-prioritize when we introduce a new one,
+ * not just the one thats loading, as the new module could affect the preference
+ * of others
+ */
+ for (std::map<std::string, std::pair<ircd_module*, Module*> >::iterator n = Modules.begin(); n != Modules.end(); ++n)
+ n->second.second->Prioritize();
+
+ Instance->BuildISupport();
+ return true;
+}