changeset 341:45065955ba2d

Irccd: many changes related to new split
author David Demelier <markand@malikania.fr>
date Sat, 12 Nov 2016 22:03:17 +0100
parents 0f14931a76c1
children 40abc6522cd7
files irccd/CMakeLists.txt irccd/main.cpp libirccd-js/CMakeLists.txt libirccd-js/irccd/mod-directory.cpp libirccd-js/irccd/mod-directory.hpp libirccd-js/irccd/mod-elapsed-timer.cpp libirccd-js/irccd/mod-elapsed-timer.hpp libirccd-js/irccd/mod-file.cpp libirccd-js/irccd/mod-file.hpp libirccd-js/irccd/mod-irccd.cpp libirccd-js/irccd/mod-irccd.hpp libirccd-js/irccd/mod-logger.cpp libirccd-js/irccd/mod-logger.hpp libirccd-js/irccd/mod-plugin.cpp libirccd-js/irccd/mod-plugin.hpp libirccd-js/irccd/mod-server.cpp libirccd-js/irccd/mod-server.hpp libirccd-js/irccd/mod-system.cpp libirccd-js/irccd/mod-system.hpp libirccd-js/irccd/mod-timer.cpp libirccd-js/irccd/mod-timer.hpp libirccd-js/irccd/mod-unicode.cpp libirccd-js/irccd/mod-unicode.hpp libirccd-js/irccd/mod-util.cpp libirccd-js/irccd/mod-util.hpp libirccd-js/irccd/module.hpp libirccd-js/irccd/plugin-js.cpp libirccd-js/irccd/plugin-js.hpp libirccd/irccd/irccd.cpp libirccd/irccd/server.cpp libirccd/irccd/transport.cpp
diffstat 31 files changed, 305 insertions(+), 208 deletions(-) [+]
line wrap: on
line diff
--- a/irccd/CMakeLists.txt	Sat Nov 12 22:02:54 2016 +0100
+++ b/irccd/CMakeLists.txt	Sat Nov 12 22:03:17 2016 +0100
@@ -23,7 +23,9 @@
     DESCRIPTION "The main irccd daemon."
     SOURCES CMakeLists.txt main.cpp
     INCLUDES ${irccd_SOURCE_DIR}
-    LIBRARIES libirccd
+    LIBRARIES
+        libirccd
+        $<$<BOOL:${WITH_JS}>:libirccd-js>
 )
 
 if (IRCCD_SYSTEM_MAC)
--- a/irccd/main.cpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/irccd/main.cpp	Sat Nov 12 22:03:17 2016 +0100
@@ -35,16 +35,52 @@
 
 #include <format.h>
 
+#include "cmd-plugin-reload.hpp"
+#include "cmd-plugin-unload.hpp"
+#include "cmd-server-cmode.hpp"
+#include "cmd-server-cnotice.hpp"
+#include "cmd-server-connect.hpp"
+#include "cmd-server-disconnect.hpp"
+#include "cmd-server-info.hpp"
+#include "cmd-server-invite.hpp"
+#include "cmd-server-join.hpp"
+#include "cmd-server-kick.hpp"
+#include "cmd-server-list.hpp"
+#include "cmd-server-me.hpp"
+#include "cmd-server-message.hpp"
+#include "cmd-server-mode.hpp"
+#include "cmd-server-nick.hpp"
+#include "cmd-server-notice.hpp"
+#include "cmd-server-part.hpp"
+#include "cmd-server-reconnect.hpp"
+
 #include "logger.hpp"
 #include "options.hpp"
 #include "path.hpp"
+#include "system.hpp"
+#include "config.hpp"
+#include "irccd.hpp"
+
+#include "service-command.hpp"
 #include "service-plugin.hpp"
 #include "service-rule.hpp"
 #include "service-server.hpp"
 #include "service-transport.hpp"
-#include "system.hpp"
-#include "config.hpp"
-#include "irccd.hpp"
+
+#if defined(WITH_JS)
+#   include "mod-directory.hpp"
+#   include "mod-elapsed-timer.hpp"
+#   include "mod-file.hpp"
+#   include "mod-irccd.hpp"
+#   include "mod-logger.hpp"
+#   include "mod-plugin.hpp"
+#   include "mod-server.hpp"
+#   include "mod-system.hpp"
+#   include "mod-timer.hpp"
+#   include "mod-unicode.hpp"
+#   include "mod-util.hpp"
+#   include "plugin-js.hpp"
+#endif
 
 using namespace fmt::literals;
 
@@ -84,6 +120,7 @@
     // Register some signals.
     signal(SIGINT, stop);
     signal(SIGTERM, stop);
+    signal(SIGPIPE, SIG_IGN);
 
 #if defined(SIGQUIT)
     signal(SIGQUIT, stop);
@@ -259,8 +296,44 @@
 
     option::Result options = parse(argc, argv);
 
-    // Find configuration file.
     instance = std::make_unique<Irccd>();
