adchpp-docker/src/adchpp/PluginManager.cpp

215 lines
5.6 KiB
C++

/*
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
*
* This program 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "adchpp.h"
#include "PluginManager.h"
#include "SimpleXML.h"
#include "LogManager.h"
#include "SocketManager.h"
#include "version.h"
#include "File.h"
#include "Text.h"
#include "Core.h"
#ifdef _WIN32
#define PLUGIN_EXT _T(".dll")
#define PM_LOAD_LIBRARY(filename) ::LoadLibrary(filename)
#define PM_UNLOAD_LIBRARY(lib) ::FreeLibrary(lib)
#define PM_GET_ADDRESS(lib, name) ::GetProcAddress(lib, name)
#define PM_GET_ERROR_STRING() Util::translateError(GetLastError())
#else
#include "dlfcn.h"
#define PLUGIN_EXT ".so"
#define PM_LOAD_LIBRARY(filename) ::dlopen(filename, RTLD_LAZY | RTLD_GLOBAL)
#define PM_UNLOAD_LIBRARY(lib) ::dlclose(lib)
#define PM_GET_ADDRESS(lib, name) ::dlsym(lib, name)
#define PM_GET_ERROR_STRING() ::dlerror()
#endif
namespace adchpp {
using namespace std;
using std::placeholders::_1;
const string PluginManager::className = "PluginManager";
PluginManager::PluginManager(Core &core) throw() : core(core) {
}
void PluginManager::attention(const function<void()>& f) {
core.addJob(f);
}
void PluginManager::load() {
for(StringIter i = plugins.begin(); i != plugins.end(); ++i) {
loadPlugin(*i + PLUGIN_EXT);
}
}
bool PluginManager::loadPlugin(const string& file) {
if(file.length() < 3) {
return false;
}
plugin_t h;
#ifndef _WIN32
if(!File::isAbsolutePath(file)) {
h = PM_LOAD_LIBRARY((pluginPath + file).c_str());
} else {
h = PM_LOAD_LIBRARY(file.c_str());
}
#else
if(!File::isAbsolutePath(file)) {
h = LoadLibraryEx((pluginPath + file).c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES);
} else {
h = LoadLibraryEx(file.c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES);
}
#endif
if(h == NULL) {
LOG(className, "Failed to load " + Text::utf8ToAcp(file) + ": " + PM_GET_ERROR_STRING());
return false;
}
PLUGIN_GET_VERSION v = (PLUGIN_GET_VERSION)PM_GET_ADDRESS(h, "pluginGetVersion");
if(v != NULL) {
double ver = v();
if(ver == PLUGINVERSION) {
#ifdef _WIN32
// Reload plugin with references resolved...
FreeLibrary(h);
if(!File::isAbsolutePath(file)) {
h = PM_LOAD_LIBRARY((pluginPath + file).c_str());
} else {
h = PM_LOAD_LIBRARY(file.c_str());
}
if(h == NULL) {
LOG(className, "Failed to load " + Text::utf8ToAcp(file) + ": " + PM_GET_ERROR_STRING());
return false;
}
#endif
PLUGIN_LOAD l = (PLUGIN_LOAD)PM_GET_ADDRESS(h, "pluginLoad");
PLUGIN_UNLOAD u = (PLUGIN_UNLOAD)PM_GET_ADDRESS(h, "pluginUnload");
if(l != NULL && u != NULL) {
int i = l(this);
if(i != 0) {
LOG(className, "Failed to load plugin " + Text::utf8ToAcp(file) + " (Error " + Util::toString(i) + ")");
} else {
// Wonderful, we have a plugin...
active.push_back(PluginInfo(h, v, l, u));
LOG(className, Text::utf8ToAcp(file) + " loaded");
return true;
}
} else {
LOG(className, Text::utf8ToAcp(file) + " is not a valid ADCH++ plugin");
}
} else {
LOG(className, Text::utf8ToAcp(file) + " is for another version of ADCH++ (" + Util::toString(ver) + "), please get the correct one from the author");
}
} else {
LOG(className, Text::utf8ToAcp(file) + " is not a valid ADCH++ plugin");
}
PM_UNLOAD_LIBRARY(h);
return false;
}
void PluginManager::shutdown() {
registry.clear();
for(PluginList::reverse_iterator i = active.rbegin(); i != active.rend(); ++i)
i->pluginUnload();
#ifndef HAVE_BROKEN_MTALLOC
for(PluginList::reverse_iterator i = active.rbegin(); i != active.rend(); ++i)
PM_UNLOAD_LIBRARY(i->handle);
#endif
active.clear();
}
PluginManager::CommandDispatch::CommandDispatch(PluginManager& pm, const std::string& name_, const PluginManager::CommandSlot& f_) :
name('+' + name_),
f(f_),
pm(&pm)
{
}
void PluginManager::CommandDispatch::operator()(Entity& e, AdcCommand& cmd, bool& ok) {
if(e.getState() != Entity::STATE_NORMAL) {
return;
}
if(cmd.getCommand() != AdcCommand::CMD_MSG) {
return;
}
if(cmd.getParameters().size() < 1) {
return;
}
StringList l;
Util::tokenize(l, cmd.getParameters()[0], ' ');
if(l[0] != name) {
return;
}
l[0] = name.substr(1);
if(!pm->handleCommand(e, l)) {
return;
}
cmd.setPriority(AdcCommand::PRIORITY_IGNORE);
f(e, l, ok);
}
ClientManager::SignalReceive::Connection PluginManager::onCommand(const std::string& commandName, const CommandSlot& f) {
return core.getClientManager().signalReceive().connect(CommandDispatch(*this, commandName, f));
}
PluginManager::CommandSignal& PluginManager::getCommandSignal(const std::string& commandName) {
CommandHandlers::iterator i = commandHandlers.find(commandName);
if(i == commandHandlers.end())
return commandHandlers.insert(make_pair(commandName, CommandSignal())).first->second;
return i->second;
}
bool PluginManager::handleCommand(Entity& e, const StringList& l) {
CommandHandlers::iterator i = commandHandlers.find(l[0]);
if(i == commandHandlers.end())
return true;
bool ok = true;
i->second(e, l, ok);
return ok;
}
Core &PluginManager::getCore() { return core; }
}