------------------------------------------------------------ revno: 3133 committer: poy <p...@123gen.com> branch nick: trunk timestamp: Sun 2012-11-11 18:43:20 +0100 message: update the script plugin modified: plugins/Dev/pluginsdk.cpp plugins/Script/Plugin.cpp plugins/Script/Plugin.h plugins/Script/pluginsdk.cpp plugins/Test/pluginsdk.cpp pluginsdk/cpp/pluginsdk/Hooks.cpp pluginsdk/cpp/pluginsdk/Hooks.h
-- lp:dcplusplus https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk Your team Dcplusplus-team is subscribed to branch lp:dcplusplus. To unsubscribe from this branch go to https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk/+edit-subscription
=== modified file 'plugins/Dev/pluginsdk.cpp' --- plugins/Dev/pluginsdk.cpp 2012-11-11 16:57:44 +0000 +++ plugins/Dev/pluginsdk.cpp 2012-11-11 17:43:20 +0000 @@ -3,8 +3,8 @@ #include "version.h" +#include <pluginsdk/Config.cpp> #include <pluginsdk/Core.cpp> -#include <pluginsdk/Config.cpp> #include <pluginsdk/Hooks.cpp> #include <pluginsdk/Hubs.cpp> #include <pluginsdk/Logger.cpp> === modified file 'plugins/Script/Plugin.cpp' --- plugins/Script/Plugin.cpp 2012-11-11 15:12:15 +0000 +++ plugins/Script/Plugin.cpp 2012-11-11 17:43:20 +0000 @@ -20,14 +20,20 @@ #include "Plugin.h" #include <pluginsdk/Config.h> +#include <pluginsdk/Connections.h> #include <pluginsdk/Core.h> +#include <pluginsdk/Hooks.h> +#include <pluginsdk/Hubs.h> #include <pluginsdk/Logger.h> #include <pluginsdk/Util.h> #include <boost/filesystem/operations.hpp> using dcapi::Config; +using dcapi::Connections; using dcapi::Core; +using dcapi::Hooks; +using dcapi::Hubs; using dcapi::Logger; using dcapi::Util; @@ -39,17 +45,15 @@ if(L) { if(!hubs.empty()) { for(auto& i: hubs) { - hub->remove_hub(i); - hub->release(i); + Hubs::handle()->remove_hub(i); + Hubs::handle()->release(i); } Logger::log("Script plugin warning: scripts do not correctly remove hubs they add!"); hubs.clear(); } - for(auto& i: events) - hooks->release_hook(i.second); - events.clear(); + Hooks::clear(); chatCache.clear(); @@ -62,10 +66,8 @@ case ON_INSTALL: case ON_LOAD: { - Bool res = True; instance = new Plugin(); - instance->onLoad(core, (state == ON_INSTALL), res); - return res; + return instance->onLoad(core, state == ON_INSTALL) ? True : False; } case ON_UNINSTALL: @@ -83,17 +85,11 @@ } } -void Plugin::onLoad(DCCorePtr core, bool install, Bool& loadRes) { - hooks = reinterpret_cast<DCHooksPtr>(core->query_interface(DCINTF_HOOKS, DCINTF_HOOKS_VER)); - - hub = reinterpret_cast<DCHubPtr>(core->query_interface(DCINTF_DCPP_HUBS, DCINTF_DCPP_HUBS_VER)); - connection = reinterpret_cast<DCConnectionPtr>(core->query_interface(DCINTF_DCPP_CONNECTIONS, DCINTF_DCPP_CONNECTIONS_VER)); - - if(!Util::init(core) || !Config::init(core) || !Logger::init(core) || !hooks || !hub || !connection) { - loadRes = False; - return; - } +bool Plugin::onLoad(DCCorePtr core, bool install) { Core::init(core); + if(!Config::init() || !Connections::init() || !Hooks::init() || !Hubs::init() || !Logger::init() || !Util::init()) { + return false; + } // Default settings if(Config::getConfig("ScriptPath").empty()) { @@ -110,7 +106,7 @@ if(install) { Logger::log("Script plugin installed, please restart " + Core::appName + " to begin using the plugin."); - return; + return true; } L = luaL_newstate(); @@ -124,25 +120,17 @@ Lunar<LuaManager>::Register(L); lua_pop(L, lua_gettop(L)); - events[HOOK_CHAT_OUT] = hooks->bind_hook(HOOK_CHAT_OUT, [](dcptr_t pObject, dcptr_t pData, dcptr_t, Bool*) { - return instance->onOwnChatOut(reinterpret_cast<HubDataPtr>(pObject), reinterpret_cast<char*>(pData)); }, nullptr); - - events[HOOK_HUB_ONLINE] = hooks->bind_hook(HOOK_HUB_ONLINE, [](dcptr_t pObject, dcptr_t, dcptr_t, Bool*) { - return instance->onHubConnected(reinterpret_cast<HubDataPtr>(pObject)); }, nullptr); - events[HOOK_HUB_OFFLINE] = hooks->bind_hook(HOOK_HUB_OFFLINE, [](dcptr_t pObject, dcptr_t, dcptr_t, Bool*) { - return instance->onHubDisconnected(reinterpret_cast<HubDataPtr>(pObject)); }, nullptr); - - events[HOOK_NETWORK_HUB_IN] = hooks->bind_hook(HOOK_NETWORK_HUB_IN, [](dcptr_t pObject, dcptr_t pData, dcptr_t, Bool*) { - return instance->onHubDataIn(reinterpret_cast<HubDataPtr>(pObject), reinterpret_cast<char*>(pData)); }, nullptr); - events[HOOK_NETWORK_HUB_OUT] = hooks->bind_hook(HOOK_NETWORK_HUB_OUT, [](dcptr_t pObject, dcptr_t pData, dcptr_t, Bool* bBreak) { - return instance->onHubDataOut(reinterpret_cast<HubDataPtr>(pObject), reinterpret_cast<char*>(pData), bBreak); }, nullptr); - events[HOOK_NETWORK_CONN_IN] = hooks->bind_hook(HOOK_NETWORK_CONN_IN, [](dcptr_t pObject, dcptr_t pData, dcptr_t, Bool*) { - return instance->onConnectionDataIn(reinterpret_cast<ConnectionDataPtr>(pObject), reinterpret_cast<char*>(pData)); }, nullptr); - events[HOOK_NETWORK_CONN_OUT] = hooks->bind_hook(HOOK_NETWORK_CONN_OUT, [](dcptr_t pObject, dcptr_t pData, dcptr_t, Bool*) { - return instance->onConnectionDataOut(reinterpret_cast<ConnectionDataPtr>(pObject), reinterpret_cast<char*>(pData)); }, nullptr); - - events[HOOK_UI_CHAT_COMMAND] = hooks->bind_hook(HOOK_UI_CHAT_COMMAND, [](dcptr_t pObject, dcptr_t pData, dcptr_t, Bool*) { - return instance->onHubEnter(reinterpret_cast<HubDataPtr>(pObject), reinterpret_cast<CommandDataPtr>(pData)); }, nullptr); + Hooks::Chat::onOutgoingChat([this](HubDataPtr hub, char* msg, bool&) { return onOwnChatOut(hub, msg); }); + + Hooks::Hubs::onOnline([this](HubDataPtr hub, bool&) { return onHubConnected(hub); }); + Hooks::Hubs::onOffline([this](HubDataPtr hub, bool&) { return onHubDisconnected(hub); }); + + Hooks::Network::onHubDataIn([this](HubDataPtr hub, char* msg, bool&) { return onHubDataIn(hub, msg); }); + Hooks::Network::onHubDataOut([this](HubDataPtr hub, char* msg, bool& bBreak) { return onHubDataOut(hub, msg, bBreak); }); + Hooks::Network::onClientDataIn([this](ConnectionDataPtr conn, char* msg, bool&) { return onConnectionDataIn(conn, msg); }); + Hooks::Network::onClientDataOut([this](ConnectionDataPtr conn, char* msg, bool&) { return onConnectionDataOut(conn, msg); }); + + Hooks::UI::onChatCommand([this](HubDataPtr hub, CommandDataPtr command, bool&) { return onHubEnter(hub, command); }); /// @todo let the user configure which files to auto-load auto file = Util::fromUtf8(Config::getConfig("ScriptPath")) + "startup.lua"; @@ -151,20 +139,60 @@ // This ensures that FormatChatText is only called when present... if(CheckFunction("dcpp", "FormatChatHTML")) { - events[HOOK_UI_CHAT_DISPLAY] = hooks->bind_hook(HOOK_UI_CHAT_DISPLAY, [](dcptr_t pObject, dcptr_t pData, dcptr_t, Bool* bBreak) { - return instance->onFormatChat(reinterpret_cast<UserDataPtr>(pObject), reinterpret_cast<StringDataPtr>(pData), bBreak); }, nullptr); + Hooks::UI::onChatDisplay([this](UserDataPtr user, StringDataPtr str, bool& bBreak) { return onFormatChat(user, str, bBreak); }); } + + return true; } void Plugin::setTimer(bool bState) { - auto i = events.find(HOOK_TIMER_SECOND); - if(bState && i == events.end()) { - events[HOOK_TIMER_SECOND] = hooks->bind_hook(HOOK_TIMER_SECOND, [](dcptr_t, dcptr_t, dcptr_t, Bool*) { - return instance->onTimer(); }, nullptr); - } else if(i != events.end()) { - hooks->release_hook(i->second); - i->second = nullptr; - } + static bool timerHookAdded = false; + if(bState && !timerHookAdded) { + Hooks::Timer::onSecond([this](uint64_t, bool&) { return onTimer(); }); + timerHookAdded = true; + } else if(timerHookAdded) { + Hooks::remove(HOOK_TIMER_SECOND); + timerHookAdded = false; + } +} + +HubDataPtr Plugin::createHub(const char* url, const char* nick, const char* password) { + auto hHub = Hubs::handle()->add_hub(url, nick, password); + if(!hHub) hubs.insert(hHub); + return hHub; +} + +void Plugin::destroyHub(HubDataPtr hHub) { + auto i = hubs.find(hHub); + if(i != hubs.end()) { + Hubs::handle()->remove_hub(*i); + Hubs::handle()->release(*i); + hubs.erase(i); + } +} + +void Plugin::sendHubCommand(HubDataPtr hHub, const string& cmd) { + if(hHub) + Hubs::handle()->send_protocol_cmd(hHub, cmd.c_str()); +} + +void Plugin::injectHubCommand(HubDataPtr hHub, const string& cmd) { + if(hHub) + Hubs::handle()->emulate_protocol_cmd(hHub, cmd.c_str()); +} + +void Plugin::sendUdpPacket(const char* ip, uint32_t port, const string& data) { + Connections::handle()->send_udp_data(ip, port, reinterpret_cast<dcptr_t>(const_cast<char*>(data.c_str())), data.size()); +} + +void Plugin::sendClientCommand(ConnectionDataPtr hConn, const char* cmd) { + if(hConn) + Connections::handle()->send_protocol_cmd(hConn, cmd); +} + +void Plugin::dropClientConnection(ConnectionDataPtr hConn, bool graceless) { + if(hConn) + Connections::handle()->terminate_conn(hConn, graceless ? True : False); } namespace { string formatBytes(double val) { @@ -177,7 +205,7 @@ return buf; } } -Bool Plugin::onHubEnter(HubDataPtr hHub, CommandDataPtr cmd) { +bool Plugin::onHubEnter(HubDataPtr hHub, CommandDataPtr cmd) { if(stricmp(cmd->command, "help") == 0) { if(stricmp(cmd->params, "plugins") == 0) { const char* help = @@ -188,76 +216,76 @@ "\t /luadebug \t\t\t Toggle non-fatal error messages (default: off)\n" "\t /formatchat \t\t\t Toggle Lua chat formatting hook (default: on, if available)\n"; - hub->local_message(hHub, help, MSG_SYSTEM); - return True; + Hubs::handle()->local_message(hHub, help, MSG_SYSTEM); + return true; } } else if(stricmp(cmd->command, "lua") == 0) { if(strlen(cmd->params) != 0) { ScriptInstance::EvaluateChunk(cmd->params); } else { - hub->local_message(hHub, "You must supply a parameter!", MSG_SYSTEM); + Hubs::handle()->local_message(hHub, "You must supply a parameter!", MSG_SYSTEM); } - return True; + return true; } else if(stricmp(cmd->command, "luafile") == 0) { if(strlen(cmd->params) > 4) { ScriptInstance::EvaluateFile(cmd->params); } else { - hub->local_message(hHub, "You must supply a valid parameter!", MSG_SYSTEM); + Hubs::handle()->local_message(hHub, "You must supply a valid parameter!", MSG_SYSTEM); } - return True; + return true; } else if(stricmp(cmd->command, "meminfo") == 0 || stricmp(cmd->command, "luamem") == 0) { // Run GC and get LUA memory usage lua_gc(L, LUA_GCCOLLECT, 0); double mem = lua_gc(L, LUA_GCCOUNT, 0); auto stats = "Scripts (" LUA_DIST ") are using " + formatBytes(mem) + " of system memory"; - hub->local_message(hHub, stats.c_str(), MSG_SYSTEM); - return True; + Hubs::handle()->local_message(hHub, stats.c_str(), MSG_SYSTEM); + return true; } else if(stricmp(cmd->command, "luadebug") == 0) { bool state = Config::getBoolConfig("LuaDebug"); const char* status = (state ? "Additional error messages disabled" : "Additional error messages enabled"); Config::setConfig("LuaDebug", state); - hub->local_message(hHub, status, MSG_SYSTEM); - return True; + Hubs::handle()->local_message(hHub, status, MSG_SYSTEM); + return true; } else if(stricmp(cmd->command, "formatchat") == 0) { bool state = Config::getBoolConfig("FormatChat"); const char* status = (state ? "Lua chat formatting disabled" : "Lua chat formatting enabled"); Config::setConfig("FormatChat", state); - hub->local_message(hHub, status, MSG_SYSTEM); - return True; + Hubs::handle()->local_message(hHub, status, MSG_SYSTEM); + return true; } // This stupidity is because of the API - return onOwnChatOut(hHub, string("/" + string(cmd->command) + " " + string(cmd->params)).c_str()); + return onOwnChatOut(hHub, const_cast<char*>(string("/" + string(cmd->command) + " " + string(cmd->params)).c_str())); } -Bool Plugin::onOwnChatOut(HubDataPtr hHub, const char* message) { +bool Plugin::onOwnChatOut(HubDataPtr hHub, char* message) { Lock l(cs); - return MakeCall("dcpp", "OnCommandEnter", 1, hHub, string(message)) ? GetLuaBool() : False; + return MakeCall("dcpp", "OnCommandEnter", 1, hHub, string(message)) ? static_cast<bool>(GetLuaBool()) : false; } -Bool Plugin::onHubConnected(HubDataPtr hHub) { +bool Plugin::onHubConnected(HubDataPtr hHub) { MakeCall(GetHubType(hHub), "OnHubAdded", 0, hHub); - return False; + return false; } -Bool Plugin::onHubDisconnected(HubDataPtr hHub) { +bool Plugin::onHubDisconnected(HubDataPtr hHub) { // fixme: DC++ may trigger this incorrectly (for hubs where OnHubAdded was never invoked), if socket creation fails... MakeCall(GetHubType(hHub), "OnHubRemoved", 0, hHub); removeChatCache(hHub); - return False; + return false; } -Bool Plugin::onHubDataIn(HubDataPtr hHub, const char* message) { +bool Plugin::onHubDataIn(HubDataPtr hHub, char* message) { Lock l(cs); - return MakeCall(GetHubType(hHub), "DataArrival", 1, hHub, string(message)) ? GetLuaBool() : False; + return MakeCall(GetHubType(hHub), "DataArrival", 1, hHub, string(message)) ? static_cast<bool>(GetLuaBool()) : false; } -Bool Plugin::onHubDataOut(HubDataPtr hHub, const char* message, Bool* bBreak) { +bool Plugin::onHubDataOut(HubDataPtr hHub, char* message, bool& bBreak) { string sText = message; if(sText.find("LuaExec") != string::npos) { string delim = (hHub->protocol == PROTOCOL_ADC) ? "\n" : "|"; @@ -273,27 +301,27 @@ j = ++i; } if(out.length() > 1) - hub->send_protocol_cmd(hHub, out.c_str()); + Hubs::handle()->send_protocol_cmd(hHub, out.c_str()); // We don't need to send this to other plugins - *bBreak = True; - return True; + bBreak = true; + return true; } - return False; -} - -Bool Plugin::onConnectionDataIn(ConnectionDataPtr hConn, const char* message) { - Lock l(cs); - return MakeCall("dcpp", "UserDataIn", 1, hConn, string(message)) ? GetLuaBool() : False; -} - -Bool Plugin::onConnectionDataOut(ConnectionDataPtr hConn, const char* message) { - Lock l(cs); - return MakeCall("dcpp", "UserDataOut", 1, hConn, string(message)) ? GetLuaBool() : False; -} - -Bool Plugin::onFormatChat(UserDataPtr hUser, StringDataPtr line, Bool* bBreak) { + return false; +} + +bool Plugin::onConnectionDataIn(ConnectionDataPtr hConn, char* message) { + Lock l(cs); + return MakeCall("dcpp", "UserDataIn", 1, hConn, string(message)) ? static_cast<bool>(GetLuaBool()) : false; +} + +bool Plugin::onConnectionDataOut(ConnectionDataPtr hConn, char* message) { + Lock l(cs); + return MakeCall("dcpp", "UserDataOut", 1, hConn, string(message)) ? static_cast<bool>(GetLuaBool()) : false; +} + +bool Plugin::onFormatChat(UserDataPtr hUser, StringDataPtr line, bool& bBreak) { Lock l(cs); if(!hUser || !Config::getBoolConfig("FormatChat") || !MakeCall("dcpp", "FormatChatHTML", 1, hUser, string(line->in))) return False; @@ -308,14 +336,15 @@ lua_settop(L, 0); - if(line->out != NULL) { + if(line->out) { // We don't send this to other plugins (users can control which formatting applies via plugin order) - *bBreak = True; - return True; - } else return False; + bBreak = true; + return true; + } + return false; } -Bool Plugin::onTimer() { +bool Plugin::onTimer() { MakeCall("dcpp", "OnTimer", 0, 0); - return False; + return false; } === modified file 'plugins/Script/Plugin.h' --- plugins/Script/Plugin.h 2012-11-05 20:39:11 +0000 +++ plugins/Script/Plugin.h 2012-11-11 17:43:20 +0000 @@ -35,45 +35,16 @@ void setTimer(bool bState); - HubDataPtr createHub(const char* url, const char* nick, const char* password) { - auto hHub = hub->add_hub(url, nick, password); - if(!hHub) hubs.insert(hHub); - return hHub; - } - - void destroyHub(HubDataPtr hHub) { - auto i = hubs.find(hHub); - if(i != hubs.end()) { - hub->remove_hub(*i); - hub->release(*i); - hubs.erase(i); - } - } - - void sendHubCommand(HubDataPtr hHub, const string& cmd) { - if(hHub) - hub->send_protocol_cmd(hHub, cmd.c_str()); - } - - void injectHubCommand(HubDataPtr hHub, const string& cmd) { - if(hHub) - hub->emulate_protocol_cmd(hHub, cmd.c_str()); - } + HubDataPtr createHub(const char* url, const char* nick, const char* password); + void destroyHub(HubDataPtr hHub); + + void sendHubCommand(HubDataPtr hHub, const string& cmd); + void injectHubCommand(HubDataPtr hHub, const string& cmd); // Accessors (c<->c connections) - void sendUdpPacket(const char* ip, uint32_t port, const string& data) { - connection->send_udp_data(ip, port, (dcptr_t)data.c_str(), data.size()); - } - - void sendClientCommand(ConnectionDataPtr hConn, const char* cmd) { - if(hConn) - connection->send_protocol_cmd(hConn, cmd); - } - - void dropClientConnection(ConnectionDataPtr hConn, bool graceless) { - if(hConn) - connection->terminate_conn(hConn, graceless ? True : False); - } + void sendUdpPacket(const char* ip, uint32_t port, const string& data); + void sendClientCommand(ConnectionDataPtr hConn, const char* cmd); + void dropClientConnection(ConnectionDataPtr hConn, bool graceless); static Plugin* getInstance() { return instance; } @@ -81,17 +52,17 @@ Plugin(); ~Plugin(); - void onLoad(DCCorePtr core, bool install, Bool& loadRes); - Bool onHubEnter(HubDataPtr hHub, CommandDataPtr cmd); - Bool onOwnChatOut(HubDataPtr hHub, const char* message); - Bool onHubConnected(HubDataPtr hHub); - Bool onHubDisconnected(HubDataPtr hHub); - Bool onHubDataIn(HubDataPtr hHub, const char* message); - Bool onHubDataOut(HubDataPtr hHub, const char* message, Bool* bBreak); - Bool onConnectionDataIn(ConnectionDataPtr hConn, const char* message); - Bool onConnectionDataOut(ConnectionDataPtr hConn, const char* message); - Bool onFormatChat(UserDataPtr hUser, StringDataPtr line, Bool* bBreak); - Bool onTimer(); + bool onLoad(DCCorePtr core, bool install); + bool onHubEnter(HubDataPtr hHub, CommandDataPtr cmd); + bool onOwnChatOut(HubDataPtr hHub, char* message); + bool onHubConnected(HubDataPtr hHub); + bool onHubDisconnected(HubDataPtr hHub); + bool onHubDataIn(HubDataPtr hHub, char* message); + bool onHubDataOut(HubDataPtr hHub, char* message, bool& bBreak); + bool onConnectionDataIn(ConnectionDataPtr hConn, char* message); + bool onConnectionDataOut(ConnectionDataPtr hConn, char* message); + bool onFormatChat(UserDataPtr hUser, StringDataPtr line, bool& bBreak); + bool onTimer(); void removeChatCache(const HubDataPtr hub) { auto j = chatCache.find(hub->url); @@ -99,17 +70,9 @@ chatCache.erase(j); } - map<string, subsHandle> events; map<string, string> chatCache; set<HubDataPtr> hubs; - DCHooksPtr hooks; - - DCHubPtr hub; - DCConnectionPtr connection; - - /** @todo switch to dcpp::Singleton when <http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51494> - is fixed */ static Plugin* instance; }; === modified file 'plugins/Script/pluginsdk.cpp' --- plugins/Script/pluginsdk.cpp 2012-11-04 19:15:24 +0000 +++ plugins/Script/pluginsdk.cpp 2012-11-11 17:43:20 +0000 @@ -3,7 +3,10 @@ #include "version.h" +#include <pluginsdk/Config.cpp> +#include <pluginsdk/Connections.cpp> #include <pluginsdk/Core.cpp> -#include <pluginsdk/Config.cpp> +#include <pluginsdk/Hooks.cpp> +#include <pluginsdk/Hubs.cpp> #include <pluginsdk/Logger.cpp> #include <pluginsdk/Util.cpp> === modified file 'plugins/Test/pluginsdk.cpp' --- plugins/Test/pluginsdk.cpp 2012-11-11 17:07:47 +0000 +++ plugins/Test/pluginsdk.cpp 2012-11-11 17:43:20 +0000 @@ -3,8 +3,8 @@ #include "version.h" +#include <pluginsdk/Config.cpp> #include <pluginsdk/Core.cpp> -#include <pluginsdk/Config.cpp> #include <pluginsdk/Hooks.cpp> #include <pluginsdk/Logger.cpp> #include <pluginsdk/Tagger.cpp> === modified file 'pluginsdk/cpp/pluginsdk/Hooks.cpp' --- pluginsdk/cpp/pluginsdk/Hooks.cpp 2012-11-11 16:28:14 +0000 +++ pluginsdk/cpp/pluginsdk/Hooks.cpp 2012-11-11 17:43:20 +0000 @@ -144,6 +144,14 @@ events.clear(); } +void Hooks::remove(const char* id) { + auto i = events.find(id); + if(i != events.end()) { + hooks->release_hook(i->second.first); + events.erase(i); + } +} + void Hooks::addEvent(const char* id, Callback f) { // insert first to construct map keys etc; then create the hook, using the map key as pCommon. auto it = events.insert(make_pair(id, make_pair(nullptr, f))).first; === modified file 'pluginsdk/cpp/pluginsdk/Hooks.h' --- pluginsdk/cpp/pluginsdk/Hooks.h 2012-11-11 16:28:14 +0000 +++ pluginsdk/cpp/pluginsdk/Hooks.h 2012-11-11 17:43:20 +0000 @@ -98,6 +98,7 @@ static bool empty(); static void clear(); + static void remove(const char* id); private: typedef function<bool (dcptr_t, dcptr_t, bool&)> Callback;
_______________________________________________ Mailing list: https://launchpad.net/~linuxdcpp-team Post to : linuxdcpp-team@lists.launchpad.net Unsubscribe : https://launchpad.net/~linuxdcpp-team More help : https://help.launchpad.net/ListHelp