+    instance->commands().add(std::make_unique<command::PluginReloadCommand>());
+    instance->commands().add(std::make_unique<command::PluginUnloadCommand>());
+    instance->commands().add(std::make_unique<command::ServerChannelModeCommand>());
+    instance->commands().add(std::make_unique<command::ServerChannelNoticeCommand>());
+    instance->commands().add(std::make_unique<command::ServerConnectCommand>());
+    instance->commands().add(std::make_unique<command::ServerDisconnectCommand>());
+    instance->commands().add(std::make_unique<command::ServerInfoCommand>());
+    instance->commands().add(std::make_unique<command::ServerInviteCommand>());
+    instance->commands().add(std::make_unique<command::ServerJoinCommand>());
+    instance->commands().add(std::make_unique<command::ServerKickCommand>());
+    instance->commands().add(std::make_unique<command::ServerListCommand>());
+    instance->commands().add(std::make_unique<command::ServerMeCommand>());
+    instance->commands().add(std::make_unique<command::ServerMessageCommand>());
+    instance->commands().add(std::make_unique<command::ServerModeCommand>());
+    instance->commands().add(std::make_unique<command::ServerNickCommand>());
+    instance->commands().add(std::make_unique<command::ServerNoticeCommand>());
+    instance->commands().add(std::make_unique<command::ServerPartCommand>());
+    instance->commands().add(std::make_unique<command::ServerReconnectCommand>());
+
+    // Load Javascript API and plugin loader.
+#if defined(WITH_JS)
+    auto loader = std::make_unique<JsPluginLoader>(*instance);
+
+    loader->addModule(std::make_unique<DirectoryModule>());
+    loader->addModule(std::make_unique<ElapsedTimerModule>());
+    loader->addModule(std::make_unique<FileModule>());
+    loader->addModule(std::make_unique<IrccdModule>());
+    loader->addModule(std::make_unique<LoggerModule>());
+    loader->addModule(std::make_unique<PluginModule>());
+    loader->addModule(std::make_unique<ServerModule>());
+    loader->addModule(std::make_unique<SystemModule>());
+    loader->addModule(std::make_unique<TimerModule>());
+    loader->addModule(std::make_unique<UnicodeModule>());
+    loader->addModule(std::make_unique<UtilModule>());
+
+    instance->plugins().addLoader(std::move(loader));
+#endif
 
     try {
         load(open(options), options);
@@ -276,6 +349,4 @@
      */
     instance->run();
     instance = nullptr;
-
-    return 0;
 }
--- a/libirccd-js/CMakeLists.txt	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/CMakeLists.txt	Sat Nov 12 22:03:17 2016 +0100
@@ -45,4 +45,5 @@
         ${HEADERS}
         ${SOURCES}
     LIBRARIES extern-duktape libirccd
+    PUBLIC_INCLUDES ${libirccd-js_SOURCE_DIR}/irccd
 )
--- a/libirccd-js/irccd/mod-directory.cpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-directory.cpp	Sat Nov 12 22:03:17 2016 +0100
@@ -371,21 +371,21 @@
 {
 }
 
-void DirectoryModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
+void DirectoryModule::load(Irccd &, JsPlugin &plugin)
 {
-    StackAssert sa(plugin->context());
+    StackAssert sa(plugin.context());
 
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_c_function(plugin->context(), constructor, 2);
-    duk_put_number_list(plugin->context(), -1, constants);
-    duk_put_function_list(plugin->context(), -1, functions);
-    dukx_push_std_string(plugin->context(), std::string{fs::separator()});
-    duk_put_prop_string(plugin->context(), -2, "separator");
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, methods);
-    duk_put_prop_string(plugin->context(), -2, "prototype");
-    duk_put_prop_string(plugin->context(), -2, "Directory");
-    duk_pop(plugin->context());
+    duk_get_global_string(plugin.context(), "Irccd");
+    duk_push_c_function(plugin.context(), constructor, 2);
+    duk_put_number_list(plugin.context(), -1, constants);
+    duk_put_function_list(plugin.context(), -1, functions);
+    dukx_push_std_string(plugin.context(), std::string{fs::separator()});
+    duk_put_prop_string(plugin.context(), -2, "separator");
+    duk_push_object(plugin.context());
+    duk_put_function_list(plugin.context(), -1, methods);
+    duk_put_prop_string(plugin.context(), -2, "prototype");
+    duk_put_prop_string(plugin.context(), -2, "Directory");
+    duk_pop(plugin.context());
 }
 
 } // !irccd
--- a/libirccd-js/irccd/mod-directory.hpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-directory.hpp	Sat Nov 12 22:03:17 2016 +0100
@@ -42,7 +42,7 @@
     /**
      * \copydoc Module::load
      */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
+    IRCCD_EXPORT void load(Irccd &irccd, JsPlugin &plugin) override;
 };
 
 } // !irccd
--- a/libirccd-js/irccd/mod-elapsed-timer.cpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-elapsed-timer.cpp	Sat Nov 12 22:03:17 2016 +0100
@@ -143,19 +143,19 @@
 {
 }
 
-void ElapsedTimerModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
+void ElapsedTimerModule::load(Irccd &, JsPlugin &plugin)
 {
-    StackAssert sa(plugin->context());
+    StackAssert sa(plugin.context());
 
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_c_function(plugin->context(), constructor, 0);
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, methods);
-    duk_push_c_function(plugin->context(), destructor, 1);
-    duk_set_finalizer(plugin->context(), -2);
-    duk_put_prop_string(plugin->context(), -2, "prototype");
-    duk_put_prop_string(plugin->context(), -2, "ElapsedTimer");
-    duk_pop(plugin->context());
+    duk_get_global_string(plugin.context(), "Irccd");
+    duk_push_c_function(plugin.context(), constructor, 0);
+    duk_push_object(plugin.context());
+    duk_put_function_list(plugin.context(), -1, methods);
+    duk_push_c_function(plugin.context(), destructor, 1);
+    duk_set_finalizer(plugin.context(), -2);
+    duk_put_prop_string(plugin.context(), -2, "prototype");
+    duk_put_prop_string(plugin.context(), -2, "ElapsedTimer");
+    duk_pop(plugin.context());
 }
 
 } // !irccd
--- a/libirccd-js/irccd/mod-elapsed-timer.hpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-elapsed-timer.hpp	Sat Nov 12 22:03:17 2016 +0100
@@ -42,7 +42,7 @@
     /**
      * \copydoc Module::load
      */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
+    IRCCD_EXPORT void load(Irccd &irccd, JsPlugin &plugin) override;
 };
 
 } // !irccd
--- a/libirccd-js/irccd/mod-file.cpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-file.cpp	Sat Nov 12 22:03:17 2016 +0100
@@ -632,23 +632,23 @@
 {
 }
 
-void FileModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
+void FileModule::load(Irccd &, JsPlugin &plugin)
 {
-    StackAssert sa(plugin->context());
+    StackAssert sa(plugin.context());
 
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_c_function(plugin->context(), constructor, 2);
-    duk_put_number_list(plugin->context(), -1, constants);
-    duk_put_function_list(plugin->context(), -1, functions);
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, methods);
-    duk_push_c_function(plugin->context(), destructor, 1);
-    duk_set_finalizer(plugin->context(), -2);
-    duk_dup(plugin->context(), -1);
-    duk_put_global_string(plugin->context(), Prototype);
-    duk_put_prop_string(plugin->context(), -2, "prototype");
-    duk_put_prop_string(plugin->context(), -2, "File");
-    duk_pop(plugin->context());
+    duk_get_global_string(plugin.context(), "Irccd");
+    duk_push_c_function(plugin.context(), constructor, 2);
+    duk_put_number_list(plugin.context(), -1, constants);
+    duk_put_function_list(plugin.context(), -1, functions);
+    duk_push_object(plugin.context());
+    duk_put_function_list(plugin.context(), -1, methods);
+    duk_push_c_function(plugin.context(), destructor, 1);
+    duk_set_finalizer(plugin.context(), -2);
+    duk_dup(plugin.context(), -1);
+    duk_put_global_string(plugin.context(), Prototype);
+    duk_put_prop_string(plugin.context(), -2, "prototype");
+    duk_put_prop_string(plugin.context(), -2, "File");
+    duk_pop(plugin.context());
 }
 
 void dukx_new_file(duk_context *ctx, File *fp)
--- a/libirccd-js/irccd/mod-file.hpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-file.hpp	Sat Nov 12 22:03:17 2016 +0100
@@ -148,7 +148,7 @@
     /**
      * \copydoc Module::load
      */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
+    IRCCD_EXPORT void load(Irccd &irccd, JsPlugin &plugin) override;
 };
 
 /**
--- a/libirccd-js/irccd/mod-irccd.cpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-irccd.cpp	Sat Nov 12 22:03:17 2016 +0100
@@ -155,46 +155,46 @@
 {
 }
 
-void IrccdModule::load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin)
+void IrccdModule::load(Irccd &irccd, JsPlugin &plugin)
 {
-    StackAssert sa(plugin->context());
+    StackAssert sa(plugin.context());
 
     // Irccd.
-    duk_push_object(plugin->context());
+    duk_push_object(plugin.context());
 
     // Version.
-    duk_push_object(plugin->context());
-    duk_push_int(plugin->context(), IRCCD_VERSION_MAJOR);
-    duk_put_prop_string(plugin->context(), -2, "major");
-    duk_push_int(plugin->context(), IRCCD_VERSION_MINOR);
-    duk_put_prop_string(plugin->context(), -2, "minor");
-    duk_push_int(plugin->context(), IRCCD_VERSION_PATCH);
-    duk_put_prop_string(plugin->context(), -2, "patch");
-    duk_put_prop_string(plugin->context(), -2, "version");
+    duk_push_object(plugin.context());
+    duk_push_int(plugin.context(), IRCCD_VERSION_MAJOR);
+    duk_put_prop_string(plugin.context(), -2, "major");
+    duk_push_int(plugin.context(), IRCCD_VERSION_MINOR);
+    duk_put_prop_string(plugin.context(), -2, "minor");
+    duk_push_int(plugin.context(), IRCCD_VERSION_PATCH);
+    duk_put_prop_string(plugin.context(), -2, "patch");
+    duk_put_prop_string(plugin.context(), -2, "version");
 
     // Create the SystemError that inherits from Error.
-    duk_push_c_function(plugin->context(), constructor, 2);
+    duk_push_c_function(plugin.context(), constructor, 2);
 
     // Put errno codes into the Irccd.SystemError object.
     for (const auto &pair : errors) {
-        duk_push_int(plugin->context(), pair.second);
-        duk_put_prop_string(plugin->context(), -2, pair.first.c_str());
+        duk_push_int(plugin.context(), pair.second);
+        duk_put_prop_string(plugin.context(), -2, pair.first.c_str());
     }
 
-    duk_push_object(plugin->context());
-    duk_get_global_string(plugin->context(), "Error");
-    duk_get_prop_string(plugin->context(), -1, "prototype");
-    duk_remove(plugin->context(), -2);
-    duk_set_prototype(plugin->context(), -2);
-    duk_put_prop_string(plugin->context(), -2, "prototype");
-    duk_put_prop_string(plugin->context(), -2, "SystemError");
+    duk_push_object(plugin.context());
+    duk_get_global_string(plugin.context(), "Error");
+    duk_get_prop_string(plugin.context(), -1, "prototype");
+    duk_remove(plugin.context(), -2);
+    duk_set_prototype(plugin.context(), -2);
+    duk_put_prop_string(plugin.context(), -2, "prototype");
+    duk_put_prop_string(plugin.context(), -2, "SystemError");
 
     // Set Irccd as global.
-    duk_put_global_string(plugin->context(), "Irccd");
+    duk_put_global_string(plugin.context(), "Irccd");
 
     // Store global instance.
-    duk_push_pointer(plugin->context(), &irccd);
-    duk_put_global_string(plugin->context(), "\xff""\xff""irccd-ref");
+    duk_push_pointer(plugin.context(), &irccd);
+    duk_put_global_string(plugin.context(), "\xff""\xff""irccd-ref");
 }
 
 Irccd &dukx_get_irccd(duk_context *ctx)
--- a/libirccd-js/irccd/mod-irccd.hpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-irccd.hpp	Sat Nov 12 22:03:17 2016 +0100
@@ -77,7 +77,7 @@
     /**
      * \copydoc Module::load
      */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
+    IRCCD_EXPORT void load(Irccd &irccd, JsPlugin &plugin) override;
 };
 
 /**
--- a/libirccd-js/irccd/mod-logger.cpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-logger.cpp	Sat Nov 12 22:03:17 2016 +0100
@@ -27,7 +27,7 @@
 
 duk_ret_t print(duk_context *ctx, std::ostream &out)
 {
-    out << "plugin " << dukx_get_plugin(ctx)->name() << ": " << duk_require_string(ctx, 0) << std::endl;
+    out << "plugin " << dukx_get_plugin(ctx).name() << ": " << duk_require_string(ctx, 0) << std::endl;
 
     return 0;
 }
@@ -88,15 +88,15 @@
 {
 }
 
-void LoggerModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
+void LoggerModule::load(Irccd &, JsPlugin &plugin)
 {
-    StackAssert sa(plugin->context());
+    StackAssert sa(plugin.context());
 
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, functions);
-    duk_put_prop_string(plugin->context(), -2, "Logger");
-    duk_pop(plugin->context());
+    duk_get_global_string(plugin.context(), "Irccd");
+    duk_push_object(plugin.context());
+    duk_put_function_list(plugin.context(), -1, functions);
+    duk_put_prop_string(plugin.context(), -2, "Logger");
+    duk_pop(plugin.context());
 }
 
 } // !irccd
--- a/libirccd-js/irccd/mod-logger.hpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-logger.hpp	Sat Nov 12 22:03:17 2016 +0100
@@ -42,7 +42,7 @@
     /**
      * \copydoc Module::load
      */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
+    IRCCD_EXPORT void load(Irccd &irccd, JsPlugin &plugin) override;
 };
 
 } // !irccd
--- a/libirccd-js/irccd/mod-plugin.cpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-plugin.cpp	Sat Nov 12 22:03:17 2016 +0100
@@ -187,6 +187,7 @@
  */
 duk_idx_t info(duk_context *ctx)
 {
+#if 0
     std::shared_ptr<Plugin> plugin;
 
     if (duk_get_top(ctx) >= 1)
@@ -198,17 +199,18 @@
         return 0;
 
     duk_push_object(ctx);
-    dukx_push_std_string(ctx, plugin->name());
+    dukx_push_std_string(ctx, plugin.name());
     duk_put_prop_string(ctx, -2, "name");
-    dukx_push_std_string(ctx, plugin->author());
+    dukx_push_std_string(ctx, plugin.author());
     duk_put_prop_string(ctx, -2, "author");
-    dukx_push_std_string(ctx, plugin->license());
+    dukx_push_std_string(ctx, plugin.license());
     duk_put_prop_string(ctx, -2, "license");
-    dukx_push_std_string(ctx, plugin->summary());
+    dukx_push_std_string(ctx, plugin.summary());
     duk_put_prop_string(ctx, -2, "summary");
-    dukx_push_std_string(ctx, plugin->version());
+    dukx_push_std_string(ctx, plugin.version());
     duk_put_prop_string(ctx, -2, "version");
 
+#endif
     return 1;
 }
 
@@ -223,9 +225,11 @@
  */
 duk_idx_t list(duk_context *ctx)
 {
+#if 0
     dukx_push_array(ctx, dukx_get_irccd(ctx).plugins().list(), [] (auto ctx, auto plugin) {
-        dukx_push_std_string(ctx, plugin->name());
+        dukx_push_std_string(ctx, plugin.name());
     });
+#endif
 
     return 1;
 }
@@ -304,50 +308,38 @@
 {
 }
 
-void PluginModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
+void PluginModule::load(Irccd &, JsPlugin &plugin)
 {
-    StackAssert sa(plugin->context());
+    StackAssert sa(plugin.context());
 
-    duk_push_pointer(plugin->context(), new std::shared_ptr<JsPlugin>(plugin));
-    duk_put_global_string(plugin->context(), PluginGlobal);
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, functions);
+    duk_push_pointer(plugin.context(), &plugin);
+    duk_put_global_string(plugin.context(), PluginGlobal);
+    duk_get_global_string(plugin.context(), "Irccd");
+    duk_push_object(plugin.context());
+    duk_put_function_list(plugin.context(), -1, functions);
 
     // 'config' property.
-    duk_push_string(plugin->context(), "config");
-    duk_push_c_function(plugin->context(), getConfig, 0);
-    duk_push_c_function(plugin->context(), setConfig, 1);
-    duk_def_prop(plugin->context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER);
+    duk_push_string(plugin.context(), "config");
+    duk_push_c_function(plugin.context(), getConfig, 0);
+    duk_push_c_function(plugin.context(), setConfig, 1);
+    duk_def_prop(plugin.context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER);
 
     // 'format' property.
-    duk_push_string(plugin->context(), "format");
-    duk_push_c_function(plugin->context(), getFormat, 0);
-    duk_push_c_function(plugin->context(), setFormat, 1);
-    duk_def_prop(plugin->context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER);
+    duk_push_string(plugin.context(), "format");
+    duk_push_c_function(plugin.context(), getFormat, 0);
+    duk_push_c_function(plugin.context(), setFormat, 1);
+    duk_def_prop(plugin.context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER);
 
-    duk_put_prop_string(plugin->context(), -2, "Plugin");
-    duk_pop(plugin->context());
+    duk_put_prop_string(plugin.context(), -2, "Plugin");
+    duk_pop(plugin.context());
 }
 
-void PluginModule::unload(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
-{
-    StackAssert sa(plugin->context());
-
-    duk_push_global_object(plugin->context());
-    duk_get_prop_string(plugin->context(), -1, PluginGlobal);
-    delete static_cast<std::shared_ptr<JsPlugin> *>(duk_to_pointer(plugin->context(), -1));
-    duk_pop(plugin->context());
-    duk_del_prop_string(plugin->context(), -1, PluginGlobal);
-    duk_pop(plugin->context());
-}
-
-std::shared_ptr<JsPlugin> dukx_get_plugin(duk_context *ctx)
+JsPlugin &dukx_get_plugin(duk_context *ctx)
 {
     StackAssert sa(ctx);
 
     duk_get_global_string(ctx, PluginGlobal);
-    auto plugin = static_cast<std::shared_ptr<JsPlugin> *>(duk_to_pointer(ctx, -1));
+    auto plugin = static_cast<JsPlugin *>(duk_to_pointer(ctx, -1));
     duk_pop(ctx);
 
     return *plugin;
--- a/libirccd-js/irccd/mod-plugin.hpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-plugin.hpp	Sat Nov 12 22:03:17 2016 +0100
@@ -43,12 +43,7 @@
     /**
      * \copydoc Module::load
      */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
-
-    /**
-     * \copydoc Module::unload
-     */
-    IRCCD_EXPORT void unload(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
+    IRCCD_EXPORT void load(Irccd &irccd, irccd::JsPlugin &plugin) override;
 };
 
 /**
@@ -57,7 +52,7 @@
  * \param ctx the context
  * \return the plugin
  */
-std::shared_ptr<JsPlugin> dukx_get_plugin(duk_context *ctx);
+JsPlugin &dukx_get_plugin(duk_context *ctx);
 
 } // !irccd
 
--- a/libirccd-js/irccd/mod-server.cpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-server.cpp	Sat Nov 12 22:03:17 2016 +0100
@@ -533,22 +533,22 @@
 {
 }
 
-void ServerModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
+void ServerModule::load(Irccd &, JsPlugin &plugin)
 {
-    StackAssert sa(plugin->context());
+    StackAssert sa(plugin.context());
 
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_c_function(plugin->context(), constructor, 1);
-    duk_put_function_list(plugin->context(), -1, functions);
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, methods);
-    duk_push_c_function(plugin->context(), destructor, 1);
-    duk_set_finalizer(plugin->context(), -2);
-    duk_dup_top(plugin->context());
-    duk_put_global_string(plugin->context(), Prototype);
-    duk_put_prop_string(plugin->context(), -2, "prototype");
-    duk_put_prop_string(plugin->context(), -2, "Server");
-    duk_pop(plugin->context());
+    duk_get_global_string(plugin.context(), "Irccd");
+    duk_push_c_function(plugin.context(), constructor, 1);
+    duk_put_function_list(plugin.context(), -1, functions);
+    duk_push_object(plugin.context());
+    duk_put_function_list(plugin.context(), -1, methods);
+    duk_push_c_function(plugin.context(), destructor, 1);
+    duk_set_finalizer(plugin.context(), -2);
+    duk_dup_top(plugin.context());
+    duk_put_global_string(plugin.context(), Prototype);
+    duk_put_prop_string(plugin.context(), -2, "prototype");
+    duk_put_prop_string(plugin.context(), -2, "Server");
+    duk_pop(plugin.context());
 }
 
 void dukx_push_server(duk_context *ctx, std::shared_ptr<Server> server)
--- a/libirccd-js/irccd/mod-server.hpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-server.hpp	Sat Nov 12 22:03:17 2016 +0100
@@ -44,7 +44,7 @@
     /**
      * \copydoc Module::load
      */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
+    IRCCD_EXPORT void load(Irccd &irccd, JsPlugin &plugin) override;
 };
 
 /**
--- a/libirccd-js/irccd/mod-system.cpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-system.cpp	Sat Nov 12 22:03:17 2016 +0100
@@ -229,15 +229,15 @@
 {
 }
 
-void SystemModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
+void SystemModule::load(Irccd &, JsPlugin &plugin)
 {
-    StackAssert sa(plugin->context());
+    StackAssert sa(plugin.context());
 
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, functions);
-    duk_put_prop_string(plugin->context(), -2, "System");
-    duk_pop(plugin->context());
+    duk_get_global_string(plugin.context(), "Irccd");
+    duk_push_object(plugin.context());
+    duk_put_function_list(plugin.context(), -1, functions);
+    duk_put_prop_string(plugin.context(), -2, "System");
+    duk_pop(plugin.context());
 }
 
 } // !irccd
--- a/libirccd-js/irccd/mod-system.hpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-system.hpp	Sat Nov 12 22:03:17 2016 +0100
@@ -42,7 +42,7 @@
     /**
      * \copydoc Module::load
      */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
+    IRCCD_EXPORT void load(Irccd &irccd, JsPlugin &plugin) override;
 };
 
 } // !irccd
--- a/libirccd-js/irccd/mod-timer.cpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-timer.cpp	Sat Nov 12 22:03:17 2016 +0100
@@ -37,28 +37,30 @@
 
 void handleSignal(std::weak_ptr<JsPlugin> ptr, std::string key)
 {
+#if 0
     auto plugin = ptr.lock();
 
     if (!plugin)
         return;
 
-    auto &irccd = dukx_get_irccd(plugin->context());
+    auto &irccd = dukx_get_irccd(plugin.context());
 
     irccd.post([plugin, key] (Irccd &) {
-        StackAssert sa(plugin->context());
+        StackAssert sa(plugin.context());
 
-        duk_get_global_string(plugin->context(), CallbackTable);
-        duk_get_prop_string(plugin->context(), -1, key.c_str());
-        duk_remove(plugin->context(), -2);
+        duk_get_global_string(plugin.context(), CallbackTable);
+        duk_get_prop_string(plugin.context(), -1, key.c_str());
+        duk_remove(plugin.context(), -2);
 
-        if (duk_is_callable(plugin->context(), -1)) {
-            if (duk_pcall(plugin->context(), 0) != 0)
-                log::warning("plugin {}: {}"_format(plugin->name(), dukx_exception(plugin->context(), -1).stack));
+        if (duk_is_callable(plugin.context(), -1)) {
+            if (duk_pcall(plugin.context(), 0) != 0)
+                log::warning("plugin {}: {}"_format(plugin.name(), dukx_exception(plugin.context(), -1).stack));
             else
-                duk_pop(plugin->context());
+                duk_pop(plugin.context());
         } else
-            duk_pop(plugin->context());
+            duk_pop(plugin.context());
     });
+#endif
 }
 
 std::shared_ptr<Timer> self(duk_context *ctx)
@@ -127,6 +129,7 @@
  */
 duk_ret_t constructor(duk_context *ctx)
 {
+#if 0
     // Check parameters.
     auto type = duk_require_int(ctx, 0);
     auto delay = duk_require_int(ctx, 1);
@@ -173,7 +176,7 @@
     duk_dup(ctx, 2);
     duk_put_prop_string(ctx, -2, hash.c_str());
     duk_pop(ctx);
-
+#endif
     return 0;
 }
 
@@ -190,20 +193,20 @@
 {
 }
 
-void TimerModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
+void TimerModule::load(Irccd &, JsPlugin &plugin)
 {
-    StackAssert sa(plugin->context());
+    StackAssert sa(plugin.context());
 
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_c_function(plugin->context(), constructor, 3);
-    duk_put_number_list(plugin->context(), -1, constants);
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, methods);
-    duk_put_prop_string(plugin->context(), -2, "prototype");
-    duk_put_prop_string(plugin->context(), -2, "Timer");
-    duk_pop(plugin->context());
-    duk_push_object(plugin->context());
-    duk_put_global_string(plugin->context(), CallbackTable);
+    duk_get_global_string(plugin.context(), "Irccd");
+    duk_push_c_function(plugin.context(), constructor, 3);
+    duk_put_number_list(plugin.context(), -1, constants);
+    duk_push_object(plugin.context());
+    duk_put_function_list(plugin.context(), -1, methods);
+    duk_put_prop_string(plugin.context(), -2, "prototype");
+    duk_put_prop_string(plugin.context(), -2, "Timer");
+    duk_pop(plugin.context());
+    duk_push_object(plugin.context());
+    duk_put_global_string(plugin.context(), CallbackTable);
 }
 
 } // !irccd
--- a/libirccd-js/irccd/mod-timer.hpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-timer.hpp	Sat Nov 12 22:03:17 2016 +0100
@@ -42,7 +42,7 @@
     /**
      * \copydoc Module::load
      */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
+    IRCCD_EXPORT void load(Irccd &irccd, JsPlugin &plugin) override;
 };
 
 } // !irccd
--- a/libirccd-js/irccd/mod-unicode.cpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-unicode.cpp	Sat Nov 12 22:03:17 2016 +0100
@@ -138,15 +138,15 @@
 {
 }
 
-void UnicodeModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
+void UnicodeModule::load(Irccd &, JsPlugin &plugin)
 {
-    StackAssert sa(plugin->context());
+    StackAssert sa(plugin.context());
 
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, functions);
-    duk_put_prop_string(plugin->context(), -2, "Unicode");
-    duk_pop(plugin->context());
+    duk_get_global_string(plugin.context(), "Irccd");
+    duk_push_object(plugin.context());
+    duk_put_function_list(plugin.context(), -1, functions);
+    duk_put_prop_string(plugin.context(), -2, "Unicode");
+    duk_pop(plugin.context());
 }
 
 } // !irccd
--- a/libirccd-js/irccd/mod-unicode.hpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-unicode.hpp	Sat Nov 12 22:03:17 2016 +0100
@@ -42,7 +42,7 @@
     /**
      * \copydoc Module::load
      */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
+    IRCCD_EXPORT void load(Irccd &irccd, JsPlugin &plugin) override;
 };
 
 } // !irccd
--- a/libirccd-js/irccd/mod-util.cpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-util.cpp	Sat Nov 12 22:03:17 2016 +0100
@@ -136,15 +136,15 @@
 {
 }
 
-void UtilModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin)
+void UtilModule::load(Irccd &, JsPlugin &plugin)
 {
-    StackAssert sa(plugin->context());
+    StackAssert sa(plugin.context());
 
-    duk_get_global_string(plugin->context(), "Irccd");
-    duk_push_object(plugin->context());
-    duk_put_function_list(plugin->context(), -1, functions);
-    duk_put_prop_string(plugin->context(), -2, "Util");
-    duk_pop(plugin->context());
+    duk_get_global_string(plugin.context(), "Irccd");
+    duk_push_object(plugin.context());
+    duk_put_function_list(plugin.context(), -1, functions);
+    duk_put_prop_string(plugin.context(), -2, "Util");
+    duk_pop(plugin.context());
 }
 
 } // !irccd
--- a/libirccd-js/irccd/mod-util.hpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/mod-util.hpp	Sat Nov 12 22:03:17 2016 +0100
@@ -42,7 +42,7 @@
     /**
      * \copydoc Module::load
      */
-    IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override;
+    IRCCD_EXPORT void load(Irccd &irccd, JsPlugin &plugin) override;
 };
 
 } // !irccd
--- a/libirccd-js/irccd/module.hpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/module.hpp	Sat Nov 12 22:03:17 2016 +0100
@@ -80,18 +80,7 @@
      * \param irccd the irccd instance
      * \param plugin the plugin
      */
-    virtual void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin)
-    {
-        util::unused(irccd, plugin);
-    }
-
-    /**
-     * Unload the module from the JavaScript plugin.
-     *
-     * \param irccd the irccd instance
-     * \param plugin the plugin
-     */
-    virtual void unload(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin)
+    virtual void load(Irccd &irccd, JsPlugin &plugin)
     {
         util::unused(irccd, plugin);
     }
--- a/libirccd-js/irccd/plugin-js.cpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/plugin-js.cpp	Sat Nov 12 22:03:17 2016 +0100
@@ -140,6 +140,10 @@
     duk_put_global_string(m_context, ConfigProperty);
     duk_push_object(m_context);
     duk_put_global_string(m_context, FormatProperty);
+
+    // Used by many Javascript APIs.
+    duk_push_object(m_context);
+    duk_put_global_string(m_context, "Irccd");
 }
 
 void JsPlugin::onChannelMode(Irccd &, const ChannelModeEvent &event)
@@ -420,6 +424,20 @@
     call("onWhois", 2);
 }
 
+JsPluginLoader::JsPluginLoader(Irccd &irccd) noexcept
+    : m_irccd(irccd)
+{
+}
+
+JsPluginLoader::~JsPluginLoader() noexcept = default;
+
+void JsPluginLoader::addModule(std::unique_ptr<Module> module)
+{
+    assert(module);
+
+    m_modules.push_back(std::move(module));
+}
+
 std::shared_ptr<Plugin> JsPluginLoader::open(const std::string &id,
                                              const std::string &path) noexcept
 {
@@ -427,7 +445,15 @@
         return nullptr;
 
     try {
-        return std::make_shared<JsPlugin>(id, path);
+        auto plugin = std::make_shared<JsPlugin>(id, path);
+
+        for (const auto &mod : m_modules) {
+            log::debug() << "plugin " << plugin->name() << ": ";
+            log::debug() << "loading " << mod->name() << " Javascript API" << std::endl;
+            mod->load(m_irccd, *plugin);
+        }
+
+        return plugin;
     } catch (const std::exception &ex) {
         log::warning() << "plugin " << id << ": " << ex.what() << std::endl;
     }
--- a/libirccd-js/irccd/plugin-js.hpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd-js/irccd/plugin-js.hpp	Sat Nov 12 22:03:17 2016 +0100
@@ -24,12 +24,16 @@
  * \brief JavaScript plugins for irccd.
  */
 
+#include <vector>
+
 #include "duktape.hpp"
 #include "path.hpp"
 #include "plugin.hpp"
 
 namespace irccd {
 
+class Module;
+
 /**
  * \brief JavaScript plugins for irccd.
  * \ingroup plugins
@@ -218,7 +222,17 @@
  * \brief Implementation for searching Javascript plugins.
  */
 class JsPluginLoader : public PluginLoader {
+private:
+    Irccd &m_irccd;
+    std::vector<std::unique_ptr<Module>> m_modules;
+
 public:
+    JsPluginLoader(Irccd &irccd) noexcept;
+
+    ~JsPluginLoader() noexcept;
+
+    void addModule(std::unique_ptr<Module> module);
+
     /**
      * \copydoc PluginLoader::find
      */
--- a/libirccd/irccd/irccd.cpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd/irccd/irccd.cpp	Sat Nov 12 22:03:17 2016 +0100
@@ -54,7 +54,7 @@
 void Irccd::run()
 {
     while (m_running)
-        util::poller::poll(250, *m_interruptService, *m_servers, *m_transports);
+        util::poller::poll(250, *this);
 }
 
 void Irccd::prepare(fd_set &in, fd_set &out, net::Handle &max)
--- a/libirccd/irccd/server.cpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd/irccd/server.cpp	Sat Nov 12 22:03:17 2016 +0100
@@ -430,13 +430,13 @@
     onPart(PartEvent{shared_from_this(), strify(orig), strify(params[0]), strify(params[1])});
 }
 
-void Server::handlePing(const char *, const char **params) noexcept
+void Server::handlePing(const char *, const char **) noexcept
 {
     // Reset the timer to detect disconnection.
     m_timer.reset();
 
-    // Don't forget to respond.
-    send("PONG {}"_format(params[0]));
+//    // Don't forget to respond.
+//    send("PONG {}"_format(params[0]));
 }
 
 void Server::handleQuery(const char *orig, const char **params) noexcept
--- a/libirccd/irccd/transport.cpp	Sat Nov 12 22:02:54 2016 +0100
+++ b/libirccd/irccd/transport.cpp	Sat Nov 12 22:03:17 2016 +0100
@@ -169,6 +169,7 @@
 
     switch (m_state) {
     case Greeting:
+        FD_SET(m_socket.handle(), &in);
         FD_SET(m_socket.handle(), &out);
         break;
     case Authenticating:
@@ -195,7 +196,10 @@
 {
     switch (m_state) {
     case Greeting:
-        send();
+        if (FD_ISSET(m_socket.handle(), &in))
+            recv();
+        else if (FD_ISSET(m_socket.handle(), &out))
+            send();
 
         if (m_output.empty())
             m_state = m_parent.password().empty() ? Ready : Authenticating;