Mercurial > irccd
changeset 820:9109f19987fb
misc: remove js_ prefix in js namespace
line wrap: on
line diff
--- a/README.md Mon Nov 26 21:53:27 2018 +0100 +++ b/README.md Thu Nov 29 21:28:48 2018 +0100 @@ -18,7 +18,7 @@ - Can connect to multiple servers, - Support multiple identities, - Can be controlled by sockets and irccdctl, - - Runs on Linux, Windows and *BSD, + - Runs on Linux, Windows and \*BSD, - Extremely well documented, - Clean and powerful JavaScript API, - Very fast and light.
--- a/irccd-test/main.cpp Mon Nov 26 21:53:27 2018 +0100 +++ b/irccd-test/main.cpp Thu Nov 29 21:28:48 2018 +0100 @@ -43,8 +43,8 @@ #include <irccd/test/debug_server.hpp> #if defined(IRCCD_HAVE_JS) -# include <irccd/js/js_api.hpp> -# include <irccd/js/js_plugin.hpp> +# include <irccd/js/api.hpp> +# include <irccd/js/plugin.hpp> #endif using boost::format; @@ -59,8 +59,8 @@ using irccd::daemon::whois_event; using irccd::daemon::dynlib_plugin_loader; -using irccd::js::js_plugin_loader; -using irccd::js::js_api; +using irccd::js::plugin_loader; +using irccd::js::api; namespace irccd::test { @@ -645,9 +645,9 @@ daemon->plugins().add_loader(std::make_unique<dynlib_plugin_loader>()); #if defined(IRCCD_HAVE_JS) - auto loader = std::make_unique<js_plugin_loader>(*daemon); + auto loader = std::make_unique<plugin_loader>(*daemon); - for (const auto& f : js_api::registry()) + for (const auto& f : api::registry()) loader->get_modules().push_back(f()); daemon->plugins().add_loader(std::move(loader));
--- a/irccd/main.cpp Mon Nov 26 21:53:27 2018 +0100 +++ b/irccd/main.cpp Thu Nov 29 21:28:48 2018 +0100 @@ -35,8 +35,8 @@ #include <irccd/daemon/transport_service.hpp> #if defined(IRCCD_HAVE_JS) -# include <irccd/js/js_api.hpp> -# include <irccd/js/js_plugin.hpp> +# include <irccd/js/api.hpp> +# include <irccd/js/plugin.hpp> #endif namespace irccd::daemon { @@ -185,9 +185,9 @@ instance->plugins().add_loader(std::make_unique<dynlib_plugin_loader>()); #if defined(IRCCD_HAVE_JS) - auto loader = std::make_unique<js::js_plugin_loader>(*instance); + auto loader = std::make_unique<js::plugin_loader>(*instance); - for (const auto& f : js::js_api::registry()) + for (const auto& f : js::api::registry()) loader->get_modules().push_back(f()); instance->plugins().add_loader(std::move(loader));
--- a/libirccd-js/CMakeLists.txt Mon Nov 26 21:53:27 2018 +0100 +++ b/libirccd-js/CMakeLists.txt Thu Nov 29 21:28:48 2018 +0100 @@ -22,50 +22,49 @@ set( SOURCES - ${libirccd-js_SOURCE_DIR}/irccd/js.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/directory_js_api.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/directory_js_api.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/duk.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/duk.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/elapsed_timer_js_api.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/elapsed_timer_js_api.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/file_js_api.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/file_js_api.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/irccd_js_api.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/irccd_js_api.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/js_api.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/js_api.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/js_plugin.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/js_plugin.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/logger_js_api.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/logger_js_api.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/plugin_js_api.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/plugin_js_api.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/server_js_api.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/server_js_api.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/system_js_api.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/system_js_api.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/timer_js_api.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/timer_js_api.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/unicode.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/unicode.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/unicode_js_api.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/unicode_js_api.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/util_js_api.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/util_js_api.hpp + ${PROJECT_SOURCE_DIR}/irccd/js.hpp + ${PROJECT_SOURCE_DIR}/irccd/js/api.cpp + ${PROJECT_SOURCE_DIR}/irccd/js/api.hpp + ${PROJECT_SOURCE_DIR}/irccd/js/directory_api.cpp + ${PROJECT_SOURCE_DIR}/irccd/js/directory_api.hpp + ${PROJECT_SOURCE_DIR}/irccd/js/duk.cpp + ${PROJECT_SOURCE_DIR}/irccd/js/duk.hpp + ${PROJECT_SOURCE_DIR}/irccd/js/elapsed_timer_api.cpp + ${PROJECT_SOURCE_DIR}/irccd/js/elapsed_timer_api.hpp + ${PROJECT_SOURCE_DIR}/irccd/js/file_api.cpp + ${PROJECT_SOURCE_DIR}/irccd/js/file_api.hpp + ${PROJECT_SOURCE_DIR}/irccd/js/irccd_api.cpp + ${PROJECT_SOURCE_DIR}/irccd/js/irccd_api.hpp + ${PROJECT_SOURCE_DIR}/irccd/js/plugin.cpp + ${PROJECT_SOURCE_DIR}/irccd/js/plugin.hpp + ${PROJECT_SOURCE_DIR}/irccd/js/logger_api.cpp + ${PROJECT_SOURCE_DIR}/irccd/js/logger_api.hpp + ${PROJECT_SOURCE_DIR}/irccd/js/plugin_api.cpp + ${PROJECT_SOURCE_DIR}/irccd/js/plugin_api.hpp + ${PROJECT_SOURCE_DIR}/irccd/js/server_api.cpp + ${PROJECT_SOURCE_DIR}/irccd/js/server_api.hpp + ${PROJECT_SOURCE_DIR}/irccd/js/system_api.cpp + ${PROJECT_SOURCE_DIR}/irccd/js/system_api.hpp + ${PROJECT_SOURCE_DIR}/irccd/js/timer_api.cpp + ${PROJECT_SOURCE_DIR}/irccd/js/timer_api.hpp + ${PROJECT_SOURCE_DIR}/irccd/js/unicode.cpp + ${PROJECT_SOURCE_DIR}/irccd/js/unicode.hpp + ${PROJECT_SOURCE_DIR}/irccd/js/unicode_api.cpp + ${PROJECT_SOURCE_DIR}/irccd/js/unicode_api.hpp + ${PROJECT_SOURCE_DIR}/irccd/js/util_api.cpp + ${PROJECT_SOURCE_DIR}/irccd/js/util_api.hpp ) irccd_define_library( TARGET libirccd-js EXPORT - HEADERS ${libirccd-js_SOURCE_DIR}/irccd/ - SOURCES - ${SOURCES} + HEADERS ${PROJECT_SOURCE_DIR}/irccd/ + SOURCES ${SOURCES} LIBRARIES Boost::timer libduktape libirccd-daemon PUBLIC_INCLUDES - $<BUILD_INTERFACE:${libirccd-js_SOURCE_DIR}> + $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}> )
--- a/libirccd-js/irccd/js.hpp Mon Nov 26 21:53:27 2018 +0100 +++ b/libirccd-js/irccd/js.hpp Thu Nov 29 21:28:48 2018 +0100 @@ -26,20 +26,20 @@ #include "sysconfig.hpp" -#include "js/file_js_api.hpp" +#include "js/api.hpp" +#include "js/directory_api.hpp" +#include "js/duk.hpp" +#include "js/elapsed_timer_api.hpp" +#include "js/file_api.hpp" +#include "js/irccd_api.hpp" +#include "js/logger_api.hpp" +#include "js/plugin.hpp" +#include "js/plugin_api.hpp" +#include "js/server_api.hpp" +#include "js/system_api.hpp" +#include "js/timer_api.hpp" #include "js/unicode.hpp" -#include "js/directory_js_api.hpp" -#include "js/js_plugin.hpp" -#include "js/js_api.hpp" -#include "js/timer_js_api.hpp" -#include "js/unicode_js_api.hpp" -#include "js/util_js_api.hpp" -#include "js/duk.hpp" -#include "js/elapsed_timer_js_api.hpp" -#include "js/system_js_api.hpp" -#include "js/logger_js_api.hpp" -#include "js/irccd_js_api.hpp" -#include "js/plugin_js_api.hpp" -#include "js/server_js_api.hpp" +#include "js/unicode_api.hpp" +#include "js/util_api.hpp" #endif // !IRCCD_JS_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/api.cpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,65 @@ +/* + * api.cpp -- Javascript API module + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "directory_api.hpp" +#include "elapsed_timer_api.hpp" +#include "file_api.hpp" +#include "irccd_api.hpp" +#include "logger_api.hpp" +#include "plugin_api.hpp" +#include "server_api.hpp" +#include "system_api.hpp" +#include "timer_api.hpp" +#include "unicode_api.hpp" +#include "util_api.hpp" + +namespace irccd::js { + +namespace { + +template <typename T> +auto bind() noexcept -> api::constructor +{ + return [] () noexcept { + return std::make_unique<T>(); + }; +} + +} // !namespace + +auto api::registry() noexcept -> const std::vector<constructor>& +{ + static const std::vector<constructor> list { + // Irccd API must be loaded first. + bind<irccd_api>(), + bind<directory_api>(), + bind<elapsed_timer_api>(), + bind<file_api>(), + bind<logger_api>(), + bind<plugin_api>(), + bind<server_api>(), + bind<system_api>(), + bind<timer_api>(), + bind<unicode_api>(), + bind<util_api>() + }; + + return list; +} + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/api.hpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,92 @@ +/* + * api.hpp -- Javascript API module + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_API_HPP +#define IRCCD_JS_API_HPP + +/** + * \file api.hpp + * \brief Javascript API module. + */ + +#include <functional> +#include <memory> +#include <string_view> +#include <vector> + +#include "duk.hpp" + +namespace irccd { + +namespace daemon { + +class bot; + +} // !daemon + +namespace js { + +class plugin; + +/** + * \ingroup js-api + * \brief Javascript API module. + */ +class api { +public: + /** + * \brief Command constructor factory. + */ + using constructor = std::function<std::unique_ptr<api> ()>; + + /** + * \brief Registry of all commands. + */ + static auto registry() noexcept -> const std::vector<constructor>&; + + /** + * Default constructor. + */ + api() noexcept = default; + + /** + * Virtual destructor defaulted. + */ + virtual ~api() noexcept = default; + + /** + * Get the module name. + * + * \return the name + */ + virtual auto get_name() const noexcept -> std::string_view = 0; + + /** + * Load the module into the Javascript plugin. + * + * \param bot the irccd instance + * \param plugin the plugin + */ + virtual void load(daemon::bot& bot, std::shared_ptr<plugin> plugin) = 0; +}; + +} // !js + +} // !irccd + +#endif // !IRCCD_JS_API_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/directory_api.cpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,406 @@ +/* + * directory_api.cpp -- Irccd.Directory API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <boost/predef/os.h> + +#include <irccd/sysconfig.hpp> + +#include <cerrno> +#include <cstdio> +#include <cstring> +#include <fstream> +#include <regex> +#include <stdexcept> +#include <string> + +#include <irccd/fs_util.hpp> + +#include "directory_api.hpp" +#include "irccd_api.hpp" +#include "plugin.hpp" + +namespace fs = boost::filesystem; + +namespace irccd::js { + +namespace { + +// {{{ wrap + +/* + * Wrap the function and raise appropriate error in case of need. + */ +template <typename Handler> +auto wrap(duk_context* ctx, Handler handler) -> duk_ret_t +{ + try { + return handler(); + } catch (const boost::system::system_error& ex) { + duk::raise(ctx, ex); + } catch (const std::system_error& ex) { + duk::raise(ctx, ex); + } catch (const std::exception& ex) { + duk::raise(ctx, ex); + } + + return 0; +} + +// }}} + +// {{{ path + +/* + * Get the path associated with the directory object as this. + */ +auto path(duk_context* ctx) -> std::string +{ + duk_push_this(ctx); + duk_get_prop_string(ctx, -1, "path"); + + if (duk_get_type(ctx, -1) != DUK_TYPE_STRING) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Directory object"); + + const auto ret = duk::get<std::string>(ctx, -1); + + if (ret.empty()) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "directory object has empty path"); + + duk_pop_n(ctx, 2); + + return ret; +} + +// }}} + +// {{{ find + +/* + * Generic find function for: + * + * - Irccd.Directory.find + * - Irccd.Directory.prototype.find + * + * The pattern_index is the argument where to test if the argument is a regex or + * a string. + */ +auto find(duk_context* ctx, std::string base, bool recursive, int pattern_index) -> duk_ret_t +{ + /* + * Helper for checking if it's a valid RegExp object. + */ + const auto is_regex = [&] { + duk_get_global_string(ctx, "RegExp"); + const auto result = duk_instanceof(ctx, pattern_index, -1); + duk_pop(ctx); + + return result; + }; + + /* + * Helper for getting regex source. + */ + const auto pattern = [&] { + duk_get_prop_string(ctx, pattern_index, "source"); + const auto pattern = duk_to_string(ctx, -1); + duk_pop(ctx); + + return pattern; + }; + + std::string path; + + if (duk_is_string(ctx, pattern_index)) + path = fs_util::find(base, duk::get<std::string>(ctx, pattern_index), recursive); + else if (is_regex()) + path = fs_util::find(base, pattern(), recursive); + else + throw duk::type_error("pattern must be a string or a regex expression"); + + if (path.empty()) + return 0; + + return duk::push(ctx, path); +} + +// }}} + +// {{{ remove + +/* + * Generic remove function for: + * + * - Irccd.Directory.remove + * - Irccd.Directory.prototype.remove + */ +auto remove(const std::string& path, bool recursive) -> duk_ret_t +{ + if (!boost::filesystem::is_directory(path)) + throw std::system_error(make_error_code(std::errc::invalid_argument)); + + if (!recursive) + boost::filesystem::remove(path); + else + boost::filesystem::remove_all(path); + + return 0; +} + +// }}} + +// {{{ Irccd.Directory.prototype.find + +/* + * Method: Irccd.Directory.prototype.find(pattern, recursive) + * -------------------------------------------------------- + * + * Synonym of Irccd.Directory.find(path, pattern, recursive) but the path is + * taken from the directory object. + * + * Arguments: + * - pattern, the regular expression or file name, + * - recursive, set to true to search recursively (default: false). + * Returns: + * The path to the file or undefined if not found. + * Throws: + * - Irccd.SystemError on errors + */ +auto Directory_prototype_find(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return find(ctx, path(ctx), duk::get<bool>(ctx, 1), 0); + }); +} + +// }}} + +// {{{ Irccd.Directory.prototype.remove + +/* + * Method: Irccd.Directory.prototype.remove(recursive) + * -------------------------------------------------------- + * + * Synonym of Directory.remove(recursive) but the path is taken from the + * directory object. + * + * Arguments: + * - recursive, recursively or not (default: false). + * Throws: + * - Irccd.SystemError on errors + */ +auto Directory_prototype_remove(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return remove(path(ctx), duk::get<bool>(ctx, 0)); + }); +} + +// }}} + +// {{{ Irccd.Directory [constructor] + +/* + * Function: Irccd.Directory(path) [constructor] + * -------------------------------------------------------- + * + * Opens and read the directory at the specified path. + * + * Arguments: + * - path, the path to the directory, + * Throws: + * - Irccd.SystemError on errors + */ +auto Directory_constructor(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + if (!duk_is_constructor_call(ctx)) + return 0; + + const auto path = duk::require<std::string>(ctx, 0); + + if (!boost::filesystem::is_directory(path)) + throw std::system_error(make_error_code(std::errc::invalid_argument)); + + duk_push_this(ctx); + + // 'entries' property. + duk_push_string(ctx, "entries"); + duk_push_array(ctx); + + unsigned i = 0; + for (const auto& entry : boost::filesystem::directory_iterator(path)) { + duk_push_object(ctx); + duk::push(ctx, entry.path().filename().string()); + duk_put_prop_string(ctx, -2, "name"); + duk_push_int(ctx, entry.status().type()); + duk_put_prop_string(ctx, -2, "type"); + duk_put_prop_index(ctx, -2, i++); + } + + duk_def_prop(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); + + // 'path' property. + duk::push(ctx, "path"); + duk::push(ctx, path); + duk_def_prop(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Directory.find + +/* + * Function: Irccd.Directory.find(path, pattern, recursive) + * -------------------------------------------------------- + * + * Find an entry by a pattern or a regular expression. + * + * Arguments: + * - path, the base path, + * - pattern, the regular expression or file name, + * - recursive, set to true to search recursively (default: false). + * Returns: + * The path to the file or undefined on errors or not found. + * Throws: + * - Irccd.SystemError on errors + */ +auto Directory_find(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return find(ctx, duk::require<std::string>(ctx, 0), duk::get<bool>(ctx, 2), 1); + }); +} + +// }}} + +// {{{ Irccd.Directory.remove + +/* + * Function: Irccd.Directory.remove(path, recursive) + * -------------------------------------------------------- + * + * Remove the directory optionally recursively. + * + * Arguments: + * - path, the path to the directory, + * - recursive, recursively or not (default: false). + * Throws: + * - Irccd.SystemError on errors + */ +auto Directory_remove(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return remove(duk::require<std::string>(ctx, 0), duk::get<bool>(ctx, 1)); + }); +} + +// }}} + +// {{{ Irccd.Directory.mkdir + +/* + * Function: Irccd.Directory.mkdir(path, mode = 0700) + * -------------------------------------------------------- + * + * Create a directory specified by path. It will create needed subdirectories + * just like you have invoked mkdir -p. + * + * Arguments: + * - path, the path to the directory, + * Throws: + * - Irccd.SystemError on errors + */ +auto Directory_mkdir(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + boost::filesystem::create_directories(duk::require<std::string>(ctx, 0)); + + return 0; + }); +} + +// }}} + +// {{{ definitions + +const duk_function_list_entry methods[] = { + { "find", Directory_prototype_find, DUK_VARARGS }, + { "remove", Directory_prototype_remove, 1 }, + { nullptr, nullptr, 0 } +}; + +const duk_function_list_entry functions[] = { + { "find", Directory_find, DUK_VARARGS }, + { "mkdir", Directory_mkdir, DUK_VARARGS }, + { "remove", Directory_remove, DUK_VARARGS }, + { nullptr, nullptr, 0 } +}; + +const duk_number_list_entry constants[] = { + { "TypeFile", static_cast<int>(fs::regular_file) }, + { "TypeDir", static_cast<int>(fs::directory_file) }, + { "TypeLink", static_cast<int>(fs::symlink_file) }, + { "TypeBlock", static_cast<int>(fs::block_file) }, + { "TypeCharacter", static_cast<int>(fs::character_file) }, + { "TypeFifo", static_cast<int>(fs::fifo_file) }, + { "TypeSocket", static_cast<int>(fs::socket_file) }, + { "TypeUnknown", static_cast<int>(fs::type_unknown) }, + { nullptr, 0 } +}; + +// }}} + +} // !namespace + +// {{{ directory_api + +auto directory_api::get_name() const noexcept -> std::string_view +{ + return "Irccd.Directory"; +} + +void directory_api::load(daemon::bot&, std::shared_ptr<plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + duk_get_global_string(plugin->get_context(), "Irccd"); + duk_push_c_function(plugin->get_context(), Directory_constructor, 2); + duk_put_number_list(plugin->get_context(), -1, constants); + duk_put_function_list(plugin->get_context(), -1, functions); + +#if BOOST_OS_WINDOWS + duk_push_string(plugin->get_context(), "\\"); +#else + duk_push_string(plugin->get_context(), "/"); +#endif + + duk_put_prop_string(plugin->get_context(), -2, "separator"); + + duk_push_object(plugin->get_context()); + duk_put_function_list(plugin->get_context(), -1, methods); + duk_put_prop_string(plugin->get_context(), -2, "prototype"); + duk_put_prop_string(plugin->get_context(), -2, "Directory"); + duk_pop(plugin->get_context()); +} + +// }}} + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/directory_api.hpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,50 @@ +/* + * directory_api.hpp -- Irccd.Directory API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_DIRECTORY_API_HPP +#define IRCCD_JS_DIRECTORY_API_HPP + +/** + * \file directory_api.hpp + * \brief Irccd.Directory Javascript API. + */ + +#include "api.hpp" + +namespace irccd::js { + +/** + * \brief Irccd.Directory Javascript API. + * \ingroup js-api + */ +class directory_api : public api { +public: + /** + * \copydoc api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc api::load + */ + void load(daemon::bot& bot, std::shared_ptr<plugin> plugin) override; +}; + +} // !irccd::js + +#endif // !IRCCD_JS_DIRECTORY_API_HPP
--- a/libirccd-js/irccd/js/directory_js_api.cpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,406 +0,0 @@ -/* - * directory_js_api.cpp -- Irccd.Directory API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <boost/predef/os.h> - -#include <irccd/sysconfig.hpp> - -#include <cerrno> -#include <cstdio> -#include <cstring> -#include <fstream> -#include <regex> -#include <stdexcept> -#include <string> - -#include <irccd/fs_util.hpp> - -#include "directory_js_api.hpp" -#include "irccd_js_api.hpp" -#include "js_plugin.hpp" - -namespace fs = boost::filesystem; - -namespace irccd::js { - -namespace { - -// {{{ wrap - -/* - * Wrap the function and raise appropriate error in case of need. - */ -template <typename Handler> -auto wrap(duk_context* ctx, Handler handler) -> duk_ret_t -{ - try { - return handler(); - } catch (const boost::system::system_error& ex) { - duk::raise(ctx, ex); - } catch (const std::system_error& ex) { - duk::raise(ctx, ex); - } catch (const std::exception& ex) { - duk::raise(ctx, ex); - } - - return 0; -} - -// }}} - -// {{{ path - -/* - * Get the path associated with the directory object as this. - */ -auto path(duk_context* ctx) -> std::string -{ - duk_push_this(ctx); - duk_get_prop_string(ctx, -1, "path"); - - if (duk_get_type(ctx, -1) != DUK_TYPE_STRING) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Directory object"); - - const auto ret = duk::get<std::string>(ctx, -1); - - if (ret.empty()) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "directory object has empty path"); - - duk_pop_n(ctx, 2); - - return ret; -} - -// }}} - -// {{{ find - -/* - * Generic find function for: - * - * - Irccd.Directory.find - * - Irccd.Directory.prototype.find - * - * The pattern_index is the argument where to test if the argument is a regex or - * a string. - */ -auto find(duk_context* ctx, std::string base, bool recursive, int pattern_index) -> duk_ret_t -{ - /* - * Helper for checking if it's a valid RegExp object. - */ - const auto is_regex = [&] { - duk_get_global_string(ctx, "RegExp"); - const auto result = duk_instanceof(ctx, pattern_index, -1); - duk_pop(ctx); - - return result; - }; - - /* - * Helper for getting regex source. - */ - const auto pattern = [&] { - duk_get_prop_string(ctx, pattern_index, "source"); - const auto pattern = duk_to_string(ctx, -1); - duk_pop(ctx); - - return pattern; - }; - - std::string path; - - if (duk_is_string(ctx, pattern_index)) - path = fs_util::find(base, duk::get<std::string>(ctx, pattern_index), recursive); - else if (is_regex()) - path = fs_util::find(base, pattern(), recursive); - else - throw duk::type_error("pattern must be a string or a regex expression"); - - if (path.empty()) - return 0; - - return duk::push(ctx, path); -} - -// }}} - -// {{{ remove - -/* - * Generic remove function for: - * - * - Irccd.Directory.remove - * - Irccd.Directory.prototype.remove - */ -auto remove(const std::string& path, bool recursive) -> duk_ret_t -{ - if (!boost::filesystem::is_directory(path)) - throw std::system_error(make_error_code(std::errc::invalid_argument)); - - if (!recursive) - boost::filesystem::remove(path); - else - boost::filesystem::remove_all(path); - - return 0; -} - -// }}} - -// {{{ Irccd.Directory.prototype.find - -/* - * Method: Irccd.Directory.prototype.find(pattern, recursive) - * -------------------------------------------------------- - * - * Synonym of Irccd.Directory.find(path, pattern, recursive) but the path is - * taken from the directory object. - * - * Arguments: - * - pattern, the regular expression or file name, - * - recursive, set to true to search recursively (default: false). - * Returns: - * The path to the file or undefined if not found. - * Throws: - * - Irccd.SystemError on errors - */ -auto Directory_prototype_find(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - return find(ctx, path(ctx), duk::get<bool>(ctx, 1), 0); - }); -} - -// }}} - -// {{{ Irccd.Directory.prototype.remove - -/* - * Method: Irccd.Directory.prototype.remove(recursive) - * -------------------------------------------------------- - * - * Synonym of Directory.remove(recursive) but the path is taken from the - * directory object. - * - * Arguments: - * - recursive, recursively or not (default: false). - * Throws: - * - Irccd.SystemError on errors - */ -auto Directory_prototype_remove(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - return remove(path(ctx), duk::get<bool>(ctx, 0)); - }); -} - -// }}} - -// {{{ Irccd.Directory [constructor] - -/* - * Function: Irccd.Directory(path) [constructor] - * -------------------------------------------------------- - * - * Opens and read the directory at the specified path. - * - * Arguments: - * - path, the path to the directory, - * Throws: - * - Irccd.SystemError on errors - */ -auto Directory_constructor(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - if (!duk_is_constructor_call(ctx)) - return 0; - - const auto path = duk::require<std::string>(ctx, 0); - - if (!boost::filesystem::is_directory(path)) - throw std::system_error(make_error_code(std::errc::invalid_argument)); - - duk_push_this(ctx); - - // 'entries' property. - duk_push_string(ctx, "entries"); - duk_push_array(ctx); - - unsigned i = 0; - for (const auto& entry : boost::filesystem::directory_iterator(path)) { - duk_push_object(ctx); - duk::push(ctx, entry.path().filename().string()); - duk_put_prop_string(ctx, -2, "name"); - duk_push_int(ctx, entry.status().type()); - duk_put_prop_string(ctx, -2, "type"); - duk_put_prop_index(ctx, -2, i++); - } - - duk_def_prop(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); - - // 'path' property. - duk::push(ctx, "path"); - duk::push(ctx, path); - duk_def_prop(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Directory.find - -/* - * Function: Irccd.Directory.find(path, pattern, recursive) - * -------------------------------------------------------- - * - * Find an entry by a pattern or a regular expression. - * - * Arguments: - * - path, the base path, - * - pattern, the regular expression or file name, - * - recursive, set to true to search recursively (default: false). - * Returns: - * The path to the file or undefined on errors or not found. - * Throws: - * - Irccd.SystemError on errors - */ -auto Directory_find(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - return find(ctx, duk::require<std::string>(ctx, 0), duk::get<bool>(ctx, 2), 1); - }); -} - -// }}} - -// {{{ Irccd.Directory.remove - -/* - * Function: Irccd.Directory.remove(path, recursive) - * -------------------------------------------------------- - * - * Remove the directory optionally recursively. - * - * Arguments: - * - path, the path to the directory, - * - recursive, recursively or not (default: false). - * Throws: - * - Irccd.SystemError on errors - */ -auto Directory_remove(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - return remove(duk::require<std::string>(ctx, 0), duk::get<bool>(ctx, 1)); - }); -} - -// }}} - -// {{{ Irccd.Directory.mkdir - -/* - * Function: Irccd.Directory.mkdir(path, mode = 0700) - * -------------------------------------------------------- - * - * Create a directory specified by path. It will create needed subdirectories - * just like you have invoked mkdir -p. - * - * Arguments: - * - path, the path to the directory, - * Throws: - * - Irccd.SystemError on errors - */ -auto Directory_mkdir(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - boost::filesystem::create_directories(duk::require<std::string>(ctx, 0)); - - return 0; - }); -} - -// }}} - -// {{{ definitions - -const duk_function_list_entry methods[] = { - { "find", Directory_prototype_find, DUK_VARARGS }, - { "remove", Directory_prototype_remove, 1 }, - { nullptr, nullptr, 0 } -}; - -const duk_function_list_entry functions[] = { - { "find", Directory_find, DUK_VARARGS }, - { "mkdir", Directory_mkdir, DUK_VARARGS }, - { "remove", Directory_remove, DUK_VARARGS }, - { nullptr, nullptr, 0 } -}; - -const duk_number_list_entry constants[] = { - { "TypeFile", static_cast<int>(fs::regular_file) }, - { "TypeDir", static_cast<int>(fs::directory_file) }, - { "TypeLink", static_cast<int>(fs::symlink_file) }, - { "TypeBlock", static_cast<int>(fs::block_file) }, - { "TypeCharacter", static_cast<int>(fs::character_file) }, - { "TypeFifo", static_cast<int>(fs::fifo_file) }, - { "TypeSocket", static_cast<int>(fs::socket_file) }, - { "TypeUnknown", static_cast<int>(fs::type_unknown) }, - { nullptr, 0 } -}; - -// }}} - -} // !namespace - -// {{{ directory_js_api - -auto directory_js_api::get_name() const noexcept -> std::string_view -{ - return "Irccd.Directory"; -} - -void directory_js_api::load(daemon::bot&, std::shared_ptr<js_plugin> plugin) -{ - duk::stack_guard sa(plugin->get_context()); - - duk_get_global_string(plugin->get_context(), "Irccd"); - duk_push_c_function(plugin->get_context(), Directory_constructor, 2); - duk_put_number_list(plugin->get_context(), -1, constants); - duk_put_function_list(plugin->get_context(), -1, functions); - -#if BOOST_OS_WINDOWS - duk_push_string(plugin->get_context(), "\\"); -#else - duk_push_string(plugin->get_context(), "/"); -#endif - - duk_put_prop_string(plugin->get_context(), -2, "separator"); - - duk_push_object(plugin->get_context()); - duk_put_function_list(plugin->get_context(), -1, methods); - duk_put_prop_string(plugin->get_context(), -2, "prototype"); - duk_put_prop_string(plugin->get_context(), -2, "Directory"); - duk_pop(plugin->get_context()); -} - -// }}} - -} // !irccd::js
--- a/libirccd-js/irccd/js/directory_js_api.hpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * directory_js_api.hpp -- Irccd.Directory API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_DIRECTORY_JS_API_HPP -#define IRCCD_JS_DIRECTORY_JS_API_HPP - -/** - * \file directory_js_api.hpp - * \brief Irccd.Directory Javascript API. - */ - -#include "js_api.hpp" - -namespace irccd::js { - -/** - * \brief Irccd.Directory Javascript API. - * \ingroup js-api - */ -class directory_js_api : public js_api { -public: - /** - * \copydoc js_api::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc js_api::load - */ - void load(daemon::bot& bot, std::shared_ptr<js_plugin> plugin) override; -}; - -} // !irccd::js - -#endif // !IRCCD_JS_DIRECTORY_JS_API_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/elapsed_timer_api.cpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,182 @@ +/* + * elapsed_timer_api.cpp -- Irccd.ElapsedTimer API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <boost/timer/timer.hpp> + +#include "elapsed_timer_api.hpp" +#include "plugin.hpp" + +using irccd::daemon::bot; + +namespace irccd::js { + +namespace { + +const std::string_view signature(DUK_HIDDEN_SYMBOL("Irccd.ElapsedTimer")); + +// {{{ self + +auto self(duk_context* ctx) -> boost::timer::cpu_timer* +{ + duk::stack_guard sa(ctx); + + duk_push_this(ctx); + duk_get_prop_string(ctx, -1, signature.data()); + const auto ptr = static_cast<boost::timer::cpu_timer*>(duk_to_pointer(ctx, -1)); + duk_pop_2(ctx); + + if (!ptr) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "not an ElapsedTimer object"); + + return ptr; +} + +// }}} + +// {{{ Irccd.ElapsedTimer.prototype.pause + +/* + * Method: ElapsedTimer.prototype.pause + * ------------------------------------------------------------------ + * + * Pause the timer, without resetting the current elapsed time stored. + */ +auto ElapsedTimer_prototype_pause(duk_context* ctx) -> duk_ret_t +{ + self(ctx)->stop(); + + return 0; +} + +// }}} + +// {{{ Irccd.ElapsedTimer.prototype.restart + +/* + * Method: Irccd.ElapsedTimer.prototype.restart + * ------------------------------------------------------------------ + * + * Restart the timer without resetting the current elapsed time. + */ +auto ElapsedTimer_prototype_restart(duk_context* ctx) -> duk_ret_t +{ + self(ctx)->resume(); + + return 0; +} + +// }}} + +// {{{ Irccd.ElapsedTimer.prototype.elapsed + +/* + * Method: ElapsedTimer.prototype.elapsed + * ------------------------------------------------------------------ + * + * Get the number of elapsed milliseconds. + * + * Returns: + * The time elapsed. + */ +auto ElapsedTimer_prototype_elapsed(duk_context* ctx) -> duk_ret_t +{ + duk_push_uint(ctx, self(ctx)->elapsed().wall / 1000000LL); + + return 1; +} + +// }}} + +// {{{ Irccd.ElapsedTimer [constructor] + +/* + * Function: Irccd.ElapsedTimer [constructor] + * ------------------------------------------------------------------ + * + * Construct a new ElapsedTimer object. + */ +auto ElapsedTimer_constructor(duk_context* ctx) -> duk_ret_t +{ + duk_push_this(ctx); + duk_push_pointer(ctx, new boost::timer::cpu_timer); + duk_put_prop_string(ctx, -2, signature.data()); + duk_pop(ctx); + + return 0; +} + +// }}} + +// {{{ Irccd.ElapsedTimer [destructor] + +/* + * Function: Irccd.ElapsedTimer [destructor] + * ------------------------------------------------------------------ + * + * Delete the property. + */ +auto ElapsedTimer_destructor(duk_context* ctx) -> duk_ret_t +{ + duk_get_prop_string(ctx, 0, signature.data()); + delete static_cast<boost::timer::cpu_timer*>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + duk_del_prop_string(ctx, 0, signature.data()); + + return 0; +} + +// }}} + +// {{{ definitions + +const duk_function_list_entry methods[] = { + { "elapsed", ElapsedTimer_prototype_elapsed, 0 }, + { "pause", ElapsedTimer_prototype_pause, 0 }, + { "restart", ElapsedTimer_prototype_restart, 0 }, + { nullptr, nullptr, 0 } +}; + +// }}} + +} // !namespace + +// {{{ elapsed_timer_api + +auto elapsed_timer_api::get_name() const noexcept -> std::string_view +{ + return "Irccd.ElapsedTimer"; +} + +void elapsed_timer_api::load(bot&, std::shared_ptr<plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + duk_get_global_string(plugin->get_context(), "Irccd"); + duk_push_c_function(plugin->get_context(), ElapsedTimer_constructor, 0); + duk_push_object(plugin->get_context()); + duk_put_function_list(plugin->get_context(), -1, methods); + duk_push_c_function(plugin->get_context(), ElapsedTimer_destructor, 1); + duk_set_finalizer(plugin->get_context(), -2); + duk_put_prop_string(plugin->get_context(), -2, "prototype"); + duk_put_prop_string(plugin->get_context(), -2, "ElapsedTimer"); + duk_pop(plugin->get_context()); +} + +// }}} + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/elapsed_timer_api.hpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,50 @@ +/* + * elapsed_timer_api.hpp -- Irccd.ElapsedTimer API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_ELAPSED_TIMER_API_HPP +#define IRCCD_JS_ELAPSED_TIMER_API_HPP + +/** + * \file elapsed_timer_api.hpp + * \brief Irccd.ElapsedTimer Javascript API. + */ + +#include "api.hpp" + +namespace irccd::js { + +/** + * \ingroup js-api + * \brief Irccd.ElapsedTimer Javascript API. + */ +class elapsed_timer_api : public api { +public: + /** + * \copydoc api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc api::load + */ + void load(daemon::bot& bot, std::shared_ptr<plugin> plugin) override; +}; + +} // !irccd::js + +#endif // !IRCCD_JS_ELAPSED_TIMER_API_HPP
--- a/libirccd-js/irccd/js/elapsed_timer_js_api.cpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,182 +0,0 @@ -/* - * elapsed_timer_js_api.cpp -- Irccd.ElapsedTimer API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <boost/timer/timer.hpp> - -#include "elapsed_timer_js_api.hpp" -#include "js_plugin.hpp" - -using irccd::daemon::bot; - -namespace irccd::js { - -namespace { - -const std::string_view signature(DUK_HIDDEN_SYMBOL("Irccd.ElapsedTimer")); - -// {{{ self - -auto self(duk_context* ctx) -> boost::timer::cpu_timer* -{ - duk::stack_guard sa(ctx); - - duk_push_this(ctx); - duk_get_prop_string(ctx, -1, signature.data()); - const auto ptr = static_cast<boost::timer::cpu_timer*>(duk_to_pointer(ctx, -1)); - duk_pop_2(ctx); - - if (!ptr) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "not an ElapsedTimer object"); - - return ptr; -} - -// }}} - -// {{{ Irccd.ElapsedTimer.prototype.pause - -/* - * Method: ElapsedTimer.prototype.pause - * ------------------------------------------------------------------ - * - * Pause the timer, without resetting the current elapsed time stored. - */ -auto ElapsedTimer_prototype_pause(duk_context* ctx) -> duk_ret_t -{ - self(ctx)->stop(); - - return 0; -} - -// }}} - -// {{{ Irccd.ElapsedTimer.prototype.restart - -/* - * Method: Irccd.ElapsedTimer.prototype.restart - * ------------------------------------------------------------------ - * - * Restart the timer without resetting the current elapsed time. - */ -auto ElapsedTimer_prototype_restart(duk_context* ctx) -> duk_ret_t -{ - self(ctx)->resume(); - - return 0; -} - -// }}} - -// {{{ Irccd.ElapsedTimer.prototype.elapsed - -/* - * Method: ElapsedTimer.prototype.elapsed - * ------------------------------------------------------------------ - * - * Get the number of elapsed milliseconds. - * - * Returns: - * The time elapsed. - */ -auto ElapsedTimer_prototype_elapsed(duk_context* ctx) -> duk_ret_t -{ - duk_push_uint(ctx, self(ctx)->elapsed().wall / 1000000LL); - - return 1; -} - -// }}} - -// {{{ Irccd.ElapsedTimer [constructor] - -/* - * Function: Irccd.ElapsedTimer [constructor] - * ------------------------------------------------------------------ - * - * Construct a new ElapsedTimer object. - */ -auto ElapsedTimer_constructor(duk_context* ctx) -> duk_ret_t -{ - duk_push_this(ctx); - duk_push_pointer(ctx, new boost::timer::cpu_timer); - duk_put_prop_string(ctx, -2, signature.data()); - duk_pop(ctx); - - return 0; -} - -// }}} - -// {{{ Irccd.ElapsedTimer [destructor] - -/* - * Function: Irccd.ElapsedTimer [destructor] - * ------------------------------------------------------------------ - * - * Delete the property. - */ -auto ElapsedTimer_destructor(duk_context* ctx) -> duk_ret_t -{ - duk_get_prop_string(ctx, 0, signature.data()); - delete static_cast<boost::timer::cpu_timer*>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - duk_del_prop_string(ctx, 0, signature.data()); - - return 0; -} - -// }}} - -// {{{ definitions - -const duk_function_list_entry methods[] = { - { "elapsed", ElapsedTimer_prototype_elapsed, 0 }, - { "pause", ElapsedTimer_prototype_pause, 0 }, - { "restart", ElapsedTimer_prototype_restart, 0 }, - { nullptr, nullptr, 0 } -}; - -// }}} - -} // !namespace - -// {{{ elapsed_timer_js_api - -auto elapsed_timer_js_api::get_name() const noexcept -> std::string_view -{ - return "Irccd.ElapsedTimer"; -} - -void elapsed_timer_js_api::load(bot&, std::shared_ptr<js_plugin> plugin) -{ - duk::stack_guard sa(plugin->get_context()); - - duk_get_global_string(plugin->get_context(), "Irccd"); - duk_push_c_function(plugin->get_context(), ElapsedTimer_constructor, 0); - duk_push_object(plugin->get_context()); - duk_put_function_list(plugin->get_context(), -1, methods); - duk_push_c_function(plugin->get_context(), ElapsedTimer_destructor, 1); - duk_set_finalizer(plugin->get_context(), -2); - duk_put_prop_string(plugin->get_context(), -2, "prototype"); - duk_put_prop_string(plugin->get_context(), -2, "ElapsedTimer"); - duk_pop(plugin->get_context()); -} - -// }}} - -} // !irccd::js
--- a/libirccd-js/irccd/js/elapsed_timer_js_api.hpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * elapsed_timer_js_api.hpp -- Irccd.ElapsedTimer API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_ELAPSED_TIMER_JS_API_HPP -#define IRCCD_JS_ELAPSED_TIMER_JS_API_HPP - -/** - * \file elapsed_timer_js_api.hpp - * \brief Irccd.ElapsedTimer Javascript API. - */ - -#include "js_api.hpp" - -namespace irccd::js { - -/** - * \ingroup js-api - * \brief Irccd.ElapsedTimer Javascript API. - */ -class elapsed_timer_js_api : public js_api { -public: - /** - * \copydoc js_api::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc js_api::load - */ - void load(daemon::bot& bot, std::shared_ptr<js_plugin> plugin) override; -}; - -} // !irccd::js - -#endif // !IRCCD_JS_ELAPSED_TIMER_JS_API_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/file_api.cpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,807 @@ +/* + * file_api.cpp -- Irccd.File API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <algorithm> +#include <array> +#include <cassert> +#include <iterator> +#include <vector> + +#include <boost/filesystem.hpp> + +#include <irccd/fs_util.hpp> + +#include "file_api.hpp" +#include "irccd_api.hpp" +#include "plugin.hpp" + +using irccd::daemon::bot; + +namespace irccd::js { + +namespace { + +const std::string_view signature(DUK_HIDDEN_SYMBOL("Irccd.File")); +const std::string_view prototype(DUK_HIDDEN_SYMBOL("Irccd.File.prototype")); + +// {{{ clear_crlf + +auto clear_crlf(std::string input) noexcept -> std::string +{ + if (input.length() > 0 && input.back() == '\r') + input.pop_back(); + + return input; +} + +// }}} + +// {{{ from_errno + +auto from_errno() noexcept -> std::system_error +{ + return std::system_error(make_error_code(static_cast<std::errc>(errno))); +} + +// }}} + +// {{{ self + +auto self(duk_context* ctx) -> std::shared_ptr<file> +{ + duk::stack_guard sa(ctx); + + duk_push_this(ctx); + duk_get_prop_string(ctx, -1, signature.data()); + auto ptr = static_cast<std::shared_ptr<file>*>(duk_to_pointer(ctx, -1)); + duk_pop_2(ctx); + + if (!ptr) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a File object"); + + return *ptr; +} + +// }}} + +// {{{ wrap + +template <typename Handler> +auto wrap(duk_context* ctx, Handler handler) -> duk_ret_t +{ + try { + return handler(); + } catch (const boost::system::system_error& ex) { + duk::raise(ctx, ex); + } catch (const std::system_error& ex) { + duk::raise(ctx, ex); + } catch (const std::exception& ex) { + duk::raise(ctx, ex); + } + + return 0; +} + +// }}} + +// {{{ Irccd.File.prototype.basename + +/* + * Method: Irccd.File.prototype.basename() + * -------------------------------------------------------- + * + * Synonym of `Irccd.File.basename(path)` but with the path from the file. + * + * Returns: + * The base name. + */ +auto File_prototype_basename(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, fs_util::base_name(self(ctx)->get_path())); + }); +} + +// }}} + +// {{{ Irccd.File.prototype.close + +/* + * Method: Irccd.File.prototype.close() + * -------------------------------------------------------- + * + * Force close of the file, automatically called when object is collected. + */ +auto File_prototype_close(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + self(ctx)->close(); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.File.prototype.dirname + +/* + * Method: Irccd.File.prototype.dirname() + * -------------------------------------------------------- + * + * Synonym of `Irccd.File.dirname(path)` but with the path from the file. + * + * Returns: + * The directory name. + */ +auto File_prototype_dirname(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, fs_util::dir_name(self(ctx)->get_path())); + }); +} + +// }}} + +// {{{ Irccd.File.prototype.lines + +/* + * Method: Irccd.File.prototype.lines() + * -------------------------------------------------------- + * + * Read all lines and return an array. + * + * Returns: + * An array with all lines. + * Throws + * - Any exception on error. + */ +auto File_prototype_lines(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + duk_push_array(ctx); + + std::FILE* fp = self(ctx)->get_handle(); + std::string buffer; + std::array<char, 128> data; + std::int32_t i = 0; + + while (std::fgets(&data[0], data.size(), fp) != nullptr) { + buffer += data.data(); + + const auto pos = buffer.find('\n'); + + if (pos != std::string::npos) { + duk::push(ctx, clear_crlf(buffer.substr(0, pos))); + duk_put_prop_index(ctx, -2, i++); + + buffer.erase(0, pos + 1); + } + } + + // Maybe an error in the stream. + if (std::ferror(fp)) + throw from_errno(); + + // Missing '\n' in end of file. + if (!buffer.empty()) { + duk::push(ctx, clear_crlf(buffer)); + duk_put_prop_index(ctx, -2, i++); + } + + return 1; + }); +} + +// }}} + +// {{{ Irccd.File.prototype.read + +/* + * Method: Irccd.File.prototype.read(amount) + * -------------------------------------------------------- + * + * Read the specified amount of characters or the whole file. + * + * Arguments: + * - amount, the amount of characters or -1 to read all (Optional, default: -1). + * Returns: + * The string. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_prototype_read(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + const auto fp = self(ctx)->get_handle(); + const auto amount = duk_is_number(ctx, 0) ? duk_get_int(ctx, 0) : -1; + + if (amount == 0 || !fp) + return 0; + + std::string data; + std::size_t total = 0; + + if (amount < 0) { + std::array<char, 128> buffer; + std::size_t nread; + + while ((nread = std::fread(&buffer[0], sizeof (buffer[0]), buffer.size(), fp)) > 0) { + if (std::ferror(fp)) + throw from_errno(); + + std::copy(buffer.begin(), buffer.begin() + nread, std::back_inserter(data)); + total += nread; + } + } else { + data.resize(static_cast<std::size_t>(amount)); + total = std::fread(&data[0], sizeof (data[0]), static_cast<std::size_t>(amount), fp); + + if (std::ferror(fp)) + throw from_errno(); + + data.resize(total); + } + + return duk::push(ctx, data); + }); +} + +// }}} + +// {{{ Irccd.File.prototype.readline + +/* + * Method: Irccd.File.prototype.readline() + * -------------------------------------------------------- + * + * Read the next line available. + * + * Returns: + * The next line or undefined if eof. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_prototype_readline(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + auto fp = self(ctx)->get_handle(); + + if (fp == nullptr || std::feof(fp)) + return 0; + + std::string result; + + for (int ch; (ch = std::fgetc(fp)) != EOF && ch != '\n'; ) + result += (char)ch; + if (std::ferror(fp)) + throw from_errno(); + + return duk::push(ctx, clear_crlf(result)); + }); +} + +// }}} + +// {{{ Irccd.File.prototype.remove + +/* + * Method: Irccd.File.prototype.remove() + * -------------------------------------------------------- + * + * Synonym of Irccd.File.prototype.remove(path) but with the path from the file. + * + * Throws: + * - Irccd.SystemError on errors + */ +auto File_prototype_remove(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + boost::filesystem::remove(self(ctx)->get_path()); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.File.prototype.seek + +/* + * Method: Irccd.File.prototype.seek(type, amount) + * -------------------------------------------------------- + * + * Sets the position in the file. + * + * Arguments: + * - type, the type of setting (File.SeekSet, File.SeekCur, File.SeekSet), + * - amount, the new offset. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_prototype_seek(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + auto fp = self(ctx)->get_handle(); + auto type = duk_require_int(ctx, 0); + auto amount = duk_require_int(ctx, 1); + + if (fp != nullptr && std::fseek(fp, amount, type) != 0) + throw from_errno(); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.File.prototype.stat + +#if defined(IRCCD_HAVE_STAT) + +/* + * Method: Irccd.File.prototype.stat() [optional] + * -------------------------------------------------------- + * + * Synonym of File.stat(path) but with the path from the file. + * + * Returns: + * The stat information. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_prototype_stat(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + auto file = self(ctx); + struct stat st; + + if (file->get_handle() == nullptr && ::stat(file->get_path().c_str(), &st) < 0) + throw from_errno(); + + duk::push(ctx, st); + + return 1; + }); +} + +#endif // !IRCCD_HAVE_STAT + +// }}} + +// {{{ Irccd.File.prototype.tell + +/* + * Method: Irccd.File.prototype.tell() + * -------------------------------------------------------- + * + * Get the actual position in the file. + * + * Returns: + * The position. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_prototype_tell(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + auto fp = self(ctx)->get_handle(); + long pos; + + if (fp == nullptr) + return 0; + + if ((pos = std::ftell(fp)) == -1L) + throw from_errno(); + + duk_push_int(ctx, pos); + + return 1; + }); +} + +// }}} + +// {{{ Irccd.File.prototype.write + +/* + * Method: Irccd.File.prototype.write(data) + * -------------------------------------------------------- + * + * Write some characters to the file. + * + * Arguments: + * - data, the character to write. + * Returns: + * The number of bytes written. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_prototype_write(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + auto fp = self(ctx)->get_handle(); + auto data = duk::require<std::string>(ctx, 0); + + if (fp == nullptr) + return 0; + + const auto nwritten = std::fwrite(data.c_str(), 1, data.length(), fp); + + if (std::ferror(fp)) + throw from_errno(); + + duk_push_uint(ctx, nwritten); + + return 1; + }); +} + +// }}} + +// {{{ Irccd.File [constructor] + +/* + * Function: Irccd.File(path, mode) [constructor] + * -------------------------------------------------------- + * + * Open a file specified by path with the specified mode. + * + * Arguments: + * - path, the path to the file, + * - mode, the mode string. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_constructor(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + if (!duk_is_constructor_call(ctx)) + return 0; + + const auto path = duk::require<std::string>(ctx, 0); + const auto mode = duk::require<std::string>(ctx, 1); + + duk_push_this(ctx); + duk_push_pointer(ctx, new std::shared_ptr<file>(new file(path, mode))); + duk_put_prop_string(ctx, -2, signature.data()); + duk_pop(ctx); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.File [destructor] + +/* + * Function: Irccd.File() [destructor] + * ------------------------------------------------------------------ + * + * Delete the property. + */ +auto File_destructor(duk_context* ctx) -> duk_ret_t +{ + duk_get_prop_string(ctx, 0, signature.data()); + delete static_cast<std::shared_ptr<file>*>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + duk_del_prop_string(ctx, 0, signature.data()); + + return 0; +} + +// }}} + +// {{{ Irccd.File.basename + +/* + * Function: Irccd.File.basename(path) + * -------------------------------------------------------- + * + * duk_ret_turn the file basename as specified in `basename(3)` C function. + * + * Arguments: + * - path, the path to the file. + * Returns: + * The base name. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_basename(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, fs_util::base_name(duk_require_string(ctx, 0))); + }); +} + +// }}} + +// {{{ Irccd.File.dirname + +/* + * Function: Irccd.File.dirname(path) + * -------------------------------------------------------- + * + * duk_ret_turn the file directory name as specified in `dirname(3)` C function. + * + * Arguments: + * - path, the path to the file. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_dirname(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, fs_util::dir_name(duk_require_string(ctx, 0))); + }); +} + +// }}} + +// {{{ Irccd.File.exists + +/* + * Function: Irccd.File.exists(path) + * -------------------------------------------------------- + * + * Check if the file exists. + * + * Arguments: + * - path, the path to the file. + * Returns: + * True if exists. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_exists(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, boost::filesystem::exists(duk_require_string(ctx, 0))); + }); +} + +// }}} + +// {{{ Irccd.File.remove + +/* + * Function Irccd.File.remove(path) + * -------------------------------------------------------- + * + * Remove the file at the specified path. + * + * Arguments: + * - path, the path to the file. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_remove(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + boost::filesystem::remove(duk::require<std::string>(ctx, 0)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.File.stat + +#if defined(IRCCD_HAVE_STAT) + +/* + * Function Irccd.File.stat(path) [optional] + * -------------------------------------------------------- + * + * Get file information at the specified path. + * + * Arguments: + * - path, the path to the file. + * Returns: + * The stat information. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_stat(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + struct stat st; + + if (::stat(duk_require_string(ctx, 0), &st) < 0) + throw from_errno(); + + return duk::push(ctx, st); + }); +} + +#endif // !IRCCD_HAVE_STAT + +// }}} + +// {{{ definitions + +const duk_function_list_entry methods[] = { + { "basename", File_prototype_basename, 0 }, + { "close", File_prototype_close, 0 }, + { "dirname", File_prototype_dirname, 0 }, + { "lines", File_prototype_lines, 0 }, + { "read", File_prototype_read, 1 }, + { "readline", File_prototype_readline, 0 }, + { "remove", File_prototype_remove, 0 }, + { "seek", File_prototype_seek, 2 }, +#if defined(IRCCD_HAVE_STAT) + { "stat", File_prototype_stat, 0 }, +#endif + { "tell", File_prototype_tell, 0 }, + { "write", File_prototype_write, 1 }, + { nullptr, nullptr, 0 } +}; + +const duk_function_list_entry functions[] = { + { "basename", File_basename, 1 }, + { "dirname", File_dirname, 1 }, + { "exists", File_exists, 1 }, + { "remove", File_remove, 1 }, +#if defined(IRCCD_HAVE_STAT) + { "stat", File_stat, 1 }, +#endif + { nullptr, nullptr, 0 } +}; + +const duk_number_list_entry constants[] = { + { "SeekCur", SEEK_CUR }, + { "SeekEnd", SEEK_END }, + { "SeekSet", SEEK_SET }, + { nullptr, 0 } +}; + +// }}} + +} // !namespace + +// {{{ file_api + +auto file_api::get_name() const noexcept -> std::string_view +{ + return "Irccd.File"; +} + +void file_api::load(bot&, std::shared_ptr<plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + duk_get_global_string(plugin->get_context(), "Irccd"); + duk_push_c_function(plugin->get_context(), File_constructor, 2); + duk_put_number_list(plugin->get_context(), -1, constants); + duk_put_function_list(plugin->get_context(), -1, functions); + duk_push_object(plugin->get_context()); + duk_put_function_list(plugin->get_context(), -1, methods); + duk_push_c_function(plugin->get_context(), File_destructor, 1); + duk_set_finalizer(plugin->get_context(), -2); + duk_dup(plugin->get_context(), -1); + duk_put_global_string(plugin->get_context(), prototype.data()); + duk_put_prop_string(plugin->get_context(), -2, "prototype"); + duk_put_prop_string(plugin->get_context(), -2, "File"); + duk_pop(plugin->get_context()); +} + +// }}} + +// {{{ duk::type_traits<std::shared_ptr<file>> + +using file_traits = duk::type_traits<std::shared_ptr<file>>; + +void file_traits::push(duk_context* ctx, std::shared_ptr<file> fp) +{ + assert(ctx); + assert(fp); + + duk::stack_guard sa(ctx, 1); + + duk_push_object(ctx); + duk_push_pointer(ctx, new std::shared_ptr<file>(std::move(fp))); + duk_put_prop_string(ctx, -2, signature.data()); + duk_get_global_string(ctx, prototype.data()); + duk_set_prototype(ctx, -2); +} + +auto file_traits::require(duk_context* ctx, duk_idx_t index) -> std::shared_ptr<file> +{ + if (!duk_is_object(ctx, index) || !duk_has_prop_string(ctx, index, signature.data())) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a File object"); + + duk_get_prop_string(ctx, index, signature.data()); + const auto fp = static_cast<std::shared_ptr<file>*>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + + return *fp; +} + +// }}} + +// {{{ duk::type_traits<struct stat> + +#if defined(IRCCD_HAVE_STAT) + +void duk::type_traits<struct stat>::push(duk_context* ctx, const struct stat& st) +{ + duk::stack_guard sa(ctx, 1); + + duk_push_object(ctx); + +#if defined(IRCCD_HAVE_STAT_ST_ATIME) + duk_push_int(ctx, st.st_atime); + duk_put_prop_string(ctx, -2, "atime"); +#endif +#if defined(IRCCD_HAVE_STAT_ST_BLKSIZE) + duk_push_int(ctx, st.st_blksize); + duk_put_prop_string(ctx, -2, "blksize"); +#endif +#if defined(IRCCD_HAVE_STAT_ST_BLOCKS) + duk_push_int(ctx, st.st_blocks); + duk_put_prop_string(ctx, -2, "blocks"); +#endif +#if defined(IRCCD_HAVE_STAT_ST_CTIME) + duk_push_int(ctx, st.st_ctime); + duk_put_prop_string(ctx, -2, "ctime"); +#endif +#if defined(IRCCD_HAVE_STAT_ST_DEV) + duk_push_int(ctx, st.st_dev); + duk_put_prop_string(ctx, -2, "dev"); +#endif +#if defined(IRCCD_HAVE_STAT_ST_GID) + duk_push_int(ctx, st.st_gid); + duk_put_prop_string(ctx, -2, "gid"); +#endif +#if defined(IRCCD_HAVE_STAT_ST_INO) + duk_push_int(ctx, st.st_ino); + duk_put_prop_string(ctx, -2, "ino"); +#endif +#if defined(IRCCD_HAVE_STAT_ST_MODE) + duk_push_int(ctx, st.st_mode); + duk_put_prop_string(ctx, -2, "mode"); +#endif +#if defined(IRCCD_HAVE_STAT_ST_MTIME) + duk_push_int(ctx, st.st_mtime); + duk_put_prop_string(ctx, -2, "mtime"); +#endif +#if defined(IRCCD_HAVE_STAT_ST_NLINK) + duk_push_int(ctx, st.st_nlink); + duk_put_prop_string(ctx, -2, "nlink"); +#endif +#if defined(IRCCD_HAVE_STAT_ST_RDEV) + duk_push_int(ctx, st.st_rdev); + duk_put_prop_string(ctx, -2, "rdev"); +#endif +#if defined(IRCCD_HAVE_STAT_ST_SIZE) + duk_push_int(ctx, st.st_size); + duk_put_prop_string(ctx, -2, "size"); +#endif +#if defined(IRCCD_HAVE_STAT_ST_UID) + duk_push_int(ctx, st.st_uid); + duk_put_prop_string(ctx, -2, "uid"); +#endif +} + +#endif // !IRCCD_HAVE_STAT + +// }}} + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/file_api.hpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,211 @@ +/* + * file_api.hpp -- Irccd.File API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_FILE_API_HPP +#define IRCCD_JS_FILE_API_HPP + +/** + * \file file_api.hpp + * \brief Irccd.File Javascript API. + */ + +#include <irccd/sysconfig.hpp> + +#if defined(IRCCD_HAVE_STAT) +# include <sys/types.h> +# include <sys/stat.h> +#endif + +#include <cassert> +#include <cerrno> +#include <cstdio> +#include <cstring> +#include <functional> +#include <stdexcept> +#include <string> + +#include "api.hpp" + +namespace irccd::js { + +/** + * \brief Object for Javascript to perform I/O. + * + * This class can be constructed to Javascript. + * + * It is used in: + * + * - Irccd.File [constructor] + * - Irccd.System.popen (optional) + */ +class file { +private: + file(const file&) = delete; + file& operator=(const file&) = delete; + + file(file&&) = delete; + file& operator=(file&&) = delete; + +private: + std::string path_; + std::FILE* stream_; + std::function<void (std::FILE*)> destructor_; + +public: + /** + * Construct a file specified by path + * + * \param path the path + * \param mode the mode string (for std::fopen) + * \throw std::runtime_error on failures + */ + inline file(std::string path, const std::string& mode) + : path_(std::move(path)) + , destructor_([] (std::FILE* fp) { std::fclose(fp); }) + { + if ((stream_ = std::fopen(path_.c_str(), mode.c_str())) == nullptr) + throw std::runtime_error(std::strerror(errno)); + } + + /** + * Construct a file from a already created FILE pointer (e.g. popen). + * + * The class takes ownership of fp and will close it. + * + * \pre destructor must not be null + * \param fp the file pointer + * \param destructor the function to close fp (e.g. std::fclose) + */ + inline file(std::FILE* fp, std::function<void (std::FILE*)> destructor) noexcept + : stream_(fp) + , destructor_(std::move(destructor)) + { + assert(destructor_ != nullptr); + } + + /** + * Closes the file. + */ + virtual ~file() noexcept + { + close(); + } + + /** + * Get the path. + * + * \return the path + * \warning empty when constructed from the FILE constructor + */ + inline const std::string& get_path() const noexcept + { + return path_; + } + + /** + * Get the handle. + * + * \return the handle or nullptr if the stream was closed + */ + inline std::FILE* get_handle() noexcept + { + return stream_; + } + + /** + * Force close, can be safely called multiple times. + */ + inline void close() noexcept + { + if (stream_) { + destructor_(stream_); + stream_ = nullptr; + } + } +}; + +/** + * \ingroup js-api + * \brief Irccd.File Javascript API. + */ +class file_api : public api { +public: + /** + * \copydoc api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc api::load + */ + void load(daemon::bot& bot, std::shared_ptr<plugin> plugin) override; +}; + +namespace duk { + +/** + * \brief Specialization for generic file type as shared_ptr. + * + * Supports push, require. + */ +template <> +struct type_traits<std::shared_ptr<file>> { + /** + * Push a file. + * + * \pre fp != nullptr + * \param ctx the the context + * \param fp the file + */ + static void push(duk_context* ctx, std::shared_ptr<file> fp); + + /** + * Require a file. Raises a JavaScript error if not a File. + * + * \param ctx the context + * \param index the index + * \return the file pointer + */ + static auto require(duk_context* ctx, duk_idx_t index) -> std::shared_ptr<file>; +}; + +#if defined(IRCCD_HAVE_STAT) + +/** + * \brief Specialization for struct stat. + * + * Supports push. + */ +template <> +struct type_traits<struct stat> { + /** + * Push the stat information to the stack as Javascript object. + * + * \param ctx the context + * \param st the stat structure + */ + static void push(duk_context* ctx, const struct stat& st); +}; + +#endif // !IRCCD_HAVE_STAT + +} // !duk + +} // !irccd::js + +#endif // !IRCCD_JS_FILE_API_HPP
--- a/libirccd-js/irccd/js/file_js_api.cpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,807 +0,0 @@ -/* - * file_js_api.cpp -- Irccd.File API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <algorithm> -#include <array> -#include <cassert> -#include <iterator> -#include <vector> - -#include <boost/filesystem.hpp> - -#include <irccd/fs_util.hpp> - -#include "file_js_api.hpp" -#include "irccd_js_api.hpp" -#include "js_plugin.hpp" - -using irccd::daemon::bot; - -namespace irccd::js { - -namespace { - -const std::string_view signature(DUK_HIDDEN_SYMBOL("Irccd.File")); -const std::string_view prototype(DUK_HIDDEN_SYMBOL("Irccd.File.prototype")); - -// {{{ clear_crlf - -auto clear_crlf(std::string input) noexcept -> std::string -{ - if (input.length() > 0 && input.back() == '\r') - input.pop_back(); - - return input; -} - -// }}} - -// {{{ from_errno - -auto from_errno() noexcept -> std::system_error -{ - return std::system_error(make_error_code(static_cast<std::errc>(errno))); -} - -// }}} - -// {{{ self - -auto self(duk_context* ctx) -> std::shared_ptr<file> -{ - duk::stack_guard sa(ctx); - - duk_push_this(ctx); - duk_get_prop_string(ctx, -1, signature.data()); - auto ptr = static_cast<std::shared_ptr<file>*>(duk_to_pointer(ctx, -1)); - duk_pop_2(ctx); - - if (!ptr) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a File object"); - - return *ptr; -} - -// }}} - -// {{{ wrap - -template <typename Handler> -auto wrap(duk_context* ctx, Handler handler) -> duk_ret_t -{ - try { - return handler(); - } catch (const boost::system::system_error& ex) { - duk::raise(ctx, ex); - } catch (const std::system_error& ex) { - duk::raise(ctx, ex); - } catch (const std::exception& ex) { - duk::raise(ctx, ex); - } - - return 0; -} - -// }}} - -// {{{ Irccd.File.prototype.basename - -/* - * Method: Irccd.File.prototype.basename() - * -------------------------------------------------------- - * - * Synonym of `Irccd.File.basename(path)` but with the path from the file. - * - * Returns: - * The base name. - */ -auto File_prototype_basename(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - return duk::push(ctx, fs_util::base_name(self(ctx)->get_path())); - }); -} - -// }}} - -// {{{ Irccd.File.prototype.close - -/* - * Method: Irccd.File.prototype.close() - * -------------------------------------------------------- - * - * Force close of the file, automatically called when object is collected. - */ -auto File_prototype_close(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - self(ctx)->close(); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.File.prototype.dirname - -/* - * Method: Irccd.File.prototype.dirname() - * -------------------------------------------------------- - * - * Synonym of `Irccd.File.dirname(path)` but with the path from the file. - * - * Returns: - * The directory name. - */ -auto File_prototype_dirname(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - return duk::push(ctx, fs_util::dir_name(self(ctx)->get_path())); - }); -} - -// }}} - -// {{{ Irccd.File.prototype.lines - -/* - * Method: Irccd.File.prototype.lines() - * -------------------------------------------------------- - * - * Read all lines and return an array. - * - * Returns: - * An array with all lines. - * Throws - * - Any exception on error. - */ -auto File_prototype_lines(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - duk_push_array(ctx); - - std::FILE* fp = self(ctx)->get_handle(); - std::string buffer; - std::array<char, 128> data; - std::int32_t i = 0; - - while (std::fgets(&data[0], data.size(), fp) != nullptr) { - buffer += data.data(); - - const auto pos = buffer.find('\n'); - - if (pos != std::string::npos) { - duk::push(ctx, clear_crlf(buffer.substr(0, pos))); - duk_put_prop_index(ctx, -2, i++); - - buffer.erase(0, pos + 1); - } - } - - // Maybe an error in the stream. - if (std::ferror(fp)) - throw from_errno(); - - // Missing '\n' in end of file. - if (!buffer.empty()) { - duk::push(ctx, clear_crlf(buffer)); - duk_put_prop_index(ctx, -2, i++); - } - - return 1; - }); -} - -// }}} - -// {{{ Irccd.File.prototype.read - -/* - * Method: Irccd.File.prototype.read(amount) - * -------------------------------------------------------- - * - * Read the specified amount of characters or the whole file. - * - * Arguments: - * - amount, the amount of characters or -1 to read all (Optional, default: -1). - * Returns: - * The string. - * Throws: - * - Irccd.SystemError on errors - */ -auto File_prototype_read(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - const auto fp = self(ctx)->get_handle(); - const auto amount = duk_is_number(ctx, 0) ? duk_get_int(ctx, 0) : -1; - - if (amount == 0 || !fp) - return 0; - - std::string data; - std::size_t total = 0; - - if (amount < 0) { - std::array<char, 128> buffer; - std::size_t nread; - - while ((nread = std::fread(&buffer[0], sizeof (buffer[0]), buffer.size(), fp)) > 0) { - if (std::ferror(fp)) - throw from_errno(); - - std::copy(buffer.begin(), buffer.begin() + nread, std::back_inserter(data)); - total += nread; - } - } else { - data.resize(static_cast<std::size_t>(amount)); - total = std::fread(&data[0], sizeof (data[0]), static_cast<std::size_t>(amount), fp); - - if (std::ferror(fp)) - throw from_errno(); - - data.resize(total); - } - - return duk::push(ctx, data); - }); -} - -// }}} - -// {{{ Irccd.File.prototype.readline - -/* - * Method: Irccd.File.prototype.readline() - * -------------------------------------------------------- - * - * Read the next line available. - * - * Returns: - * The next line or undefined if eof. - * Throws: - * - Irccd.SystemError on errors - */ -auto File_prototype_readline(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - auto fp = self(ctx)->get_handle(); - - if (fp == nullptr || std::feof(fp)) - return 0; - - std::string result; - - for (int ch; (ch = std::fgetc(fp)) != EOF && ch != '\n'; ) - result += (char)ch; - if (std::ferror(fp)) - throw from_errno(); - - return duk::push(ctx, clear_crlf(result)); - }); -} - -// }}} - -// {{{ Irccd.File.prototype.remove - -/* - * Method: Irccd.File.prototype.remove() - * -------------------------------------------------------- - * - * Synonym of Irccd.File.prototype.remove(path) but with the path from the file. - * - * Throws: - * - Irccd.SystemError on errors - */ -auto File_prototype_remove(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - boost::filesystem::remove(self(ctx)->get_path()); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.File.prototype.seek - -/* - * Method: Irccd.File.prototype.seek(type, amount) - * -------------------------------------------------------- - * - * Sets the position in the file. - * - * Arguments: - * - type, the type of setting (File.SeekSet, File.SeekCur, File.SeekSet), - * - amount, the new offset. - * Throws: - * - Irccd.SystemError on errors - */ -auto File_prototype_seek(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - auto fp = self(ctx)->get_handle(); - auto type = duk_require_int(ctx, 0); - auto amount = duk_require_int(ctx, 1); - - if (fp != nullptr && std::fseek(fp, amount, type) != 0) - throw from_errno(); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.File.prototype.stat - -#if defined(IRCCD_HAVE_STAT) - -/* - * Method: Irccd.File.prototype.stat() [optional] - * -------------------------------------------------------- - * - * Synonym of File.stat(path) but with the path from the file. - * - * Returns: - * The stat information. - * Throws: - * - Irccd.SystemError on errors - */ -auto File_prototype_stat(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - auto file = self(ctx); - struct stat st; - - if (file->get_handle() == nullptr && ::stat(file->get_path().c_str(), &st) < 0) - throw from_errno(); - - duk::push(ctx, st); - - return 1; - }); -} - -#endif // !IRCCD_HAVE_STAT - -// }}} - -// {{{ Irccd.File.prototype.tell - -/* - * Method: Irccd.File.prototype.tell() - * -------------------------------------------------------- - * - * Get the actual position in the file. - * - * Returns: - * The position. - * Throws: - * - Irccd.SystemError on errors - */ -auto File_prototype_tell(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - auto fp = self(ctx)->get_handle(); - long pos; - - if (fp == nullptr) - return 0; - - if ((pos = std::ftell(fp)) == -1L) - throw from_errno(); - - duk_push_int(ctx, pos); - - return 1; - }); -} - -// }}} - -// {{{ Irccd.File.prototype.write - -/* - * Method: Irccd.File.prototype.write(data) - * -------------------------------------------------------- - * - * Write some characters to the file. - * - * Arguments: - * - data, the character to write. - * Returns: - * The number of bytes written. - * Throws: - * - Irccd.SystemError on errors - */ -auto File_prototype_write(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - auto fp = self(ctx)->get_handle(); - auto data = duk::require<std::string>(ctx, 0); - - if (fp == nullptr) - return 0; - - const auto nwritten = std::fwrite(data.c_str(), 1, data.length(), fp); - - if (std::ferror(fp)) - throw from_errno(); - - duk_push_uint(ctx, nwritten); - - return 1; - }); -} - -// }}} - -// {{{ Irccd.File [constructor] - -/* - * Function: Irccd.File(path, mode) [constructor] - * -------------------------------------------------------- - * - * Open a file specified by path with the specified mode. - * - * Arguments: - * - path, the path to the file, - * - mode, the mode string. - * Throws: - * - Irccd.SystemError on errors - */ -auto File_constructor(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - if (!duk_is_constructor_call(ctx)) - return 0; - - const auto path = duk::require<std::string>(ctx, 0); - const auto mode = duk::require<std::string>(ctx, 1); - - duk_push_this(ctx); - duk_push_pointer(ctx, new std::shared_ptr<file>(new file(path, mode))); - duk_put_prop_string(ctx, -2, signature.data()); - duk_pop(ctx); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.File [destructor] - -/* - * Function: Irccd.File() [destructor] - * ------------------------------------------------------------------ - * - * Delete the property. - */ -auto File_destructor(duk_context* ctx) -> duk_ret_t -{ - duk_get_prop_string(ctx, 0, signature.data()); - delete static_cast<std::shared_ptr<file>*>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - duk_del_prop_string(ctx, 0, signature.data()); - - return 0; -} - -// }}} - -// {{{ Irccd.File.basename - -/* - * Function: Irccd.File.basename(path) - * -------------------------------------------------------- - * - * duk_ret_turn the file basename as specified in `basename(3)` C function. - * - * Arguments: - * - path, the path to the file. - * Returns: - * The base name. - * Throws: - * - Irccd.SystemError on errors - */ -auto File_basename(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - return duk::push(ctx, fs_util::base_name(duk_require_string(ctx, 0))); - }); -} - -// }}} - -// {{{ Irccd.File.dirname - -/* - * Function: Irccd.File.dirname(path) - * -------------------------------------------------------- - * - * duk_ret_turn the file directory name as specified in `dirname(3)` C function. - * - * Arguments: - * - path, the path to the file. - * Throws: - * - Irccd.SystemError on errors - */ -auto File_dirname(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - return duk::push(ctx, fs_util::dir_name(duk_require_string(ctx, 0))); - }); -} - -// }}} - -// {{{ Irccd.File.exists - -/* - * Function: Irccd.File.exists(path) - * -------------------------------------------------------- - * - * Check if the file exists. - * - * Arguments: - * - path, the path to the file. - * Returns: - * True if exists. - * Throws: - * - Irccd.SystemError on errors - */ -auto File_exists(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - return duk::push(ctx, boost::filesystem::exists(duk_require_string(ctx, 0))); - }); -} - -// }}} - -// {{{ Irccd.File.remove - -/* - * Function Irccd.File.remove(path) - * -------------------------------------------------------- - * - * Remove the file at the specified path. - * - * Arguments: - * - path, the path to the file. - * Throws: - * - Irccd.SystemError on errors - */ -auto File_remove(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - boost::filesystem::remove(duk::require<std::string>(ctx, 0)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.File.stat - -#if defined(IRCCD_HAVE_STAT) - -/* - * Function Irccd.File.stat(path) [optional] - * -------------------------------------------------------- - * - * Get file information at the specified path. - * - * Arguments: - * - path, the path to the file. - * Returns: - * The stat information. - * Throws: - * - Irccd.SystemError on errors - */ -auto File_stat(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - struct stat st; - - if (::stat(duk_require_string(ctx, 0), &st) < 0) - throw from_errno(); - - return duk::push(ctx, st); - }); -} - -#endif // !IRCCD_HAVE_STAT - -// }}} - -// {{{ definitions - -const duk_function_list_entry methods[] = { - { "basename", File_prototype_basename, 0 }, - { "close", File_prototype_close, 0 }, - { "dirname", File_prototype_dirname, 0 }, - { "lines", File_prototype_lines, 0 }, - { "read", File_prototype_read, 1 }, - { "readline", File_prototype_readline, 0 }, - { "remove", File_prototype_remove, 0 }, - { "seek", File_prototype_seek, 2 }, -#if defined(IRCCD_HAVE_STAT) - { "stat", File_prototype_stat, 0 }, -#endif - { "tell", File_prototype_tell, 0 }, - { "write", File_prototype_write, 1 }, - { nullptr, nullptr, 0 } -}; - -const duk_function_list_entry functions[] = { - { "basename", File_basename, 1 }, - { "dirname", File_dirname, 1 }, - { "exists", File_exists, 1 }, - { "remove", File_remove, 1 }, -#if defined(IRCCD_HAVE_STAT) - { "stat", File_stat, 1 }, -#endif - { nullptr, nullptr, 0 } -}; - -const duk_number_list_entry constants[] = { - { "SeekCur", SEEK_CUR }, - { "SeekEnd", SEEK_END }, - { "SeekSet", SEEK_SET }, - { nullptr, 0 } -}; - -// }}} - -} // !namespace - -// {{{ file_js_api - -auto file_js_api::get_name() const noexcept -> std::string_view -{ - return "Irccd.File"; -} - -void file_js_api::load(bot&, std::shared_ptr<js_plugin> plugin) -{ - duk::stack_guard sa(plugin->get_context()); - - duk_get_global_string(plugin->get_context(), "Irccd"); - duk_push_c_function(plugin->get_context(), File_constructor, 2); - duk_put_number_list(plugin->get_context(), -1, constants); - duk_put_function_list(plugin->get_context(), -1, functions); - duk_push_object(plugin->get_context()); - duk_put_function_list(plugin->get_context(), -1, methods); - duk_push_c_function(plugin->get_context(), File_destructor, 1); - duk_set_finalizer(plugin->get_context(), -2); - duk_dup(plugin->get_context(), -1); - duk_put_global_string(plugin->get_context(), prototype.data()); - duk_put_prop_string(plugin->get_context(), -2, "prototype"); - duk_put_prop_string(plugin->get_context(), -2, "File"); - duk_pop(plugin->get_context()); -} - -// }}} - -// {{{ duk::type_traits<std::shared_ptr<file>> - -using file_traits = duk::type_traits<std::shared_ptr<file>>; - -void file_traits::push(duk_context* ctx, std::shared_ptr<file> fp) -{ - assert(ctx); - assert(fp); - - duk::stack_guard sa(ctx, 1); - - duk_push_object(ctx); - duk_push_pointer(ctx, new std::shared_ptr<file>(std::move(fp))); - duk_put_prop_string(ctx, -2, signature.data()); - duk_get_global_string(ctx, prototype.data()); - duk_set_prototype(ctx, -2); -} - -auto file_traits::require(duk_context* ctx, duk_idx_t index) -> std::shared_ptr<file> -{ - if (!duk_is_object(ctx, index) || !duk_has_prop_string(ctx, index, signature.data())) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a File object"); - - duk_get_prop_string(ctx, index, signature.data()); - const auto fp = static_cast<std::shared_ptr<file>*>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - - return *fp; -} - -// }}} - -// {{{ duk::type_traits<struct stat> - -#if defined(IRCCD_HAVE_STAT) - -void duk::type_traits<struct stat>::push(duk_context* ctx, const struct stat& st) -{ - duk::stack_guard sa(ctx, 1); - - duk_push_object(ctx); - -#if defined(IRCCD_HAVE_STAT_ST_ATIME) - duk_push_int(ctx, st.st_atime); - duk_put_prop_string(ctx, -2, "atime"); -#endif -#if defined(IRCCD_HAVE_STAT_ST_BLKSIZE) - duk_push_int(ctx, st.st_blksize); - duk_put_prop_string(ctx, -2, "blksize"); -#endif -#if defined(IRCCD_HAVE_STAT_ST_BLOCKS) - duk_push_int(ctx, st.st_blocks); - duk_put_prop_string(ctx, -2, "blocks"); -#endif -#if defined(IRCCD_HAVE_STAT_ST_CTIME) - duk_push_int(ctx, st.st_ctime); - duk_put_prop_string(ctx, -2, "ctime"); -#endif -#if defined(IRCCD_HAVE_STAT_ST_DEV) - duk_push_int(ctx, st.st_dev); - duk_put_prop_string(ctx, -2, "dev"); -#endif -#if defined(IRCCD_HAVE_STAT_ST_GID) - duk_push_int(ctx, st.st_gid); - duk_put_prop_string(ctx, -2, "gid"); -#endif -#if defined(IRCCD_HAVE_STAT_ST_INO) - duk_push_int(ctx, st.st_ino); - duk_put_prop_string(ctx, -2, "ino"); -#endif -#if defined(IRCCD_HAVE_STAT_ST_MODE) - duk_push_int(ctx, st.st_mode); - duk_put_prop_string(ctx, -2, "mode"); -#endif -#if defined(IRCCD_HAVE_STAT_ST_MTIME) - duk_push_int(ctx, st.st_mtime); - duk_put_prop_string(ctx, -2, "mtime"); -#endif -#if defined(IRCCD_HAVE_STAT_ST_NLINK) - duk_push_int(ctx, st.st_nlink); - duk_put_prop_string(ctx, -2, "nlink"); -#endif -#if defined(IRCCD_HAVE_STAT_ST_RDEV) - duk_push_int(ctx, st.st_rdev); - duk_put_prop_string(ctx, -2, "rdev"); -#endif -#if defined(IRCCD_HAVE_STAT_ST_SIZE) - duk_push_int(ctx, st.st_size); - duk_put_prop_string(ctx, -2, "size"); -#endif -#if defined(IRCCD_HAVE_STAT_ST_UID) - duk_push_int(ctx, st.st_uid); - duk_put_prop_string(ctx, -2, "uid"); -#endif -} - -#endif // !IRCCD_HAVE_STAT - -// }}} - -} // !irccd::js
--- a/libirccd-js/irccd/js/file_js_api.hpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,211 +0,0 @@ -/* - * file_js_api.hpp -- Irccd.File API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_FILE_JS_API_HPP -#define IRCCD_JS_FILE_JS_API_HPP - -/** - * \file file_js_api.hpp - * \brief Irccd.File Javascript API. - */ - -#include <irccd/sysconfig.hpp> - -#if defined(IRCCD_HAVE_STAT) -# include <sys/types.h> -# include <sys/stat.h> -#endif - -#include <cassert> -#include <cerrno> -#include <cstdio> -#include <cstring> -#include <functional> -#include <stdexcept> -#include <string> - -#include "js_api.hpp" - -namespace irccd::js { - -/** - * \brief Object for Javascript to perform I/O. - * - * This class can be constructed to Javascript. - * - * It is used in: - * - * - Irccd.File [constructor] - * - Irccd.System.popen (optional) - */ -class file { -private: - file(const file&) = delete; - file& operator=(const file&) = delete; - - file(file&&) = delete; - file& operator=(file&&) = delete; - -private: - std::string path_; - std::FILE* stream_; - std::function<void (std::FILE*)> destructor_; - -public: - /** - * Construct a file specified by path - * - * \param path the path - * \param mode the mode string (for std::fopen) - * \throw std::runtime_error on failures - */ - inline file(std::string path, const std::string& mode) - : path_(std::move(path)) - , destructor_([] (std::FILE* fp) { std::fclose(fp); }) - { - if ((stream_ = std::fopen(path_.c_str(), mode.c_str())) == nullptr) - throw std::runtime_error(std::strerror(errno)); - } - - /** - * Construct a file from a already created FILE pointer (e.g. popen). - * - * The class takes ownership of fp and will close it. - * - * \pre destructor must not be null - * \param fp the file pointer - * \param destructor the function to close fp (e.g. std::fclose) - */ - inline file(std::FILE* fp, std::function<void (std::FILE*)> destructor) noexcept - : stream_(fp) - , destructor_(std::move(destructor)) - { - assert(destructor_ != nullptr); - } - - /** - * Closes the file. - */ - virtual ~file() noexcept - { - close(); - } - - /** - * Get the path. - * - * \return the path - * \warning empty when constructed from the FILE constructor - */ - inline const std::string& get_path() const noexcept - { - return path_; - } - - /** - * Get the handle. - * - * \return the handle or nullptr if the stream was closed - */ - inline std::FILE* get_handle() noexcept - { - return stream_; - } - - /** - * Force close, can be safely called multiple times. - */ - inline void close() noexcept - { - if (stream_) { - destructor_(stream_); - stream_ = nullptr; - } - } -}; - -/** - * \ingroup js-api - * \brief Irccd.File Javascript API. - */ -class file_js_api : public js_api { -public: - /** - * \copydoc js_api::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc js_api::load - */ - void load(daemon::bot& bot, std::shared_ptr<js_plugin> plugin) override; -}; - -namespace duk { - -/** - * \brief Specialization for generic file type as shared_ptr. - * - * Supports push, require. - */ -template <> -struct type_traits<std::shared_ptr<file>> { - /** - * Push a file. - * - * \pre fp != nullptr - * \param ctx the the context - * \param fp the file - */ - static void push(duk_context* ctx, std::shared_ptr<file> fp); - - /** - * Require a file. Raises a JavaScript error if not a File. - * - * \param ctx the context - * \param index the index - * \return the file pointer - */ - static auto require(duk_context* ctx, duk_idx_t index) -> std::shared_ptr<file>; -}; - -#if defined(IRCCD_HAVE_STAT) - -/** - * \brief Specialization for struct stat. - * - * Supports push. - */ -template <> -struct type_traits<struct stat> { - /** - * Push the stat information to the stack as Javascript object. - * - * \param ctx the context - * \param st the stat structure - */ - static void push(duk_context* ctx, const struct stat& st); -}; - -#endif // !IRCCD_HAVE_STAT - -} // !duk - -} // !irccd::js - -#endif // !IRCCD_JS_FILE_JS_API_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/irccd_api.cpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,246 @@ +/* + * irccd_api.cpp -- Irccd API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <irccd/sysconfig.hpp> + +#include <cerrno> +#include <string> +#include <unordered_map> + +#include "irccd_api.hpp" +#include "plugin.hpp" + +using irccd::daemon::bot; + +namespace irccd::js { + +namespace { + +// {{{ do_raise + +template <typename Error> +void do_raise(duk_context* ctx, const Error& ex) +{ + duk::stack_guard sa(ctx, 1); + + duk_get_global_string(ctx, "Irccd"); + duk_get_prop_string(ctx, -1, "SystemError"); + duk_remove(ctx, -2); + duk::push(ctx, ex.code().value()); + duk::push(ctx, ex.code().message()); + duk_new(ctx, 2); + + (void)duk_throw(ctx); +} + +// }}} + +// {{{ Irccd.SystemError [constructor] + +auto SystemError_constructor(duk_context* ctx) -> duk_ret_t +{ + duk_push_this(ctx); + duk_push_int(ctx, duk_require_int(ctx, 0)); + duk_put_prop_string(ctx, -2, "errno"); + duk_push_string(ctx, duk_require_string(ctx, 1)); + duk_put_prop_string(ctx, -2, "message"); + duk_push_string(ctx, "SystemError"); + duk_put_prop_string(ctx, -2, "name"); + duk_pop(ctx); + + return 0; +} + +// }}} + +// {{{ definitions + +const std::unordered_map<std::string, int> errors{ + { "E2BIG", E2BIG }, + { "EACCES", EACCES }, + { "EADDRINUSE", EADDRINUSE }, + { "EADDRNOTAVAIL", EADDRNOTAVAIL }, + { "EAFNOSUPPORT", EAFNOSUPPORT }, + { "EAGAIN", EAGAIN }, + { "EALREADY", EALREADY }, + { "EBADF", EBADF }, +#if defined(EBADMSG) + { "EBADMSG", EBADMSG }, +#endif + { "EBUSY", EBUSY }, + { "ECANCELED", ECANCELED }, + { "ECHILD", ECHILD }, + { "ECONNABORTED", ECONNABORTED }, + { "ECONNREFUSED", ECONNREFUSED }, + { "ECONNRESET", ECONNRESET }, + { "EDEADLK", EDEADLK }, + { "EDESTADDRREQ", EDESTADDRREQ }, + { "EDOM", EDOM }, + { "EEXIST", EEXIST }, + { "EFAULT", EFAULT }, + { "EFBIG", EFBIG }, + { "EHOSTUNREACH", EHOSTUNREACH }, +#if defined(EIDRM) + { "EIDRM", EIDRM }, +#endif + { "EILSEQ", EILSEQ }, + { "EINPROGRESS", EINPROGRESS }, + { "EINTR", EINTR }, + { "EINVAL", EINVAL }, + { "EIO", EIO }, + { "EISCONN", EISCONN }, + { "EISDIR", EISDIR }, + { "ELOOP", ELOOP }, + { "EMFILE", EMFILE }, + { "EMLINK", EMLINK }, + { "EMSGSIZE", EMSGSIZE }, + { "ENAMETOOLONG", ENAMETOOLONG }, + { "ENETDOWN", ENETDOWN }, + { "ENETRESET", ENETRESET }, + { "ENETUNREACH", ENETUNREACH }, + { "ENFILE", ENFILE }, + { "ENOBUFS", ENOBUFS }, +#if defined(ENODATA) + { "ENODATA", ENODATA }, +#endif + { "ENODEV", ENODEV }, + { "ENOENT", ENOENT }, + { "ENOEXEC", ENOEXEC }, + { "ENOLCK", ENOLCK }, +#if defined(ENOLINK) + { "ENOLINK", ENOLINK }, +#endif + { "ENOMEM", ENOMEM }, +#if defined(ENOMSG) + { "ENOMSG", ENOMSG }, +#endif + { "ENOPROTOOPT", ENOPROTOOPT }, + { "ENOSPC", ENOSPC }, +#if defined(ENOSR) + { "ENOSR", ENOSR }, +#endif +#if defined(ENOSTR) + { "ENOSTR", ENOSTR }, +#endif + { "ENOSYS", ENOSYS }, + { "ENOTCONN", ENOTCONN }, + { "ENOTDIR", ENOTDIR }, + { "ENOTEMPTY", ENOTEMPTY }, +#if defined(ENOTRECOVERABLE) + { "ENOTRECOVERABLE", ENOTRECOVERABLE }, +#endif + { "ENOTSOCK", ENOTSOCK }, + { "ENOTSUP", ENOTSUP }, + { "ENOTTY", ENOTTY }, + { "ENXIO", ENXIO }, + { "EOPNOTSUPP", EOPNOTSUPP }, + { "EOVERFLOW", EOVERFLOW }, + { "EOWNERDEAD", EOWNERDEAD }, + { "EPERM", EPERM }, + { "EPIPE", EPIPE }, + { "EPROTO", EPROTO }, + { "EPROTONOSUPPORT", EPROTONOSUPPORT }, + { "EPROTOTYPE", EPROTOTYPE }, + { "ERANGE", ERANGE }, + { "EROFS", EROFS }, + { "ESPIPE", ESPIPE }, + { "ESRCH", ESRCH }, +#if defined(ETIME) + { "ETIME", ETIME }, +#endif + { "ETIMEDOUT", ETIMEDOUT }, +#if defined(ETXTBSY) + { "ETXTBSY", ETXTBSY }, +#endif + { "EWOULDBLOCK", EWOULDBLOCK }, + { "EXDEV", EXDEV } +}; + +// }}} + +} // !namespace + +void duk::type_traits<std::system_error>::raise(duk_context* ctx, const std::system_error& ex) +{ + do_raise(ctx, ex); +} + +void duk::type_traits<boost::system::system_error>::raise(duk_context* ctx, const boost::system::system_error& ex) +{ + do_raise(ctx, ex); +} + +auto irccd_api::get_name() const noexcept -> std::string_view +{ + return "Irccd"; +} + +void irccd_api::load(bot& bot, std::shared_ptr<plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + // irccd. + duk_push_object(plugin->get_context()); + + // Version. + duk_push_object(plugin->get_context()); + duk::push(plugin->get_context(), IRCCD_VERSION_MAJOR); + duk_put_prop_string(plugin->get_context(), -2, "major"); + duk::push(plugin->get_context(), IRCCD_VERSION_MINOR); + duk_put_prop_string(plugin->get_context(), -2, "minor"); + duk::push(plugin->get_context(), IRCCD_VERSION_PATCH); + duk_put_prop_string(plugin->get_context(), -2, "patch"); + duk_put_prop_string(plugin->get_context(), -2, "version"); + + // Create the system_error that inherits from Error. + duk_push_c_function(plugin->get_context(), SystemError_constructor, 2); + + // Put errno codes into the irccd.system_error object. + for (const auto& [k, v] : errors) { + duk_push_int(plugin->get_context(), v); + duk_put_prop_string(plugin->get_context(), -2, k.c_str()); + } + + duk_push_object(plugin->get_context()); + duk_get_global_string(plugin->get_context(), "Error"); + duk_get_prop_string(plugin->get_context(), -1, "prototype"); + duk_remove(plugin->get_context(), -2); + duk_set_prototype(plugin->get_context(), -2); + duk_put_prop_string(plugin->get_context(), -2, "prototype"); + duk_put_prop_string(plugin->get_context(), -2, "SystemError"); + + // Set irccd as global. + duk_put_global_string(plugin->get_context(), "Irccd"); + + // Store global instance. + duk_push_pointer(plugin->get_context(), &bot); + duk_put_global_string(plugin->get_context(), DUK_HIDDEN_SYMBOL("irccd-ref")); +} + +auto duk::type_traits<bot>::self(duk_context *ctx) -> bot& +{ + duk::stack_guard sa(ctx); + + duk_get_global_string(ctx, DUK_HIDDEN_SYMBOL("irccd-ref")); + const auto ptr = static_cast<bot*>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + + return *ptr; +} + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/irccd_api.hpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,109 @@ +/* + * irccd_api.hpp -- Irccd API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_IRCCD_API_HPP +#define IRCCD_JS_IRCCD_API_HPP + +/** + * \file irccd_api.hpp + * \brief irccd Javascript API. + */ + +#include <cerrno> +#include <cstring> +#include <string> +#include <system_error> + +#include <boost/system/system_error.hpp> + +#include "api.hpp" + +namespace irccd::daemon { + +class bot; + +} // !irccd + +namespace irccd::js { + +/** + * \ingroup js-api + * \brief Irccd Javascript API. + */ +class irccd_api : public api { +public: + /** + * \copydoc api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc api::load + */ + void load(daemon::bot& bot, std::shared_ptr<plugin> plugin) override; +}; + +namespace duk { + +/** + * \brief Specialize dukx_type_traits for bot. + */ +template <> +struct type_traits<daemon::bot> { + /** + * Get irccd instance stored in this context. + * + * \param ctx the context + * \return the irccd reference + */ + static auto self(duk_context* ctx) -> daemon::bot&; +}; + +/** + * \brief Specialize dukx_type_traits for boost::system::system_error. + */ +template <> +struct type_traits<std::system_error> { + /** + * Raise an Irccd.SystemError. + * + * \param ctx the context + * \param ex the exception + */ + static void raise(duk_context* ctx, const std::system_error& ex); +}; + +/** + * \brief Specialize dukx_type_traits for boost::system::system_error. + */ +template <> +struct type_traits<boost::system::system_error> { + /** + * Raise an Irccd.SystemError. + * + * \param ctx the context + * \param ex the exception + */ + static void raise(duk_context* ctx, const boost::system::system_error& ex); +}; + +} // !duk + +} // !irccd::js + +#endif // !IRCCD_JS_IRCCD_API_HPP
--- a/libirccd-js/irccd/js/irccd_js_api.cpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,246 +0,0 @@ -/* - * irccd_js_api.cpp -- Irccd API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <irccd/sysconfig.hpp> - -#include <cerrno> -#include <string> -#include <unordered_map> - -#include "irccd_js_api.hpp" -#include "js_plugin.hpp" - -using irccd::daemon::bot; - -namespace irccd::js { - -namespace { - -// {{{ do_raise - -template <typename Error> -void do_raise(duk_context* ctx, const Error& ex) -{ - duk::stack_guard sa(ctx, 1); - - duk_get_global_string(ctx, "Irccd"); - duk_get_prop_string(ctx, -1, "SystemError"); - duk_remove(ctx, -2); - duk::push(ctx, ex.code().value()); - duk::push(ctx, ex.code().message()); - duk_new(ctx, 2); - - (void)duk_throw(ctx); -} - -// }}} - -// {{{ Irccd.SystemError [constructor] - -auto SystemError_constructor(duk_context* ctx) -> duk_ret_t -{ - duk_push_this(ctx); - duk_push_int(ctx, duk_require_int(ctx, 0)); - duk_put_prop_string(ctx, -2, "errno"); - duk_push_string(ctx, duk_require_string(ctx, 1)); - duk_put_prop_string(ctx, -2, "message"); - duk_push_string(ctx, "SystemError"); - duk_put_prop_string(ctx, -2, "name"); - duk_pop(ctx); - - return 0; -} - -// }}} - -// {{{ definitions - -const std::unordered_map<std::string, int> errors{ - { "E2BIG", E2BIG }, - { "EACCES", EACCES }, - { "EADDRINUSE", EADDRINUSE }, - { "EADDRNOTAVAIL", EADDRNOTAVAIL }, - { "EAFNOSUPPORT", EAFNOSUPPORT }, - { "EAGAIN", EAGAIN }, - { "EALREADY", EALREADY }, - { "EBADF", EBADF }, -#if defined(EBADMSG) - { "EBADMSG", EBADMSG }, -#endif - { "EBUSY", EBUSY }, - { "ECANCELED", ECANCELED }, - { "ECHILD", ECHILD }, - { "ECONNABORTED", ECONNABORTED }, - { "ECONNREFUSED", ECONNREFUSED }, - { "ECONNRESET", ECONNRESET }, - { "EDEADLK", EDEADLK }, - { "EDESTADDRREQ", EDESTADDRREQ }, - { "EDOM", EDOM }, - { "EEXIST", EEXIST }, - { "EFAULT", EFAULT }, - { "EFBIG", EFBIG }, - { "EHOSTUNREACH", EHOSTUNREACH }, -#if defined(EIDRM) - { "EIDRM", EIDRM }, -#endif - { "EILSEQ", EILSEQ }, - { "EINPROGRESS", EINPROGRESS }, - { "EINTR", EINTR }, - { "EINVAL", EINVAL }, - { "EIO", EIO }, - { "EISCONN", EISCONN }, - { "EISDIR", EISDIR }, - { "ELOOP", ELOOP }, - { "EMFILE", EMFILE }, - { "EMLINK", EMLINK }, - { "EMSGSIZE", EMSGSIZE }, - { "ENAMETOOLONG", ENAMETOOLONG }, - { "ENETDOWN", ENETDOWN }, - { "ENETRESET", ENETRESET }, - { "ENETUNREACH", ENETUNREACH }, - { "ENFILE", ENFILE }, - { "ENOBUFS", ENOBUFS }, -#if defined(ENODATA) - { "ENODATA", ENODATA }, -#endif - { "ENODEV", ENODEV }, - { "ENOENT", ENOENT }, - { "ENOEXEC", ENOEXEC }, - { "ENOLCK", ENOLCK }, -#if defined(ENOLINK) - { "ENOLINK", ENOLINK }, -#endif - { "ENOMEM", ENOMEM }, -#if defined(ENOMSG) - { "ENOMSG", ENOMSG }, -#endif - { "ENOPROTOOPT", ENOPROTOOPT }, - { "ENOSPC", ENOSPC }, -#if defined(ENOSR) - { "ENOSR", ENOSR }, -#endif -#if defined(ENOSTR) - { "ENOSTR", ENOSTR }, -#endif - { "ENOSYS", ENOSYS }, - { "ENOTCONN", ENOTCONN }, - { "ENOTDIR", ENOTDIR }, - { "ENOTEMPTY", ENOTEMPTY }, -#if defined(ENOTRECOVERABLE) - { "ENOTRECOVERABLE", ENOTRECOVERABLE }, -#endif - { "ENOTSOCK", ENOTSOCK }, - { "ENOTSUP", ENOTSUP }, - { "ENOTTY", ENOTTY }, - { "ENXIO", ENXIO }, - { "EOPNOTSUPP", EOPNOTSUPP }, - { "EOVERFLOW", EOVERFLOW }, - { "EOWNERDEAD", EOWNERDEAD }, - { "EPERM", EPERM }, - { "EPIPE", EPIPE }, - { "EPROTO", EPROTO }, - { "EPROTONOSUPPORT", EPROTONOSUPPORT }, - { "EPROTOTYPE", EPROTOTYPE }, - { "ERANGE", ERANGE }, - { "EROFS", EROFS }, - { "ESPIPE", ESPIPE }, - { "ESRCH", ESRCH }, -#if defined(ETIME) - { "ETIME", ETIME }, -#endif - { "ETIMEDOUT", ETIMEDOUT }, -#if defined(ETXTBSY) - { "ETXTBSY", ETXTBSY }, -#endif - { "EWOULDBLOCK", EWOULDBLOCK }, - { "EXDEV", EXDEV } -}; - -// }}} - -} // !namespace - -void duk::type_traits<std::system_error>::raise(duk_context* ctx, const std::system_error& ex) -{ - do_raise(ctx, ex); -} - -void duk::type_traits<boost::system::system_error>::raise(duk_context* ctx, const boost::system::system_error& ex) -{ - do_raise(ctx, ex); -} - -auto irccd_js_api::get_name() const noexcept -> std::string_view -{ - return "Irccd"; -} - -void irccd_js_api::load(bot& bot, std::shared_ptr<js_plugin> plugin) -{ - duk::stack_guard sa(plugin->get_context()); - - // irccd. - duk_push_object(plugin->get_context()); - - // Version. - duk_push_object(plugin->get_context()); - duk::push(plugin->get_context(), IRCCD_VERSION_MAJOR); - duk_put_prop_string(plugin->get_context(), -2, "major"); - duk::push(plugin->get_context(), IRCCD_VERSION_MINOR); - duk_put_prop_string(plugin->get_context(), -2, "minor"); - duk::push(plugin->get_context(), IRCCD_VERSION_PATCH); - duk_put_prop_string(plugin->get_context(), -2, "patch"); - duk_put_prop_string(plugin->get_context(), -2, "version"); - - // Create the system_error that inherits from Error. - duk_push_c_function(plugin->get_context(), SystemError_constructor, 2); - - // Put errno codes into the irccd.system_error object. - for (const auto& [k, v] : errors) { - duk_push_int(plugin->get_context(), v); - duk_put_prop_string(plugin->get_context(), -2, k.c_str()); - } - - duk_push_object(plugin->get_context()); - duk_get_global_string(plugin->get_context(), "Error"); - duk_get_prop_string(plugin->get_context(), -1, "prototype"); - duk_remove(plugin->get_context(), -2); - duk_set_prototype(plugin->get_context(), -2); - duk_put_prop_string(plugin->get_context(), -2, "prototype"); - duk_put_prop_string(plugin->get_context(), -2, "SystemError"); - - // Set irccd as global. - duk_put_global_string(plugin->get_context(), "Irccd"); - - // Store global instance. - duk_push_pointer(plugin->get_context(), &bot); - duk_put_global_string(plugin->get_context(), DUK_HIDDEN_SYMBOL("irccd-ref")); -} - -auto duk::type_traits<bot>::self(duk_context *ctx) -> bot& -{ - duk::stack_guard sa(ctx); - - duk_get_global_string(ctx, DUK_HIDDEN_SYMBOL("irccd-ref")); - const auto ptr = static_cast<bot*>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - - return *ptr; -} - -} // !irccd::js
--- a/libirccd-js/irccd/js/irccd_js_api.hpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,109 +0,0 @@ -/* - * irccd_js_api.hpp -- Irccd API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_IRCCD_JS_API_HPP -#define IRCCD_JS_IRCCD_JS_API_HPP - -/** - * \file irccd_js_api.hpp - * \brief irccd Javascript API. - */ - -#include <cerrno> -#include <cstring> -#include <string> -#include <system_error> - -#include <boost/system/system_error.hpp> - -#include "js_api.hpp" - -namespace irccd::daemon { - -class bot; - -} // !irccd - -namespace irccd::js { - -/** - * \ingroup js-api - * \brief Irccd Javascript API. - */ -class irccd_js_api : public js_api { -public: - /** - * \copydoc js_api::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc js_api::load - */ - void load(daemon::bot& bot, std::shared_ptr<js_plugin> plugin) override; -}; - -namespace duk { - -/** - * \brief Specialize dukx_type_traits for bot. - */ -template <> -struct type_traits<daemon::bot> { - /** - * Get irccd instance stored in this context. - * - * \param ctx the context - * \return the irccd reference - */ - static auto self(duk_context* ctx) -> daemon::bot&; -}; - -/** - * \brief Specialize dukx_type_traits for boost::system::system_error. - */ -template <> -struct type_traits<std::system_error> { - /** - * Raise an Irccd.SystemError. - * - * \param ctx the context - * \param ex the exception - */ - static void raise(duk_context* ctx, const std::system_error& ex); -}; - -/** - * \brief Specialize dukx_type_traits for boost::system::system_error. - */ -template <> -struct type_traits<boost::system::system_error> { - /** - * Raise an Irccd.SystemError. - * - * \param ctx the context - * \param ex the exception - */ - static void raise(duk_context* ctx, const boost::system::system_error& ex); -}; - -} // !duk - -} // !irccd::js - -#endif // !IRCCD_JS_IRCCD_JS_API_HPP
--- a/libirccd-js/irccd/js/js_api.cpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -/* - * js_api.cpp -- Javascript API module - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "directory_js_api.hpp" -#include "elapsed_timer_js_api.hpp" -#include "file_js_api.hpp" -#include "irccd_js_api.hpp" -#include "logger_js_api.hpp" -#include "plugin_js_api.hpp" -#include "server_js_api.hpp" -#include "system_js_api.hpp" -#include "timer_js_api.hpp" -#include "unicode_js_api.hpp" -#include "util_js_api.hpp" - -namespace irccd::js { - -namespace { - -template <typename T> -auto bind() noexcept -> js_api::constructor -{ - return [] () noexcept { - return std::make_unique<T>(); - }; -} - -} // !namespace - -auto js_api::registry() noexcept -> const std::vector<constructor>& -{ - static const std::vector<constructor> list { - // Irccd API must be loaded first. - bind<irccd_js_api>(), - bind<directory_js_api>(), - bind<elapsed_timer_js_api>(), - bind<file_js_api>(), - bind<logger_js_api>(), - bind<plugin_js_api>(), - bind<server_js_api>(), - bind<system_js_api>(), - bind<timer_js_api>(), - bind<unicode_js_api>(), - bind<util_js_api>() - }; - - return list; -} - -} // !irccd::js
--- a/libirccd-js/irccd/js/js_api.hpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,92 +0,0 @@ -/* - * js_api.hpp -- Javascript API module - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_JS_API_HPP -#define IRCCD_JS_JS_API_HPP - -/** - * \file js_api.hpp - * \brief Javascript API module. - */ - -#include <functional> -#include <memory> -#include <string_view> -#include <vector> - -#include "duk.hpp" - -namespace irccd { - -namespace daemon { - -class bot; - -} // !daemon - -namespace js { - -class js_plugin; - -/** - * \ingroup js-api - * \brief Javascript API module. - */ -class js_api { -public: - /** - * \brief Command constructor factory. - */ - using constructor = std::function<std::unique_ptr<js_api> ()>; - - /** - * \brief Registry of all commands. - */ - static auto registry() noexcept -> const std::vector<constructor>&; - - /** - * Default constructor. - */ - js_api() noexcept = default; - - /** - * Virtual destructor defaulted. - */ - virtual ~js_api() noexcept = default; - - /** - * Get the module name. - * - * \return the name - */ - virtual auto get_name() const noexcept -> std::string_view = 0; - - /** - * Load the module into the Javascript plugin. - * - * \param bot the irccd instance - * \param plugin the plugin - */ - virtual void load(daemon::bot& bot, std::shared_ptr<js_plugin> plugin) = 0; -}; - -} // !js - -} // !irccd - -#endif // !IRCCD_JS_JS_API_HPP
--- a/libirccd-js/irccd/js/js_plugin.cpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,382 +0,0 @@ -/* - * js_plugin.cpp -- Javascript plugins for irccd - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <cstring> -#include <cerrno> -#include <fstream> -#include <iterator> -#include <stdexcept> - -#include <irccd/daemon/bot.hpp> - -#include "js_api.hpp" -#include "js_plugin.hpp" -#include "server_js_api.hpp" - -using irccd::daemon::bot; -using irccd::daemon::connect_event; -using irccd::daemon::disconnect_event; -using irccd::daemon::invite_event; -using irccd::daemon::join_event; -using irccd::daemon::kick_event; -using irccd::daemon::me_event; -using irccd::daemon::message_event; -using irccd::daemon::mode_event; -using irccd::daemon::names_event; -using irccd::daemon::nick_event; -using irccd::daemon::notice_event; -using irccd::daemon::part_event; -using irccd::daemon::plugin; -using irccd::daemon::plugin_error; -using irccd::daemon::topic_event; -using irccd::daemon::whois_event; -using irccd::daemon::whois_info; - -namespace irccd::js { - -namespace { - -auto get_metadata(duk::context& ctx, std::string_view name) -> std::string_view -{ - std::string_view ret("unknown"); - - duk::stack_guard guard(ctx); - duk_get_global_string(ctx, "info"); - - if (duk_get_type(ctx, -1) == DUK_TYPE_OBJECT) { - duk_get_prop_string(ctx, -1, name.data()); - - if (duk_get_type(ctx, -1) == DUK_TYPE_STRING) - ret = duk_get_string(ctx, -1); - - duk_pop(ctx); - } - - duk_pop(ctx); - - return ret; -} - -auto get_table(duk::context& ctx, std::string_view name) -> plugin::map -{ - plugin::map result; - - duk::stack_guard sa(ctx); - duk_get_global_string(ctx, name.data()); - duk_enum(ctx, -1, 0); - - while (duk_next(ctx, -1, true)) { - result.emplace(duk_to_string(ctx, -2), duk_to_string(ctx, -1)); - duk_pop_n(ctx, 2); - } - - duk_pop_n(ctx, 2); - - return result; -} - -void set_table(duk::context& ctx, std::string_view name, const plugin::map& vars) -{ - duk::stack_guard sa(ctx); - duk_get_global_string(ctx, name.data()); - - for (const auto& pair : vars) { - duk::push(ctx, pair.second); - duk_put_prop_string(ctx, -2, pair.first.c_str()); - } - - duk_pop(ctx); -} - -} // !namespace - -void js_plugin::push() noexcept -{ -} - -template <typename Value, typename... Args> -void js_plugin::push(Value&& value, Args&&... args) -{ - duk::push(context_, std::forward<Value>(value)); - push(std::forward<Args>(args)...); -} - -template <typename... Args> -void js_plugin::call(const std::string& func, Args&&... args) -{ - duk::stack_guard sa(context_); - - duk_get_global_string(context_, func.c_str()); - - if (duk_get_type(context_, -1) == DUK_TYPE_UNDEFINED) { - duk_pop(context_); - return; - } - - push(std::forward<Args>(args)...); - - if (duk_pcall(context_, sizeof... (Args)) != 0) - throw plugin_error(plugin_error::exec_error, get_name(), duk::get_stack(context_, -1).get_stack()); - - duk_pop(context_); -} - -js_plugin::js_plugin(std::string id, std::string path) - : plugin(std::move(id)) - , path_(path) -{ - duk::stack_guard sa(context_); - - /* - * Create two special tables for configuration and formats, they are - * referenced later as - * - * - Irccd.Plugin.config - * - Irccd.Plugin.format - * - Irccd.Plugin.paths - * - * In js_plugin_module.cpp. - */ - duk_push_object(context_); - duk_put_global_string(context_, config_property.data()); - duk_push_object(context_); - duk_put_global_string(context_, format_property.data()); - duk_push_object(context_); - duk_put_global_string(context_, paths_property.data()); - - duk_push_pointer(context_, this); - duk_put_global_string(context_, DUK_HIDDEN_SYMBOL("plugin")); - duk::push(context_, path); - duk_put_global_string(context_, DUK_HIDDEN_SYMBOL("path")); -} - -auto js_plugin::get_context() noexcept -> duk::context& -{ - return context_; -} - -auto js_plugin::get_name() const noexcept -> std::string_view -{ - return get_metadata(context_, "name"); -} - -auto js_plugin::get_author() const noexcept -> std::string_view -{ - return get_metadata(context_, "author"); -} - -auto js_plugin::get_license() const noexcept -> std::string_view -{ - return get_metadata(context_, "license"); -} - -auto js_plugin::get_summary() const noexcept -> std::string_view -{ - return get_metadata(context_, "summary"); -} - -auto js_plugin::get_version() const noexcept -> std::string_view -{ - return get_metadata(context_, "version"); -} - -auto js_plugin::get_options() const -> map -{ - return get_table(context_, config_property); -} - -void js_plugin::set_options(const map& map) -{ - set_table(context_, config_property, map); -} - -auto js_plugin::get_formats() const -> map -{ - return get_table(context_, format_property); -} - -void js_plugin::set_formats(const map& map) -{ - set_table(context_, format_property, map); -} - -auto js_plugin::get_paths() const -> map -{ - return get_table(context_, paths_property); -} - -void js_plugin::set_paths(const map& map) -{ - set_table(context_, paths_property, map); -} - -void js_plugin::open() -{ - std::ifstream input(path_); - - if (!input) - throw plugin_error(plugin_error::exec_error, get_name(), std::strerror(errno)); - - std::string data( - std::istreambuf_iterator<char>(input.rdbuf()), - std::istreambuf_iterator<char>() - ); - - if (duk_peval_string(context_, data.c_str())) - throw plugin_error(plugin_error::exec_error, get_name(), duk::get_stack(context_, -1).get_stack()); -} - -void js_plugin::handle_command(bot&, const message_event& event) -{ - call("onCommand", event.server, event.origin, event.channel, event.message); -} - -void js_plugin::handle_connect(bot&, const connect_event& event) -{ - call("onConnect", event.server); -} - -void js_plugin::handle_disconnect(bot&, const disconnect_event& event) -{ - call("onDisconnect", event.server); -} - -void js_plugin::handle_invite(bot&, const invite_event& event) -{ - call("onInvite", event.server, event.origin, event.channel); -} - -void js_plugin::handle_join(bot&, const join_event& event) -{ - call("onJoin", event.server, event.origin, event.channel); -} - -void js_plugin::handle_kick(bot&, const kick_event& event) -{ - call("onKick", event.server, event.origin, event.channel, event.target, event.reason); -} - -void js_plugin::handle_load(bot&) -{ - call("onLoad"); -} - -void js_plugin::handle_message(bot&, const message_event& event) -{ - call("onMessage", event.server, event.origin, event.channel, event.message); -} - -void js_plugin::handle_me(bot&, const me_event& event) -{ - call("onMe", event.server, event.origin, event.channel, event.message); -} - -void js_plugin::handle_mode(bot&, const mode_event& event) -{ - call("onMode", event.server, event.origin, event.channel, event.mode, - event.limit, event.user, event.mask); -} - -void js_plugin::handle_names(bot&, const names_event& event) -{ - call("onNames", event.server, event.channel, event.names); -} - -void js_plugin::handle_nick(bot&, const nick_event& event) -{ - call("onNick", event.server, event.origin, event.nickname); -} - -void js_plugin::handle_notice(bot&, const notice_event& event) -{ - call("onNotice", event.server, event.origin, event.channel, event.message); -} - -void js_plugin::handle_part(bot&, const part_event& event) -{ - call("onPart", event.server, event.origin, event.channel, event.reason); -} - -void js_plugin::handle_reload(bot&) -{ - call("onReload"); -} - -void js_plugin::handle_topic(bot&, const topic_event& event) -{ - call("onTopic", event.server, event.origin, event.channel, event.topic); -} - -void js_plugin::handle_unload(bot&) -{ - call("onUnload"); -} - -void js_plugin::handle_whois(bot&, const whois_event& event) -{ - call("onWhois", event.server, event.whois); -} - -js_plugin_loader::js_plugin_loader(bot& bot, - std::vector<std::string> directories, - std::vector<std::string> extensions) noexcept - : plugin_loader(std::move(directories), std::move(extensions)) - , bot_(bot) -{ -} - -js_plugin_loader::~js_plugin_loader() noexcept = default; - -auto js_plugin_loader::get_modules() const noexcept -> const modules& -{ - return modules_; -} - -auto js_plugin_loader::get_modules() noexcept -> modules& -{ - return modules_; -} - -auto js_plugin_loader::open(std::string_view id, std::string_view path) -> std::shared_ptr<plugin> -{ - auto plugin = std::make_shared<js_plugin>(std::string(id), std::string(path)); - - for (const auto& mod : modules_) - mod->load(bot_, plugin); - - plugin->open(); - - return plugin; -} - -void duk::type_traits<whois_info>::push(duk_context* ctx, const whois_info& whois) -{ - duk_push_object(ctx); - duk::push(ctx, whois.nick); - duk_put_prop_string(ctx, -2, "nickname"); - duk::push(ctx, whois.user); - duk_put_prop_string(ctx, -2, "username"); - duk::push(ctx, whois.realname); - duk_put_prop_string(ctx, -2, "realname"); - duk::push(ctx, whois.hostname); - duk_put_prop_string(ctx, -2, "hostname"); - duk::push(ctx, whois.channels); - duk_put_prop_string(ctx, -2, "channels"); -} - -} // !irccd::js
--- a/libirccd-js/irccd/js/js_plugin.hpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,315 +0,0 @@ -/* - * js_plugin.hpp -- JavaScript plugins for irccd - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_PLUGIN_HPP -#define IRCCD_JS_PLUGIN_HPP - -/** - * \file js_plugin.hpp - * \brief JavaScript plugins for irccd. - */ - -#include <irccd/daemon/plugin.hpp> -#include <irccd/daemon/server.hpp> - -#include "duk.hpp" - -/** - * \brief Javascript namespace - */ -namespace irccd::js { - -class js_api; - -/** - * \ingroup js - * \ingroup daemon-plugins - * \brief JavaScript plugins for irccd. - */ -class js_plugin : public daemon::plugin { -public: - /** - * Global property where to read/write plugin configuration (object). - */ - static inline const std::string_view config_property{DUK_HIDDEN_SYMBOL("config")}; - - /** - * Global property where to read/write plugin formats (object). - */ - static inline const std::string_view format_property{DUK_HIDDEN_SYMBOL("formats")}; - - /** - * Global property where paths are defined (object). - */ - static inline const std::string_view paths_property{DUK_HIDDEN_SYMBOL("paths")}; - -private: - // JavaScript context. - mutable duk::context context_; - - // Path to Javascript script file. - std::string path_; - - void push() noexcept; - - template <typename Value, typename... Args> - void push(Value&& value, Args&&... args); - - template <typename... Args> - void call(const std::string&, Args&&... args); - -public: - /** - * Constructor. - * - * \param id the plugin id - * \param path the path to the plugin - */ - js_plugin(std::string id, std::string path); - - /** - * Access the Duktape context. - * - * \return the context - */ - auto get_context() noexcept -> duk::context&; - - /** - * Open the script file associated. - */ - void open(); - - /** - * \copydoc daemon::plugin::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc daemon::plugin::get_author - */ - auto get_author() const noexcept -> std::string_view override; - - /** - * \copydoc daemon::plugin::get_license - */ - auto get_license() const noexcept -> std::string_view override; - - /** - * \copydoc daemon::plugin::get_summary - */ - auto get_summary() const noexcept -> std::string_view override; - - /** - * \copydoc daemon::plugin::get_version - */ - auto get_version() const noexcept -> std::string_view override; - - /** - * \copydoc daemon::plugin::get_options - */ - auto get_options() const -> map override; - - /** - * \copydoc daemon::plugin::set_options - */ - void set_options(const map& map) override; - - /** - * \copydoc daemon::plugin::get_formats - */ - auto get_formats() const -> map override; - - /** - * \copydoc daemon::plugin::set_formats - */ - void set_formats(const map& map) override; - - /** - * \copydoc daemon::plugin::get_paths - */ - auto get_paths() const -> map override; - - /** - * \copydoc daemon::plugin::set_paths - */ - void set_paths(const map& map) override; - - /** - * \copydoc daemon::plugin::handle_command - */ - void handle_command(daemon::bot& bot, const daemon::message_event& event) override; - - /** - * \copydoc daemon::plugin::handle_connect - */ - void handle_connect(daemon::bot& bot, const daemon::connect_event& event) override; - - /** - * \copydoc daemon::plugin::handle_disconnect - */ - void handle_disconnect(daemon::bot& bot, const daemon::disconnect_event& event) override; - - /** - * \copydoc daemon::plugin::handle_invite - */ - void handle_invite(daemon::bot& bot, const daemon::invite_event& event) override; - - /** - * \copydoc daemon::plugin::handle_join - */ - void handle_join(daemon::bot& bot, const daemon::join_event& event) override; - - /** - * \copydoc daemon::plugin::handle_kick - */ - void handle_kick(daemon::bot& bot, const daemon::kick_event& event) override; - - /** - * \copydoc daemon::plugin::handle_load - */ - void handle_load(daemon::bot& bot) override; - - /** - * \copydoc daemon::plugin::handle_message - */ - void handle_message(daemon::bot& bot, const daemon::message_event& event) override; - - /** - * \copydoc daemon::plugin::handle_me - */ - void handle_me(daemon::bot& bot, const daemon::me_event& event) override; - - /** - * \copydoc daemon::plugin::handle_mode - */ - void handle_mode(daemon::bot& bot, const daemon::mode_event& event) override; - - /** - * \copydoc daemon::plugin::handle_names - */ - void handle_names(daemon::bot& bot, const daemon::names_event& event) override; - - /** - * \copydoc daemon::plugin::handle_nick - */ - void handle_nick(daemon::bot& bot, const daemon::nick_event& event) override; - - /** - * \copydoc daemon::plugin::handle_notice - */ - void handle_notice(daemon::bot& bot, const daemon::notice_event& event) override; - - /** - * \copydoc daemon::plugin::handle_part - */ - void handle_part(daemon::bot& bot, const daemon::part_event& event) override; - - /** - * \copydoc daemon::plugin::handle_reload - */ - void handle_reload(daemon::bot& bot) override; - - /** - * \copydoc daemon::plugin::handle_topic - */ - void handle_topic(daemon::bot& bot, const daemon::topic_event& event) override; - - /** - * \copydoc daemon::plugin::handle_unload - */ - void handle_unload(daemon::bot& bot) override; - - /** - * \copydoc daemon::plugin::handle_whois - */ - void handle_whois(daemon::bot& bot, const daemon::whois_event& event) override; -}; - -/** - * \ingroup plugins - * \brief Implementation for searching Javascript plugins. - */ -class js_plugin_loader : public daemon::plugin_loader { -public: - /** - * \brief The list of Javascript API modules. - */ - using modules = std::vector<std::unique_ptr<js_api>>; - -private: - daemon::bot& bot_; - modules modules_; - -public: - /** - * Constructor. - * - * \param bot the irccd instance - * \param directories directories to search - * \param extensions extensions to search - */ - js_plugin_loader(daemon::bot& bot, - std::vector<std::string> directories = {}, - std::vector<std::string> extensions = {".js"}) noexcept; - - /** - * Destructor defaulted. - */ - ~js_plugin_loader() noexcept; - - /** - * Get the list of modules. - * - * \return the modules - */ - auto get_modules() const noexcept -> const modules&; - - /** - * Overloaded function. - * - * \return the modules - */ - auto get_modules() noexcept -> modules&; - - /** - * \copydoc daemon::plugin_loader::open - */ - auto open(std::string_view id, std::string_view file) -> std::shared_ptr<daemon::plugin>; -}; - -namespace duk { - -/** - * \brief Specialization for type_traits<whois_info> - */ -template <> -struct type_traits<daemon::whois_info> : public std::true_type { - /** - * Push a whois_info. - * - * \param ctx the Duktape context - * \param who the information - */ - static void push(duk_context* ctx, const daemon::whois_info& who); -}; - -} // !duk - -} // !irccd::js - -#endif // !IRCCD_PLUGIN_JS_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/logger_api.cpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,149 @@ +/* + * logger_api.cpp -- Irccd.Logger API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <irccd/daemon/bot.hpp> +#include <irccd/daemon/logger.hpp> +#include <irccd/daemon/plugin_service.hpp> + +#include "irccd_api.hpp" +#include "plugin.hpp" +#include "logger_api.hpp" +#include "plugin_api.hpp" + +using irccd::daemon::bot; + +namespace irccd::js { + +namespace { + +// {{{ print + +auto print(duk_context* ctx, unsigned level) -> duk_ret_t +{ + assert(level <= 2); + + try { + auto& sink = duk::type_traits<bot>::self(ctx).get_log(); + auto& self = duk::type_traits<plugin>::self(ctx); + + switch (level) { + case 0: + sink.debug<daemon::plugin>(self) << duk_require_string(ctx, 0) << std::endl; + break; + case 1: + sink.info<daemon::plugin>(self) << duk_require_string(ctx, 0) << std::endl; + break; + default: + sink.warning<daemon::plugin>(self) << duk_require_string(ctx, 0) << std::endl; + break; + } + } catch (const std::exception& ex) { + duk::raise(ctx, ex); + } + + return 0; +} + +// }}} + +// {{{ Irccd.Logger.info + +/* + * Function: Irccd.Logger.info(message) + * -------------------------------------------------------- + * + * Write a verbose message. + * + * Arguments: + * - message, the message. + * Throws: + * - Irccd.SystemError on errors + */ +auto Logger_info(duk_context* ctx) -> duk_ret_t +{ + return print(ctx, 1); +} + +// }}} + +// {{{ Irccd.Logger.warning + +/* + * Function: Irccd.Logger.warning(message) + * -------------------------------------------------------- + * + * Write a warning message. + * + * Arguments: + * - message, the warning. + * Throws: + * - Irccd.SystemError on errors + */ +auto Logger_warning(duk_context* ctx) -> duk_ret_t +{ + return print(ctx, 2); +} + +// }}} + +// {{{ Irccd.Logger.debug + +/* + * Function: Irccd.Logger.debug(message) + * -------------------------------------------------------- + * + * Write a debug message, only shown if irccd is compiled in debug. + * + * Arguments: + * - message, the message. + * Throws: + * - Irccd.SystemError on errors + */ +auto Logger_debug(duk_context* ctx) -> duk_ret_t +{ + return print(ctx, 0); +} + +// }}} + +const duk_function_list_entry functions[] = { + { "info", Logger_info, 1 }, + { "warning", Logger_warning, 1 }, + { "debug", Logger_debug, 1 }, + { nullptr, nullptr, 0 } +}; + +} // !namespace + +auto logger_api::get_name() const noexcept -> std::string_view +{ + return "Irccd.Logger"; +} + +void logger_api::load(bot&, std::shared_ptr<plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + duk_get_global_string(plugin->get_context(), "Irccd"); + duk_push_object(plugin->get_context()); + duk_put_function_list(plugin->get_context(), -1, functions); + duk_put_prop_string(plugin->get_context(), -2, "Logger"); + duk_pop(plugin->get_context()); +} + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/logger_api.hpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,50 @@ +/* + * logger_api.hpp -- Irccd.Logger API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_LOGGER_API_HPP +#define IRCCD_JS_LOGGER_API_HPP + +/** + * \file logger_api.hpp + * \brief Irccd.Logger Javascript API. + */ + +#include "api.hpp" + +namespace irccd::js { + +/** + * \ingroup js-api + * \brief irccd.Logger Javascript API. + */ +class logger_api : public api { +public: + /** + * \copydoc api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc api::load + */ + void load(daemon::bot& bot, std::shared_ptr<plugin> plugin) override; +}; + +} // !irccd::js + +#endif // !IRCCD_JS_LOGGER_API_HPP
--- a/libirccd-js/irccd/js/logger_js_api.cpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,150 +0,0 @@ -/* - * logger_js_api.cpp -- Irccd.Logger API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <irccd/daemon/bot.hpp> -#include <irccd/daemon/logger.hpp> -#include <irccd/daemon/plugin_service.hpp> - -#include "irccd_js_api.hpp" -#include "js_plugin.hpp" -#include "logger_js_api.hpp" -#include "plugin_js_api.hpp" - -using irccd::daemon::bot; -using irccd::daemon::plugin; - -namespace irccd::js { - -namespace { - -// {{{ print - -auto print(duk_context* ctx, unsigned level) -> duk_ret_t -{ - assert(level <= 2); - - try { - auto& sink = duk::type_traits<bot>::self(ctx).get_log(); - auto& self = duk::type_traits<js_plugin>::self(ctx); - - switch (level) { - case 0: - sink.debug<plugin>(self) << duk_require_string(ctx, 0) << std::endl; - break; - case 1: - sink.info<plugin>(self) << duk_require_string(ctx, 0) << std::endl; - break; - default: - sink.warning<plugin>(self) << duk_require_string(ctx, 0) << std::endl; - break; - } - } catch (const std::exception& ex) { - duk::raise(ctx, ex); - } - - return 0; -} - -// }}} - -// {{{ Irccd.Logger.info - -/* - * Function: Irccd.Logger.info(message) - * -------------------------------------------------------- - * - * Write a verbose message. - * - * Arguments: - * - message, the message. - * Throws: - * - Irccd.SystemError on errors - */ -auto Logger_info(duk_context* ctx) -> duk_ret_t -{ - return print(ctx, 1); -} - -// }}} - -// {{{ Irccd.Logger.warning - -/* - * Function: Irccd.Logger.warning(message) - * -------------------------------------------------------- - * - * Write a warning message. - * - * Arguments: - * - message, the warning. - * Throws: - * - Irccd.SystemError on errors - */ -auto Logger_warning(duk_context* ctx) -> duk_ret_t -{ - return print(ctx, 2); -} - -// }}} - -// {{{ Irccd.Logger.debug - -/* - * Function: Irccd.Logger.debug(message) - * -------------------------------------------------------- - * - * Write a debug message, only shown if irccd is compiled in debug. - * - * Arguments: - * - message, the message. - * Throws: - * - Irccd.SystemError on errors - */ -auto Logger_debug(duk_context* ctx) -> duk_ret_t -{ - return print(ctx, 0); -} - -// }}} - -const duk_function_list_entry functions[] = { - { "info", Logger_info, 1 }, - { "warning", Logger_warning, 1 }, - { "debug", Logger_debug, 1 }, - { nullptr, nullptr, 0 } -}; - -} // !namespace - -auto logger_js_api::get_name() const noexcept -> std::string_view -{ - return "Irccd.Logger"; -} - -void logger_js_api::load(bot&, std::shared_ptr<js_plugin> plugin) -{ - duk::stack_guard sa(plugin->get_context()); - - duk_get_global_string(plugin->get_context(), "Irccd"); - duk_push_object(plugin->get_context()); - duk_put_function_list(plugin->get_context(), -1, functions); - duk_put_prop_string(plugin->get_context(), -2, "Logger"); - duk_pop(plugin->get_context()); -} - -} // !irccd::js
--- a/libirccd-js/irccd/js/logger_js_api.hpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * logger_js_api.hpp -- Irccd.Logger API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_LOGGER_JS_API_HPP -#define IRCCD_JS_LOGGER_JS_API_HPP - -/** - * \file logger_js_api.hpp - * \brief Irccd.Logger Javascript API. - */ - -#include "js_api.hpp" - -namespace irccd::js { - -/** - * \ingroup js-api - * \brief irccd.Logger Javascript API. - */ -class logger_js_api : public js_api { -public: - /** - * \copydoc js_api::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc js_api::load - */ - void load(daemon::bot& bot, std::shared_ptr<js_plugin> plugin) override; -}; - -} // !irccd::js - -#endif // !IRCCD_JS_LOGGER_JS_API_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/plugin.cpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,382 @@ +/* + * plugin.cpp -- Javascript plugins for irccd + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <cstring> +#include <cerrno> +#include <fstream> +#include <iterator> +#include <stdexcept> + +#include <irccd/daemon/bot.hpp> + +#include "api.hpp" +#include "plugin.hpp" +#include "server_api.hpp" + +using irccd::daemon::bot; +using irccd::daemon::connect_event; +using irccd::daemon::disconnect_event; +using irccd::daemon::invite_event; +using irccd::daemon::join_event; +using irccd::daemon::kick_event; +using irccd::daemon::me_event; +using irccd::daemon::message_event; +using irccd::daemon::mode_event; +using irccd::daemon::names_event; +using irccd::daemon::nick_event; +using irccd::daemon::notice_event; +using irccd::daemon::part_event; +using irccd::daemon::plugin; +using irccd::daemon::plugin_error; +using irccd::daemon::topic_event; +using irccd::daemon::whois_event; +using irccd::daemon::whois_info; + +namespace irccd::js { + +namespace { + +auto get_metadata(duk::context& ctx, std::string_view name) -> std::string_view +{ + std::string_view ret("unknown"); + + duk::stack_guard guard(ctx); + duk_get_global_string(ctx, "info"); + + if (duk_get_type(ctx, -1) == DUK_TYPE_OBJECT) { + duk_get_prop_string(ctx, -1, name.data()); + + if (duk_get_type(ctx, -1) == DUK_TYPE_STRING) + ret = duk_get_string(ctx, -1); + + duk_pop(ctx); + } + + duk_pop(ctx); + + return ret; +} + +auto get_table(duk::context& ctx, std::string_view name) -> plugin::map +{ + plugin::map result; + + duk::stack_guard sa(ctx); + duk_get_global_string(ctx, name.data()); + duk_enum(ctx, -1, 0); + + while (duk_next(ctx, -1, true)) { + result.emplace(duk_to_string(ctx, -2), duk_to_string(ctx, -1)); + duk_pop_n(ctx, 2); + } + + duk_pop_n(ctx, 2); + + return result; +} + +void set_table(duk::context& ctx, std::string_view name, const plugin::map& vars) +{ + duk::stack_guard sa(ctx); + duk_get_global_string(ctx, name.data()); + + for (const auto& pair : vars) { + duk::push(ctx, pair.second); + duk_put_prop_string(ctx, -2, pair.first.c_str()); + } + + duk_pop(ctx); +} + +} // !namespace + +void plugin::push() noexcept +{ +} + +template <typename Value, typename... Args> +void plugin::push(Value&& value, Args&&... args) +{ + duk::push(context_, std::forward<Value>(value)); + push(std::forward<Args>(args)...); +} + +template <typename... Args> +void plugin::call(const std::string& func, Args&&... args) +{ + duk::stack_guard sa(context_); + + duk_get_global_string(context_, func.c_str()); + + if (duk_get_type(context_, -1) == DUK_TYPE_UNDEFINED) { + duk_pop(context_); + return; + } + + push(std::forward<Args>(args)...); + + if (duk_pcall(context_, sizeof... (Args)) != 0) + throw plugin_error(plugin_error::exec_error, get_name(), duk::get_stack(context_, -1).get_stack()); + + duk_pop(context_); +} + +plugin::plugin(std::string id, std::string path) + : daemon::plugin(std::move(id)) + , path_(path) +{ + duk::stack_guard sa(context_); + + /* + * Create two special tables for configuration and formats, they are + * referenced later as + * + * - Irccd.Plugin.config + * - Irccd.Plugin.format + * - Irccd.Plugin.paths + * + * In plugin_module.cpp. + */ + duk_push_object(context_); + duk_put_global_string(context_, config_property.data()); + duk_push_object(context_); + duk_put_global_string(context_, format_property.data()); + duk_push_object(context_); + duk_put_global_string(context_, paths_property.data()); + + duk_push_pointer(context_, this); + duk_put_global_string(context_, DUK_HIDDEN_SYMBOL("plugin")); + duk::push(context_, path); + duk_put_global_string(context_, DUK_HIDDEN_SYMBOL("path")); +} + +auto plugin::get_context() noexcept -> duk::context& +{ + return context_; +} + +auto plugin::get_name() const noexcept -> std::string_view +{ + return get_metadata(context_, "name"); +} + +auto plugin::get_author() const noexcept -> std::string_view +{ + return get_metadata(context_, "author"); +} + +auto plugin::get_license() const noexcept -> std::string_view +{ + return get_metadata(context_, "license"); +} + +auto plugin::get_summary() const noexcept -> std::string_view +{ + return get_metadata(context_, "summary"); +} + +auto plugin::get_version() const noexcept -> std::string_view +{ + return get_metadata(context_, "version"); +} + +auto plugin::get_options() const -> map +{ + return get_table(context_, config_property); +} + +void plugin::set_options(const map& map) +{ + set_table(context_, config_property, map); +} + +auto plugin::get_formats() const -> map +{ + return get_table(context_, format_property); +} + +void plugin::set_formats(const map& map) +{ + set_table(context_, format_property, map); +} + +auto plugin::get_paths() const -> map +{ + return get_table(context_, paths_property); +} + +void plugin::set_paths(const map& map) +{ + set_table(context_, paths_property, map); +} + +void plugin::open() +{ + std::ifstream input(path_); + + if (!input) + throw plugin_error(plugin_error::exec_error, get_name(), std::strerror(errno)); + + std::string data( + std::istreambuf_iterator<char>(input.rdbuf()), + std::istreambuf_iterator<char>() + ); + + if (duk_peval_string(context_, data.c_str())) + throw plugin_error(plugin_error::exec_error, get_name(), duk::get_stack(context_, -1).get_stack()); +} + +void plugin::handle_command(bot&, const message_event& event) +{ + call("onCommand", event.server, event.origin, event.channel, event.message); +} + +void plugin::handle_connect(bot&, const connect_event& event) +{ + call("onConnect", event.server); +} + +void plugin::handle_disconnect(bot&, const disconnect_event& event) +{ + call("onDisconnect", event.server); +} + +void plugin::handle_invite(bot&, const invite_event& event) +{ + call("onInvite", event.server, event.origin, event.channel); +} + +void plugin::handle_join(bot&, const join_event& event) +{ + call("onJoin", event.server, event.origin, event.channel); +} + +void plugin::handle_kick(bot&, const kick_event& event) +{ + call("onKick", event.server, event.origin, event.channel, event.target, event.reason); +} + +void plugin::handle_load(bot&) +{ + call("onLoad"); +} + +void plugin::handle_message(bot&, const message_event& event) +{ + call("onMessage", event.server, event.origin, event.channel, event.message); +} + +void plugin::handle_me(bot&, const me_event& event) +{ + call("onMe", event.server, event.origin, event.channel, event.message); +} + +void plugin::handle_mode(bot&, const mode_event& event) +{ + call("onMode", event.server, event.origin, event.channel, event.mode, + event.limit, event.user, event.mask); +} + +void plugin::handle_names(bot&, const names_event& event) +{ + call("onNames", event.server, event.channel, event.names); +} + +void plugin::handle_nick(bot&, const nick_event& event) +{ + call("onNick", event.server, event.origin, event.nickname); +} + +void plugin::handle_notice(bot&, const notice_event& event) +{ + call("onNotice", event.server, event.origin, event.channel, event.message); +} + +void plugin::handle_part(bot&, const part_event& event) +{ + call("onPart", event.server, event.origin, event.channel, event.reason); +} + +void plugin::handle_reload(bot&) +{ + call("onReload"); +} + +void plugin::handle_topic(bot&, const topic_event& event) +{ + call("onTopic", event.server, event.origin, event.channel, event.topic); +} + +void plugin::handle_unload(bot&) +{ + call("onUnload"); +} + +void plugin::handle_whois(bot&, const whois_event& event) +{ + call("onWhois", event.server, event.whois); +} + +plugin_loader::plugin_loader(bot& bot, + std::vector<std::string> directories, + std::vector<std::string> extensions) noexcept + : daemon::plugin_loader(std::move(directories), std::move(extensions)) + , bot_(bot) +{ +} + +plugin_loader::~plugin_loader() noexcept = default; + +auto plugin_loader::get_modules() const noexcept -> const modules& +{ + return modules_; +} + +auto plugin_loader::get_modules() noexcept -> modules& +{ + return modules_; +} + +auto plugin_loader::open(std::string_view id, std::string_view path) -> std::shared_ptr<daemon::plugin> +{ + auto plg = std::make_shared<plugin>(std::string(id), std::string(path)); + + for (const auto& mod : modules_) + mod->load(bot_, plg); + + plg->open(); + + return plg; +} + +void duk::type_traits<whois_info>::push(duk_context* ctx, const whois_info& whois) +{ + duk_push_object(ctx); + duk::push(ctx, whois.nick); + duk_put_prop_string(ctx, -2, "nickname"); + duk::push(ctx, whois.user); + duk_put_prop_string(ctx, -2, "username"); + duk::push(ctx, whois.realname); + duk_put_prop_string(ctx, -2, "realname"); + duk::push(ctx, whois.hostname); + duk_put_prop_string(ctx, -2, "hostname"); + duk::push(ctx, whois.channels); + duk_put_prop_string(ctx, -2, "channels"); +} + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/plugin.hpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,315 @@ +/* + * plugin.hpp -- JavaScript plugins for irccd + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_PLUGIN_HPP +#define IRCCD_JS_PLUGIN_HPP + +/** + * \file plugin.hpp + * \brief JavaScript plugins for irccd. + */ + +#include <irccd/daemon/plugin.hpp> +#include <irccd/daemon/server.hpp> + +#include "duk.hpp" + +/** + * \brief Javascript namespace + */ +namespace irccd::js { + +class api; + +/** + * \ingroup js + * \ingroup daemon-plugins + * \brief JavaScript plugins for irccd. + */ +class plugin : public daemon::plugin { +public: + /** + * Global property where to read/write plugin configuration (object). + */ + static inline const std::string_view config_property{DUK_HIDDEN_SYMBOL("config")}; + + /** + * Global property where to read/write plugin formats (object). + */ + static inline const std::string_view format_property{DUK_HIDDEN_SYMBOL("formats")}; + + /** + * Global property where paths are defined (object). + */ + static inline const std::string_view paths_property{DUK_HIDDEN_SYMBOL("paths")}; + +private: + // JavaScript context. + mutable duk::context context_; + + // Path to Javascript script file. + std::string path_; + + void push() noexcept; + + template <typename Value, typename... Args> + void push(Value&& value, Args&&... args); + + template <typename... Args> + void call(const std::string&, Args&&... args); + +public: + /** + * Constructor. + * + * \param id the plugin id + * \param path the path to the plugin + */ + plugin(std::string id, std::string path); + + /** + * Access the Duktape context. + * + * \return the context + */ + auto get_context() noexcept -> duk::context&; + + /** + * Open the script file associated. + */ + void open(); + + /** + * \copydoc daemon::plugin::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc daemon::plugin::get_author + */ + auto get_author() const noexcept -> std::string_view override; + + /** + * \copydoc daemon::plugin::get_license + */ + auto get_license() const noexcept -> std::string_view override; + + /** + * \copydoc daemon::plugin::get_summary + */ + auto get_summary() const noexcept -> std::string_view override; + + /** + * \copydoc daemon::plugin::get_version + */ + auto get_version() const noexcept -> std::string_view override; + + /** + * \copydoc daemon::plugin::get_options + */ + auto get_options() const -> map override; + + /** + * \copydoc daemon::plugin::set_options + */ + void set_options(const map& map) override; + + /** + * \copydoc daemon::plugin::get_formats + */ + auto get_formats() const -> map override; + + /** + * \copydoc daemon::plugin::set_formats + */ + void set_formats(const map& map) override; + + /** + * \copydoc daemon::plugin::get_paths + */ + auto get_paths() const -> map override; + + /** + * \copydoc daemon::plugin::set_paths + */ + void set_paths(const map& map) override; + + /** + * \copydoc daemon::plugin::handle_command + */ + void handle_command(daemon::bot& bot, const daemon::message_event& event) override; + + /** + * \copydoc daemon::plugin::handle_connect + */ + void handle_connect(daemon::bot& bot, const daemon::connect_event& event) override; + + /** + * \copydoc daemon::plugin::handle_disconnect + */ + void handle_disconnect(daemon::bot& bot, const daemon::disconnect_event& event) override; + + /** + * \copydoc daemon::plugin::handle_invite + */ + void handle_invite(daemon::bot& bot, const daemon::invite_event& event) override; + + /** + * \copydoc daemon::plugin::handle_join + */ + void handle_join(daemon::bot& bot, const daemon::join_event& event) override; + + /** + * \copydoc daemon::plugin::handle_kick + */ + void handle_kick(daemon::bot& bot, const daemon::kick_event& event) override; + + /** + * \copydoc daemon::plugin::handle_load + */ + void handle_load(daemon::bot& bot) override; + + /** + * \copydoc daemon::plugin::handle_message + */ + void handle_message(daemon::bot& bot, const daemon::message_event& event) override; + + /** + * \copydoc daemon::plugin::handle_me + */ + void handle_me(daemon::bot& bot, const daemon::me_event& event) override; + + /** + * \copydoc daemon::plugin::handle_mode + */ + void handle_mode(daemon::bot& bot, const daemon::mode_event& event) override; + + /** + * \copydoc daemon::plugin::handle_names + */ + void handle_names(daemon::bot& bot, const daemon::names_event& event) override; + + /** + * \copydoc daemon::plugin::handle_nick + */ + void handle_nick(daemon::bot& bot, const daemon::nick_event& event) override; + + /** + * \copydoc daemon::plugin::handle_notice + */ + void handle_notice(daemon::bot& bot, const daemon::notice_event& event) override; + + /** + * \copydoc daemon::plugin::handle_part + */ + void handle_part(daemon::bot& bot, const daemon::part_event& event) override; + + /** + * \copydoc daemon::plugin::handle_reload + */ + void handle_reload(daemon::bot& bot) override; + + /** + * \copydoc daemon::plugin::handle_topic + */ + void handle_topic(daemon::bot& bot, const daemon::topic_event& event) override; + + /** + * \copydoc daemon::plugin::handle_unload + */ + void handle_unload(daemon::bot& bot) override; + + /** + * \copydoc daemon::plugin::handle_whois + */ + void handle_whois(daemon::bot& bot, const daemon::whois_event& event) override; +}; + +/** + * \ingroup plugins + * \brief Implementation for searching Javascript plugins. + */ +class plugin_loader : public daemon::plugin_loader { +public: + /** + * \brief The list of Javascript API modules. + */ + using modules = std::vector<std::unique_ptr<api>>; + +private: + daemon::bot& bot_; + modules modules_; + +public: + /** + * Constructor. + * + * \param bot the irccd instance + * \param directories directories to search + * \param extensions extensions to search + */ + plugin_loader(daemon::bot& bot, + std::vector<std::string> directories = {}, + std::vector<std::string> extensions = {".js"}) noexcept; + + /** + * Destructor defaulted. + */ + ~plugin_loader() noexcept; + + /** + * Get the list of modules. + * + * \return the modules + */ + auto get_modules() const noexcept -> const modules&; + + /** + * Overloaded function. + * + * \return the modules + */ + auto get_modules() noexcept -> modules&; + + /** + * \copydoc daemon::plugin_loader::open + */ + auto open(std::string_view id, std::string_view file) -> std::shared_ptr<daemon::plugin>; +}; + +namespace duk { + +/** + * \brief Specialization for type_traits<whois_info> + */ +template <> +struct type_traits<daemon::whois_info> : public std::true_type { + /** + * Push a whois_info. + * + * \param ctx the Duktape context + * \param who the information + */ + static void push(duk_context* ctx, const daemon::whois_info& who); +}; + +} // !duk + +} // !irccd::js + +#endif // !IRCCD_PLUGIN_JS_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/plugin_api.cpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,461 @@ +/* + * plugin_api.cpp -- Irccd.Plugin API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <irccd/daemon/bot.hpp> +#include <irccd/daemon/plugin_service.hpp> + +#include "irccd_api.hpp" +#include "plugin.hpp" +#include "plugin_api.hpp" + +using irccd::daemon::bot; +using irccd::daemon::plugin_error; + +namespace irccd::js { + +namespace { + +const std::string_view signature(DUK_HIDDEN_SYMBOL("Irccd.Plugin")); + +template <typename Handler> +auto wrap(duk_context* ctx, Handler handler) -> duk_idx_t +{ + try { + return handler(); + } catch (const plugin_error& ex) { + duk::raise(ctx, ex); + } catch (const std::system_error& ex) { + duk::raise(ctx, ex); + } catch (const std::exception& ex) { + duk::raise(ctx, ex); + } + + return 0; +} + +/* + * set + * ------------------------------------------------------------------ + * + * This setter is used to replace the Irccd.Plugin.(config|format|paths) + * property when the plugin assign a new one. + * + * Because the plugin configuration always has higher priority, when a new + * object is assigned to 'config' or to the 'format' property, the plugin + * configuration is merged to the assigned one, adding or replacing any values. + * + * Example: + * + * Plugin 'xyz' does: + * + * Irccd.Plugin.config = { + * mode: "simple", + * level: "123" + * }; + * + * The user configuration is: + * + * [plugin.xyz] + * mode = "hard" + * path = "/var" + * + * The final user table looks like this: + * + * Irccd.Plugin.Config = { + * mode: "hard", + * level: "123", + * path: "/var" + * }; + */ +auto set(duk_context* ctx, std::string_view name) -> duk_ret_t +{ + if (!duk_is_object(ctx, 0)) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "'%s' property must be object", name.data()); + + // Merge old table with new one. + duk_get_global_string(ctx, name.data()); + duk_enum(ctx, -1, 0); + + while (duk_next(ctx, -1, true)) + duk_put_prop(ctx, 0); + + // Pop enum and old table. + duk_pop_2(ctx); + + // Replace the old table with the new assigned one. + duk_put_global_string(ctx, name.data()); + + return 0; +} + +/* + * get + * ------------------------------------------------------------------ + * + * Get the Irccd.Plugin.(config|format|paths) property. + */ +auto get(duk_context* ctx, std::string_view name) -> duk_ret_t +{ + duk_get_global_string(ctx, name.data()); + + return 1; +} + +/* + * set_config + * ------------------------------------------------------------------ + * + * Wrap setter for Irccd.Plugin.config property. + */ +auto set_config(duk_context* ctx) -> duk_ret_t +{ + return set(ctx, plugin::config_property); +} + +/* + * get_config + * ------------------------------------------------------------------ + * + * Wrap getter for Irccd.Plugin.config property. + */ +auto get_config(duk_context* ctx) -> duk_ret_t +{ + return get(ctx, plugin::config_property); +} + +/* + * set_format + * ------------------------------------------------------------------ + * + * Wrap setter for Irccd.Plugin.format property. + */ +auto set_format(duk_context* ctx) -> duk_ret_t +{ + return set(ctx, plugin::format_property); +} + +/* + * get_format + * ------------------------------------------------------------------ + * + * Wrap getter for Irccd.Plugin.format property. + */ +auto get_format(duk_context* ctx) -> duk_ret_t +{ + return get(ctx, plugin::format_property); +} + +/* + * set_paths + * ------------------------------------------------------------------ + * + * Wrap setter for Irccd.Plugin.format property. + */ +auto set_paths(duk_context* ctx) -> duk_ret_t +{ + return set(ctx, plugin::paths_property); +} + +/* + * get_paths + * ------------------------------------------------------------------ + * + * Wrap getter for Irccd.Plugin.format property. + */ +auto get_paths(duk_context* ctx) -> duk_ret_t +{ + return get(ctx, plugin::paths_property); +} + +// {{{ Irccd.Plugin.info + +/* + * Function: Irccd.Plugin.info([name]) + * ------------------------------------------------------------------ + * + * Get information about a plugin. + * + * The returned object as the following properties: + * + * - name: (string) the plugin identifier, + * - author: (string) the author, + * - license: (string) the license, + * - summary: (string) a short description, + * - version: (string) the version + * + * Arguments: + * - name, the plugin identifier, if not specified the current plugin is + * selected. + * Returns: + * The plugin information or undefined if the plugin was not found. + * Throws: + * - Irccd.SystemError on errors. + */ +auto Plugin_info(duk_context* ctx) -> duk_idx_t +{ + return wrap(ctx, [&] { + daemon::plugin* plg; + + if (duk_get_top(ctx) >= 1) + plg = duk::type_traits<bot>::self(ctx).plugins().get(duk_require_string(ctx, 0)).get(); + else + plg = std::addressof(duk::type_traits<plugin>::self(ctx)); + + if (!plg) + return 0; + + duk_push_object(ctx); + duk::push(ctx, plg->get_name()); + duk_put_prop_string(ctx, -2, "name"); + duk::push(ctx, plg->get_author()); + duk_put_prop_string(ctx, -2, "author"); + duk::push(ctx, plg->get_license()); + duk_put_prop_string(ctx, -2, "license"); + duk::push(ctx, plg->get_summary()); + duk_put_prop_string(ctx, -2, "summary"); + duk::push(ctx, plg->get_version()); + duk_put_prop_string(ctx, -2, "version"); + + return 1; + }); +} + +// }}} + +// {{{ Irccd.Plugin.list + +/* + * Function: Irccd.Plugin.list() + * ------------------------------------------------------------------ + * + * Get the list of plugins, the array returned contains all plugin names. + * + * Returns: + * The list of all plugin names. + */ +auto Plugin_list(duk_context* ctx) -> duk_idx_t +{ + int i = 0; + + duk_push_array(ctx); + + for (const auto& plg : duk::type_traits<bot>::self(ctx).plugins().list()) { + duk::push(ctx, plg->get_id()); + duk_put_prop_index(ctx, -2, i++); + } + + return 1; +} + +// }}} + +// {{{ Irccd.Plugin.load + +/* + * Function: Irccd.Plugin.load(name) + * ------------------------------------------------------------------ + * + * Load a plugin by name. This function will search through the standard + * directories. + * + * Arguments: + * - name, the plugin identifier. + * Throws: + * - Irccd.PluginError on plugin related errors, + * - Irccd.SystemError on other errors. + */ +auto Plugin_load(duk_context* ctx) -> duk_idx_t +{ + return wrap(ctx, [&] { + duk::type_traits<bot>::self(ctx).plugins().load( + duk::require<std::string_view>(ctx, 0), ""); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Plugin.reload + +/* + * Function: Irccd.Plugin.reload(name) + * ------------------------------------------------------------------ + * + * Reload a plugin by name. + * + * Arguments: + * - name, the plugin identifier. + * Throws: + * - Irccd.PluginError on plugin related errors, + * - Irccd.SystemError on other errors. + */ +auto Plugin_reload(duk_context* ctx) -> duk_idx_t +{ + return wrap(ctx, [&] { + duk::type_traits<bot>::self(ctx).plugins().reload(duk::require<std::string>(ctx, 0)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Plugin.unload + +/* + * Function: Irccd.Plugin.unload(name) + * ------------------------------------------------------------------ + * + * Unload a plugin by name. + * + * Arguments: + * - name, the plugin identifier. + * Throws: + * - Irccd.PluginError on plugin related errors, + * - Irccd.SystemError on other errors. + */ +auto Plugin_unload(duk_context* ctx) -> duk_idx_t +{ + return wrap(ctx, [&] { + duk::type_traits<bot>::self(ctx).plugins().unload(duk::require<std::string>(ctx, 0)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.PluginError [constructor] + +/* + * Function: Irccd.PluginError(code, message) + * ------------------------------------------------------------------ + * + * Create an Irccd.PluginError object. + * + * Arguments: + * - code, the error code, + * - message, the error message. + */ +auto PluginError_constructor(duk_context* ctx) -> duk_ret_t +{ + duk_push_this(ctx); + duk_push_int(ctx, duk_require_int(ctx, 0)); + duk_put_prop_string(ctx, -2, "code"); + duk_push_string(ctx, duk_require_string(ctx, 1)); + duk_put_prop_string(ctx, -2, "message"); + duk_push_string(ctx, "PluginError"); + duk_put_prop_string(ctx, -2, "name"); + duk_pop(ctx); + + return 0; +} + +// }}} + +const duk_function_list_entry functions[] = { + { "info", Plugin_info, DUK_VARARGS }, + { "list", Plugin_list, 0 }, + { "load", Plugin_load, 1 }, + { "reload", Plugin_reload, 1 }, + { "unload", Plugin_unload, 1 }, + { nullptr, nullptr, 0 } +}; + +} // !namespace + +auto plugin_api::get_name() const noexcept -> std::string_view +{ + return "Irccd.Plugin"; +} + +void plugin_api::load(bot&, std::shared_ptr<plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + // Store plugin. + duk_push_pointer(plugin->get_context(), plugin.get()); + duk_put_global_string(plugin->get_context(), signature.data()); + + duk_get_global_string(plugin->get_context(), "Irccd"); + duk_push_object(plugin->get_context()); + duk_put_function_list(plugin->get_context(), -1, functions); + + // 'config' property. + duk_push_string(plugin->get_context(), "config"); + duk_push_c_function(plugin->get_context(), get_config, 0); + duk_push_c_function(plugin->get_context(), set_config, 1); + duk_def_prop(plugin->get_context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER); + + // 'format' property. + duk_push_string(plugin->get_context(), "format"); + duk_push_c_function(plugin->get_context(), get_format, 0); + duk_push_c_function(plugin->get_context(), set_format, 1); + duk_def_prop(plugin->get_context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER); + + // 'format' property. + duk_push_string(plugin->get_context(), "paths"); + duk_push_c_function(plugin->get_context(), get_paths, 0); + duk_push_c_function(plugin->get_context(), set_paths, 1); + duk_def_prop(plugin->get_context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER); + + // PluginError function. + duk_push_c_function(plugin->get_context(), PluginError_constructor, 2); + duk_push_object(plugin->get_context()); + duk_get_global_string(plugin->get_context(), "Error"); + duk_get_prop_string(plugin->get_context(), -1, "prototype"); + duk_remove(plugin->get_context(), -2); + duk_set_prototype(plugin->get_context(), -2); + duk_put_prop_string(plugin->get_context(), -2, "prototype"); + duk_put_prop_string(plugin->get_context(), -2, "PluginError"); + + duk_put_prop_string(plugin->get_context(), -2, "Plugin"); + duk_pop(plugin->get_context()); +} + +namespace duk { + +auto type_traits<plugin>::self(duk_context* ctx) -> plugin& +{ + duk::stack_guard sa(ctx); + + duk_get_global_string(ctx, signature.data()); + auto plg = static_cast<plugin*>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + + return *plg; +} + +void type_traits<plugin_error>::raise(duk_context* ctx, const plugin_error& ex) +{ + duk::stack_guard sa(ctx, 1); + + duk_get_global_string(ctx, "Irccd"); + duk_get_prop_string(ctx, -1, "PluginError"); + duk_remove(ctx, -2); + duk::push(ctx, ex.code().value()); + duk::push(ctx, ex.code().message()); + duk_new(ctx, 2); + + (void)duk_throw(ctx); +} + +} // !duk + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/plugin_api.hpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,83 @@ +/* + * plugin_api.hpp -- Irccd.Plugin API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_PLUGIN_API_HPP +#define IRCCD_JS_PLUGIN_API_HPP + +/** + * \file plugin_api.hpp + * \brief Irccd.Plugin Javascript API. + */ + +#include "api.hpp" +#include "plugin.hpp" + +namespace irccd::js { + +/** + * \ingroup js-api + * \brief Irccd.Plugin Javascript API. + */ +class plugin_api : public api { +public: + /** + * \copydoc api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc api::load + */ + void load(daemon::bot& bot, std::shared_ptr<plugin> plugin) override; +}; + +namespace duk { + +/** + * \brief Specialize dukx_type_traits for plugin. + */ +template <> +struct type_traits<plugin> { + /** + * Access the plugin stored in this context. + * + * \param ctx the context + * \return the plugin + */ + static auto self(duk_context* ctx) -> plugin&; +}; + +/** + * \brief Specialization for plugin_error. + */ +template <> +struct type_traits<daemon::plugin_error> { + /** + * Raise a plugin_error. + * + * \param ctx the context + * \param error the error + */ + static void raise(duk_context* ctx, const daemon::plugin_error& error); +}; + +} // !duk + +} // !irccd::js + +#endif // !IRCCD_JS_PLUGIN_API_HPP
--- a/libirccd-js/irccd/js/plugin_js_api.cpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,462 +0,0 @@ -/* - * plugin_js_api.cpp -- Irccd.Plugin API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <irccd/daemon/bot.hpp> -#include <irccd/daemon/plugin_service.hpp> - -#include "irccd_js_api.hpp" -#include "js_plugin.hpp" -#include "plugin_js_api.hpp" - -using irccd::daemon::bot; -using irccd::daemon::plugin_error; -using irccd::daemon::plugin; - -namespace irccd::js { - -namespace { - -const std::string_view signature(DUK_HIDDEN_SYMBOL("Irccd.Plugin")); - -template <typename Handler> -auto wrap(duk_context* ctx, Handler handler) -> duk_idx_t -{ - try { - return handler(); - } catch (const plugin_error& ex) { - duk::raise(ctx, ex); - } catch (const std::system_error& ex) { - duk::raise(ctx, ex); - } catch (const std::exception& ex) { - duk::raise(ctx, ex); - } - - return 0; -} - -/* - * set - * ------------------------------------------------------------------ - * - * This setter is used to replace the Irccd.Plugin.(config|format|paths) - * property when the plugin assign a new one. - * - * Because the plugin configuration always has higher priority, when a new - * object is assigned to 'config' or to the 'format' property, the plugin - * configuration is merged to the assigned one, adding or replacing any values. - * - * Example: - * - * Plugin 'xyz' does: - * - * Irccd.Plugin.config = { - * mode: "simple", - * level: "123" - * }; - * - * The user configuration is: - * - * [plugin.xyz] - * mode = "hard" - * path = "/var" - * - * The final user table looks like this: - * - * Irccd.Plugin.Config = { - * mode: "hard", - * level: "123", - * path: "/var" - * }; - */ -auto set(duk_context* ctx, std::string_view name) -> duk_ret_t -{ - if (!duk_is_object(ctx, 0)) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "'%s' property must be object", name.data()); - - // Merge old table with new one. - duk_get_global_string(ctx, name.data()); - duk_enum(ctx, -1, 0); - - while (duk_next(ctx, -1, true)) - duk_put_prop(ctx, 0); - - // Pop enum and old table. - duk_pop_2(ctx); - - // Replace the old table with the new assigned one. - duk_put_global_string(ctx, name.data()); - - return 0; -} - -/* - * get - * ------------------------------------------------------------------ - * - * Get the Irccd.Plugin.(config|format|paths) property. - */ -auto get(duk_context* ctx, std::string_view name) -> duk_ret_t -{ - duk_get_global_string(ctx, name.data()); - - return 1; -} - -/* - * set_config - * ------------------------------------------------------------------ - * - * Wrap setter for Irccd.Plugin.config property. - */ -auto set_config(duk_context* ctx) -> duk_ret_t -{ - return set(ctx, js_plugin::config_property); -} - -/* - * get_config - * ------------------------------------------------------------------ - * - * Wrap getter for Irccd.Plugin.config property. - */ -auto get_config(duk_context* ctx) -> duk_ret_t -{ - return get(ctx, js_plugin::config_property); -} - -/* - * set_format - * ------------------------------------------------------------------ - * - * Wrap setter for Irccd.Plugin.format property. - */ -auto set_format(duk_context* ctx) -> duk_ret_t -{ - return set(ctx, js_plugin::format_property); -} - -/* - * get_format - * ------------------------------------------------------------------ - * - * Wrap getter for Irccd.Plugin.format property. - */ -auto get_format(duk_context* ctx) -> duk_ret_t -{ - return get(ctx, js_plugin::format_property); -} - -/* - * set_paths - * ------------------------------------------------------------------ - * - * Wrap setter for Irccd.Plugin.format property. - */ -auto set_paths(duk_context* ctx) -> duk_ret_t -{ - return set(ctx, js_plugin::paths_property); -} - -/* - * get_paths - * ------------------------------------------------------------------ - * - * Wrap getter for Irccd.Plugin.format property. - */ -auto get_paths(duk_context* ctx) -> duk_ret_t -{ - return get(ctx, js_plugin::paths_property); -} - -// {{{ Irccd.Plugin.info - -/* - * Function: Irccd.Plugin.info([name]) - * ------------------------------------------------------------------ - * - * Get information about a plugin. - * - * The returned object as the following properties: - * - * - name: (string) the plugin identifier, - * - author: (string) the author, - * - license: (string) the license, - * - summary: (string) a short description, - * - version: (string) the version - * - * Arguments: - * - name, the plugin identifier, if not specified the current plugin is - * selected. - * Returns: - * The plugin information or undefined if the plugin was not found. - * Throws: - * - Irccd.SystemError on errors. - */ -auto Plugin_info(duk_context* ctx) -> duk_idx_t -{ - return wrap(ctx, [&] { - plugin* plugin; - - if (duk_get_top(ctx) >= 1) - plugin = duk::type_traits<bot>::self(ctx).plugins().get(duk_require_string(ctx, 0)).get(); - else - plugin = std::addressof(duk::type_traits<js_plugin>::self(ctx)); - - if (!plugin) - return 0; - - duk_push_object(ctx); - duk::push(ctx, plugin->get_name()); - duk_put_prop_string(ctx, -2, "name"); - duk::push(ctx, plugin->get_author()); - duk_put_prop_string(ctx, -2, "author"); - duk::push(ctx, plugin->get_license()); - duk_put_prop_string(ctx, -2, "license"); - duk::push(ctx, plugin->get_summary()); - duk_put_prop_string(ctx, -2, "summary"); - duk::push(ctx, plugin->get_version()); - duk_put_prop_string(ctx, -2, "version"); - - return 1; - }); -} - -// }}} - -// {{{ Irccd.Plugin.list - -/* - * Function: Irccd.Plugin.list() - * ------------------------------------------------------------------ - * - * Get the list of plugins, the array returned contains all plugin names. - * - * Returns: - * The list of all plugin names. - */ -auto Plugin_list(duk_context* ctx) -> duk_idx_t -{ - int i = 0; - - duk_push_array(ctx); - - for (const auto& plg : duk::type_traits<bot>::self(ctx).plugins().list()) { - duk::push(ctx, plg->get_id()); - duk_put_prop_index(ctx, -2, i++); - } - - return 1; -} - -// }}} - -// {{{ Irccd.Plugin.load - -/* - * Function: Irccd.Plugin.load(name) - * ------------------------------------------------------------------ - * - * Load a plugin by name. This function will search through the standard - * directories. - * - * Arguments: - * - name, the plugin identifier. - * Throws: - * - Irccd.PluginError on plugin related errors, - * - Irccd.SystemError on other errors. - */ -auto Plugin_load(duk_context* ctx) -> duk_idx_t -{ - return wrap(ctx, [&] { - duk::type_traits<bot>::self(ctx).plugins().load( - duk::require<std::string_view>(ctx, 0), ""); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Plugin.reload - -/* - * Function: Irccd.Plugin.reload(name) - * ------------------------------------------------------------------ - * - * Reload a plugin by name. - * - * Arguments: - * - name, the plugin identifier. - * Throws: - * - Irccd.PluginError on plugin related errors, - * - Irccd.SystemError on other errors. - */ -auto Plugin_reload(duk_context* ctx) -> duk_idx_t -{ - return wrap(ctx, [&] { - duk::type_traits<bot>::self(ctx).plugins().reload(duk::require<std::string>(ctx, 0)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Plugin.unload - -/* - * Function: Irccd.Plugin.unload(name) - * ------------------------------------------------------------------ - * - * Unload a plugin by name. - * - * Arguments: - * - name, the plugin identifier. - * Throws: - * - Irccd.PluginError on plugin related errors, - * - Irccd.SystemError on other errors. - */ -auto Plugin_unload(duk_context* ctx) -> duk_idx_t -{ - return wrap(ctx, [&] { - duk::type_traits<bot>::self(ctx).plugins().unload(duk::require<std::string>(ctx, 0)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.PluginError [constructor] - -/* - * Function: Irccd.PluginError(code, message) - * ------------------------------------------------------------------ - * - * Create an Irccd.PluginError object. - * - * Arguments: - * - code, the error code, - * - message, the error message. - */ -auto PluginError_constructor(duk_context* ctx) -> duk_ret_t -{ - duk_push_this(ctx); - duk_push_int(ctx, duk_require_int(ctx, 0)); - duk_put_prop_string(ctx, -2, "code"); - duk_push_string(ctx, duk_require_string(ctx, 1)); - duk_put_prop_string(ctx, -2, "message"); - duk_push_string(ctx, "PluginError"); - duk_put_prop_string(ctx, -2, "name"); - duk_pop(ctx); - - return 0; -} - -// }}} - -const duk_function_list_entry functions[] = { - { "info", Plugin_info, DUK_VARARGS }, - { "list", Plugin_list, 0 }, - { "load", Plugin_load, 1 }, - { "reload", Plugin_reload, 1 }, - { "unload", Plugin_unload, 1 }, - { nullptr, nullptr, 0 } -}; - -} // !namespace - -auto plugin_js_api::get_name() const noexcept -> std::string_view -{ - return "Irccd.Plugin"; -} - -void plugin_js_api::load(bot&, std::shared_ptr<js_plugin> plugin) -{ - duk::stack_guard sa(plugin->get_context()); - - // Store plugin. - duk_push_pointer(plugin->get_context(), plugin.get()); - duk_put_global_string(plugin->get_context(), signature.data()); - - duk_get_global_string(plugin->get_context(), "Irccd"); - duk_push_object(plugin->get_context()); - duk_put_function_list(plugin->get_context(), -1, functions); - - // 'config' property. - duk_push_string(plugin->get_context(), "config"); - duk_push_c_function(plugin->get_context(), get_config, 0); - duk_push_c_function(plugin->get_context(), set_config, 1); - duk_def_prop(plugin->get_context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER); - - // 'format' property. - duk_push_string(plugin->get_context(), "format"); - duk_push_c_function(plugin->get_context(), get_format, 0); - duk_push_c_function(plugin->get_context(), set_format, 1); - duk_def_prop(plugin->get_context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER); - - // 'format' property. - duk_push_string(plugin->get_context(), "paths"); - duk_push_c_function(plugin->get_context(), get_paths, 0); - duk_push_c_function(plugin->get_context(), set_paths, 1); - duk_def_prop(plugin->get_context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER); - - // PluginError function. - duk_push_c_function(plugin->get_context(), PluginError_constructor, 2); - duk_push_object(plugin->get_context()); - duk_get_global_string(plugin->get_context(), "Error"); - duk_get_prop_string(plugin->get_context(), -1, "prototype"); - duk_remove(plugin->get_context(), -2); - duk_set_prototype(plugin->get_context(), -2); - duk_put_prop_string(plugin->get_context(), -2, "prototype"); - duk_put_prop_string(plugin->get_context(), -2, "PluginError"); - - duk_put_prop_string(plugin->get_context(), -2, "Plugin"); - duk_pop(plugin->get_context()); -} - -namespace duk { - -auto type_traits<js_plugin>::self(duk_context* ctx) -> js_plugin& -{ - duk::stack_guard sa(ctx); - - duk_get_global_string(ctx, signature.data()); - auto plugin = static_cast<js_plugin*>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - - return *plugin; -} - -void type_traits<plugin_error>::raise(duk_context* ctx, const plugin_error& ex) -{ - duk::stack_guard sa(ctx, 1); - - duk_get_global_string(ctx, "Irccd"); - duk_get_prop_string(ctx, -1, "PluginError"); - duk_remove(ctx, -2); - duk::push(ctx, ex.code().value()); - duk::push(ctx, ex.code().message()); - duk_new(ctx, 2); - - (void)duk_throw(ctx); -} - -} // !duk - -} // !irccd::js
--- a/libirccd-js/irccd/js/plugin_js_api.hpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,83 +0,0 @@ -/* - * plugin_js_api.hpp -- Irccd.Plugin API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_PLUGIN_JS_API_HPP -#define IRCCD_JS_PLUGIN_JS_API_HPP - -/** - * \file plugin_js_api.hpp - * \brief Irccd.Plugin Javascript API. - */ - -#include "js_api.hpp" -#include "js_plugin.hpp" - -namespace irccd::js { - -/** - * \ingroup js-api - * \brief Irccd.Plugin Javascript API. - */ -class plugin_js_api : public js_api { -public: - /** - * \copydoc js_api::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc js_api::load - */ - void load(daemon::bot& bot, std::shared_ptr<js_plugin> plugin) override; -}; - -namespace duk { - -/** - * \brief Specialize dukx_type_traits for plugin. - */ -template <> -struct type_traits<js_plugin> { - /** - * Access the plugin stored in this context. - * - * \param ctx the context - * \return the plugin - */ - static auto self(duk_context* ctx) -> js_plugin&; -}; - -/** - * \brief Specialization for plugin_error. - */ -template <> -struct type_traits<daemon::plugin_error> { - /** - * Raise a plugin_error. - * - * \param ctx the context - * \param error the error - */ - static void raise(duk_context* ctx, const daemon::plugin_error& error); -}; - -} // !duk - -} // !irccd::js - -#endif // !IRCCD_JS_PLUGIN_JS_API_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/server_api.cpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,898 @@ +/* + * server_api.cpp -- Irccd.Server API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <cassert> +#include <sstream> +#include <unordered_map> + +#include <irccd/daemon/bot.hpp> +#include <irccd/daemon/server_service.hpp> +#include <irccd/daemon/server_util.hpp> + +#include "irccd_api.hpp" +#include "plugin.hpp" +#include "server_api.hpp" + +using irccd::daemon::bot; +using irccd::daemon::server; +using irccd::daemon::server_error; +using irccd::daemon::server_util::from_json; + +namespace irccd::js { + +namespace { + +const std::string_view signature(DUK_HIDDEN_SYMBOL("Irccd.Server")); +const std::string_view prototype(DUK_HIDDEN_SYMBOL("Irccd.Server.prototype")); + +auto self(duk_context* ctx) -> std::shared_ptr<server> +{ + duk::stack_guard sa(ctx); + + duk_push_this(ctx); + duk_get_prop_string(ctx, -1, signature.data()); + auto ptr = duk_to_pointer(ctx, -1); + duk_pop_2(ctx); + + if (!ptr) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Server object"); + + return *static_cast<std::shared_ptr<server>*>(ptr); +} + +template <typename Handler> +auto wrap(duk_context* ctx, Handler handler) -> duk_ret_t +{ + try { + return handler(ctx); + } catch (const server_error& ex) { + duk::raise(ctx, ex); + } catch (const std::exception& ex) { + duk::raise(ctx, ex); + } + + return 0; +} + +// {{{ Irccd.Server.prototype.info + +/* + * Method: Irccd.Server.prototype.info() + * ------------------------------------------------------------------ + * + * Get the server information as an object containing the following properties: + * + * name: the server unique name + * host: the host name + * port: the port number + * ssl: true if using ssl + * sslVerify: true if ssl was verified + * channels: an array of all channels + */ +auto Server_prototype_info(duk_context* ctx) -> duk_ret_t +{ + const auto server = self(ctx); + const auto& channels = server->get_channels(); + + duk_push_object(ctx); + duk::push(ctx, server->get_id()); + duk_put_prop_string(ctx, -2, "name"); + duk::push(ctx, server->get_hostname()); + duk_put_prop_string(ctx, -2, "hostname"); + duk_push_int(ctx, server->get_port()); + duk_put_prop_string(ctx, -2, "port"); + duk_push_boolean(ctx, (server->get_options() & server::options::ssl) == server::options::ssl); + duk_put_prop_string(ctx, -2, "ssl"); + duk::push(ctx, server->get_command_char()); + duk_put_prop_string(ctx, -2, "commandChar"); + duk::push(ctx, server->get_realname()); + duk_put_prop_string(ctx, -2, "realname"); + duk::push(ctx, server->get_nickname()); + duk_put_prop_string(ctx, -2, "nickname"); + duk::push(ctx, server->get_username()); + duk_put_prop_string(ctx, -2, "username"); + duk::push(ctx, std::vector<std::string>(channels.begin(), channels.end())); + duk_put_prop_string(ctx, -2, "channels"); + + return 1; +} + +// }}} + +// {{{ Irccd.Server.prototype.invite + +/* + * Method: Irccd.Server.prototype.invite(target, channel) + * ------------------------------------------------------------------ + * + * Invite someone to a channel. + * + * Arguments: + * - target, the target to invite, + * - channel, the channel. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_invite(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto target = duk::require<std::string>(ctx, 0); + auto channel = duk::require<std::string>(ctx, 1); + + if (target.empty()) + throw server_error(server_error::invalid_nickname); + if (channel.empty()) + throw server_error(server_error::invalid_channel); + + self(ctx)->invite(std::move(target), std::move(channel)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.isSelf + +/* + * Method: Irccd.Server.prototype.isSelf(nickname) + * ------------------------------------------------------------------ + * + * Arguments: + * - nickname, the nickname to check. + * Returns: + * True if the nickname targets this server. + * Throws: + * - Irccd.SystemError on errors. + */ +auto Server_prototype_isSelf(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + return duk::push(ctx, self(ctx)->is_self(duk::require<std::string>(ctx, 0))); + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.join + +/* + * Method: Irccd.Server.prototype.join(channel, password = undefined) + * ------------------------------------------------------------------ + * + * Join a channel with an optional password. + * + * Arguments: + * - channel, the channel to join, + * - password, the password or undefined to not use. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_join(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto channel = duk::require<std::string>(ctx, 0); + auto password = duk::get<std::string>(ctx, 1); + + if (channel.empty()) + throw server_error(server_error::invalid_channel); + + self(ctx)->join(std::move(channel), std::move(password)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.kick + +/* + * Method: Irccd.Server.prototype.kick(target, channel, reason = undefined) + * ------------------------------------------------------------------ + * + * Kick someone from a channel. + * + * Arguments: + * - target, the target to kick, + * - channel, the channel, + * - reason, the optional reason or undefined to not set. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_kick(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto target = duk::require<std::string>(ctx, 0); + auto channel = duk::require<std::string>(ctx, 1); + auto reason = duk::get<std::string>(ctx, 2); + + if (target.empty()) + throw server_error(server_error::invalid_nickname); + if (channel.empty()) + throw server_error(server_error::invalid_channel); + + self(ctx)->kick(std::move(target), std::move(channel), std::move(reason)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.me + +/* + * Method: Irccd.Server.prototype.me(target, message) + * ------------------------------------------------------------------ + * + * Send a CTCP Action. + * + * Arguments: + * - target, the target or a channel, + * - message, the message. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_me(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto target = duk::require<std::string>(ctx, 0); + auto message = duk::get<std::string>(ctx, 1); + + if (target.empty()) + throw server_error(server_error::invalid_nickname); + + self(ctx)->me(std::move(target), std::move(message)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.message + +/* + * Method: Irccd.Server.prototype.message(target, message) + * ------------------------------------------------------------------ + * + * Send a message. + * + * Arguments: + * - target, the target or a channel, + * - message, the message. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_message(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto target = duk::require<std::string>(ctx, 0); + auto message = duk::get<std::string>(ctx, 1); + + if (target.empty()) + throw server_error(server_error::invalid_nickname); + + self(ctx)->message(std::move(target), std::move(message)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.mode + +/* + * Method: Irccd.Server.prototype.mode(channel, mode, limit, user, mask) + * ------------------------------------------------------------------ + * + * Change your mode. + * + * Arguments: + * - mode, the new mode. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_mode(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto channel = duk::require<std::string>(ctx, 0); + auto mode = duk::require<std::string>(ctx, 1); + auto limit = duk::get<std::string>(ctx, 2); + auto user = duk::get<std::string>(ctx, 3); + auto mask = duk::get<std::string>(ctx, 4); + + if (channel.empty()) + throw server_error(server_error::invalid_channel); + if (mode.empty()) + throw server_error(server_error::invalid_mode); + + self(ctx)->mode( + std::move(channel), + std::move(mode), + std::move(limit), + std::move(user), + std::move(mask) + ); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.names + +/* + * Method: Irccd.Server.prototype.names(channel) + * ------------------------------------------------------------------ + * + * Get the list of names from a channel. + * + * Arguments: + * - channel, the channel. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_names(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto channel = duk::require<std::string>(ctx, 0); + + if (channel.empty()) + throw server_error(server_error::invalid_channel); + + self(ctx)->names(std::move(channel)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.nick + +/* + * Method: Irccd.Server.prototype.nick(nickname) + * ------------------------------------------------------------------ + * + * Change the nickname. + * + * Arguments: + * - nickname, the nickname. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_nick(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto nickname = duk::require<std::string>(ctx, 0); + + if (nickname.empty()) + throw server_error(server_error::invalid_nickname); + + self(ctx)->set_nickname(std::move(nickname)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.notice + +/* + * Method: Irccd.Server.prototype.notice(target, message) + * ------------------------------------------------------------------ + * + * Send a private notice. + * + * Arguments: + * - target, the target, + * - message, the notice message. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_notice(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto target = duk::require<std::string>(ctx, 0); + auto message = duk::get<std::string>(ctx, 1); + + if (target.empty()) + throw server_error(server_error::invalid_nickname); + + self(ctx)->notice(std::move(target), std::move(message)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.part + +/* + * Method: Irccd.Server.prototype.part(channel, reason = undefined) + * ------------------------------------------------------------------ + * + * Leave a channel. + * + * Arguments: + * - channel, the channel to leave, + * - reason, the optional reason, keep undefined for portability. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_part(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto channel = duk::require<std::string>(ctx, 0); + auto reason = duk::get<std::string>(ctx, 1); + + if (channel.empty()) + throw server_error(server_error::invalid_channel); + + self(ctx)->part(std::move(channel), std::move(reason)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.send + +/* + * Method: Irccd.Server.prototype.send(raw) + * ------------------------------------------------------------------ + * + * Send a raw message to the IRC server. + * + * Arguments: + * - raw, the raw message (without terminators). + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_send(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto raw = duk::require<std::string>(ctx, 0); + + if (raw.empty()) + throw server_error(server_error::invalid_message); + + self(ctx)->send(std::move(raw)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.topic + +/* + * Method: Server.prototype.topic(channel, topic) + * ------------------------------------------------------------------ + * + * Change a channel topic. + * + * Arguments: + * - channel, the channel, + * - topic, the new topic. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_topic(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto channel = duk::require<std::string>(ctx, 0); + auto topic = duk::get<std::string>(ctx, 1); + + if (channel.empty()) + throw server_error(server_error::invalid_channel); + + self(ctx)->topic(std::move(channel), std::move(topic)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.whois + +/* + * Method: Irccd.Server.prototype.whois(target) + * ------------------------------------------------------------------ + * + * Get whois information. + * + * Arguments: + * - target, the target. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_whois(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto target = duk::require<std::string>(ctx, 0); + + if (target.empty()) + throw server_error(server_error::invalid_nickname); + + self(ctx)->whois(std::move(target)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.toString + +/* + * Method: Irccd.Server.prototype.toString() + * ------------------------------------------------------------------ + * + * Convert the object to std::string, convenience for adding the object + * as property key. + * + * Returns: + * The server name (unique). + * Throws: + * - Irccd.SystemError on errors. + */ +auto Server_prototype_toString(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + duk::push(ctx, self(ctx)->get_id()); + + return 1; + }); +} + +// }}} + +// {{{ Irccd.Server [constructor] + +/* + * Function: Irccd.Server(params) [constructor] + * ------------------------------------------------------------------ + * + * Construct a new server. + * + * Params must be filled with the following properties: + * + * name: the name, + * hostname: the hostname, + * ipv6: true to use ipv6, (Optional: default false) + * port: the port number, (Optional: default 6667) + * password: the password, (Optional: default none) + * channels: array of channels (Optiona: default empty) + * ssl: true to use ssl, (Optional: default false) + * nickname: "nickname", (Optional, default: irccd) + * username: "user name", (Optional, default: irccd) + * realname: "real name", (Optional, default: IRC Client Daemon) + * commandChar: "!", (Optional, the command char, default: "!") + * + * Arguments: + * - params, the server properties + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_constructor(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + if (!duk_is_constructor_call(ctx)) + return 0; + + duk_check_type(ctx, 0, DUK_TYPE_OBJECT); + + auto json = nlohmann::json::parse(duk_json_encode(ctx, 0)); + auto s = from_json(duk::type_traits<bot>::self(ctx).get_service(), json); + + duk_push_this(ctx); + duk_push_pointer(ctx, new std::shared_ptr<server>(std::move(s))); + duk_put_prop_string(ctx, -2, signature.data()); + duk_pop(ctx); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server [destructor] + +/* + * Function: Irccd.Server() [destructor] + * ------------------------------------------------------------------ + * + * Delete the property. + */ +auto Server_destructor(duk_context* ctx) -> duk_ret_t +{ + duk_get_prop_string(ctx, 0, signature.data()); + delete static_cast<std::shared_ptr<server>*>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + duk_del_prop_string(ctx, 0, signature.data()); + + return 0; +} + +// }}} + +// {{{ Irccd.Server.add + +/* + * Function: Irccd.Server.add(s) + * ------------------------------------------------------------------ + * + * Register a new server to the irccd instance. + * + * Arguments: + * - s, the server to add. + * Throws: + * - Irccd.SystemError on errors. + */ +auto Server_add(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + duk::type_traits<bot>::self(ctx).servers().add( + duk::require<std::shared_ptr<server>>(ctx, 0)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.find + +/* + * Function: Irccd.Server.find(name) + * ------------------------------------------------------------------ + * + * Find a server by name. + * + * Arguments: + * - name, the server name + * Returns: + * The server object or undefined if not found. + * Throws: + * - Irccd.SystemError on errors. + */ +auto Server_find(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto id = duk::require<std::string>(ctx, 0); + auto server = duk::type_traits<bot>::self(ctx).servers().get(id); + + if (!server) + return 0; + + duk::push(ctx, server); + + return 1; + }); +} + +// }}} + +// {{{ Irccd.Server.list + +/* + * Function: Irccd.Server.list() + * ------------------------------------------------------------------ + * + * Get the map of all loaded servers. + * + * Returns: + * An object with string-to-servers pairs. + */ +auto Server_list(duk_context* ctx) -> duk_ret_t +{ + duk_push_object(ctx); + + for (const auto& server : duk::type_traits<bot>::self(ctx).servers().list()) { + duk::push(ctx, server); + duk_put_prop_string(ctx, -2, server->get_id().c_str()); + } + + return 1; +} + +// }}} + +// {{{ Irccd.Server.remove + +/* + * Function: Irccd.Server.remove(name) + * ------------------------------------------------------------------ + * + * Remove a server from the irccd instance. You can pass the server object since + * it's coercible to a string. + * + * Arguments: + * - name the server name. + */ +auto Server_remove(duk_context* ctx) -> duk_ret_t +{ + duk::type_traits<bot>::self(ctx).servers().remove(duk_require_string(ctx, 0)); + + return 0; +} + +// }}} + +// {{{ Irccd.ServerError + +/* + * Function: Irccd.ServerError(code, message) + * ------------------------------------------------------------------ + * + * Create an Irccd.ServerError object. + * + * Arguments: + * - code, the error code, + * - message, the error message. + */ +auto ServerError_constructor(duk_context* ctx) -> duk_ret_t +{ + duk_push_this(ctx); + duk_push_int(ctx, duk_require_int(ctx, 0)); + duk_put_prop_string(ctx, -2, "code"); + duk_push_string(ctx, duk_require_string(ctx, 1)); + duk_put_prop_string(ctx, -2, "message"); + duk_push_string(ctx, "ServerError"); + duk_put_prop_string(ctx, -2, "name"); + duk_pop(ctx); + + return 0; +} + +// }}} + +const duk_function_list_entry methods[] = { + { "info", Server_prototype_info, 0 }, + { "invite", Server_prototype_invite, 2 }, + { "isSelf", Server_prototype_isSelf, 1 }, + { "join", Server_prototype_join, DUK_VARARGS }, + { "kick", Server_prototype_kick, DUK_VARARGS }, + { "me", Server_prototype_me, 2 }, + { "message", Server_prototype_message, 2 }, + { "mode", Server_prototype_mode, 1 }, + { "names", Server_prototype_names, 1 }, + { "nick", Server_prototype_nick, 1 }, + { "notice", Server_prototype_notice, 2 }, + { "part", Server_prototype_part, DUK_VARARGS }, + { "send", Server_prototype_send, 1 }, + { "topic", Server_prototype_topic, 2 }, + { "toString", Server_prototype_toString, 0 }, + { "whois", Server_prototype_whois, 1 }, + { nullptr, nullptr, 0 } +}; + +const duk_function_list_entry functions[] = { + { "add", Server_add, 1 }, + { "find", Server_find, 1 }, + { "list", Server_list, 0 }, + { "remove", Server_remove, 1 }, + { nullptr, nullptr, 0 } +}; + +} // !namespace + +auto server_api::get_name() const noexcept -> std::string_view +{ + return "Irccd.Server"; +} + +void server_api::load(bot&, std::shared_ptr<plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + duk_get_global_string(plugin->get_context(), "Irccd"); + + // ServerError function. + duk_push_c_function(plugin->get_context(), ServerError_constructor, 2); + duk_push_object(plugin->get_context()); + duk_get_global_string(plugin->get_context(), "Error"); + duk_get_prop_string(plugin->get_context(), -1, "prototype"); + duk_remove(plugin->get_context(), -2); + duk_set_prototype(plugin->get_context(), -2); + duk_put_prop_string(plugin->get_context(), -2, "prototype"); + duk_put_prop_string(plugin->get_context(), -2, "ServerError"); + + // Server constructor. + duk_push_c_function(plugin->get_context(), Server_constructor, 1); + duk_put_function_list(plugin->get_context(), -1, functions); + duk_push_object(plugin->get_context()); + duk_put_function_list(plugin->get_context(), -1, methods); + duk_push_c_function(plugin->get_context(), Server_destructor, 1); + duk_set_finalizer(plugin->get_context(), -2); + duk_dup_top(plugin->get_context()); + duk_put_global_string(plugin->get_context(), prototype.data()); + duk_put_prop_string(plugin->get_context(), -2, "prototype"); + duk_put_prop_string(plugin->get_context(), -2, "Server"); + duk_pop(plugin->get_context()); +} + +namespace duk { + +void type_traits<std::shared_ptr<server>>::push(duk_context* ctx, std::shared_ptr<server> server) +{ + assert(ctx); + assert(server); + + duk::stack_guard sa(ctx, 1); + + duk_push_object(ctx); + duk_push_pointer(ctx, new std::shared_ptr<class server>(std::move(server))); + duk_put_prop_string(ctx, -2, signature.data()); + duk_get_global_string(ctx, prototype.data()); + duk_set_prototype(ctx, -2); +} + +auto type_traits<std::shared_ptr<server>>::require(duk_context* ctx, duk_idx_t index) -> std::shared_ptr<server> +{ + if (!duk_is_object(ctx, index) || !duk_has_prop_string(ctx, index, signature.data())) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Server object"); + + duk_get_prop_string(ctx, index, signature.data()); + auto file = *static_cast<std::shared_ptr<server>*>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + + return file; +} + +void type_traits<server_error>::raise(duk_context* ctx, const server_error& ex) +{ + duk::stack_guard sa(ctx, 1); + + duk_get_global_string(ctx, "Irccd"); + duk_get_prop_string(ctx, -1, "ServerError"); + duk_remove(ctx, -2); + duk::push(ctx, ex.code().value()); + duk::push(ctx, ex.code().message()); + duk_new(ctx, 2); + + (void)duk_throw(ctx); +} + +} // !duk + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/server_api.hpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,96 @@ +/* + * server_api.hpp -- Irccd.Server API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_SERVER_API_HPP +#define IRCCD_JS_SERVER_API_HPP + +/** + * \file server_api.hpp + * \brief Irccd.Server Javascript API. + */ + +#include <irccd/daemon/server.hpp> + +#include "api.hpp" + +namespace irccd::js { + +/** + * \ingroup js-api + * \brief Irccd.Server Javascript API. + */ +class server_api : public api { +public: + /** + * \copydoc api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc api::load + */ + void load(daemon::bot& bot, std::shared_ptr<plugin> plugin) override; +}; + +namespace duk { + +/** + * \brief Specialization for servers as shared_ptr. + * + * Supports push, require. + */ +template <> +struct type_traits<std::shared_ptr<daemon::server>> { + /** + * Push a server. + * + * \pre server != nullptr + * \param ctx the context + * \param server the server + */ + static void push(duk_context* ctx, std::shared_ptr<daemon::server> server); + + /** + * Require a server. Raise a Javascript error if not a Server. + * + * \param ctx the context + * \param index the index + * \return the server + */ + static auto require(duk_context* ctx, duk_idx_t index) -> std::shared_ptr<daemon::server>; +}; + +/** + * \brief Specialization for server_error. + */ +template <> +struct type_traits<daemon::server_error> { + /** + * Raise a server_error. + * + * \param ctx the context + * \param error the error + */ + static void raise(duk_context* ctx, const daemon::server_error& error); +}; + +} // !duk + +} // !irccd::js + +#endif // !IRCCD_JS_SERVER_API_HPP
--- a/libirccd-js/irccd/js/server_js_api.cpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,898 +0,0 @@ -/* - * server_js_api.cpp -- Irccd.Server API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <cassert> -#include <sstream> -#include <unordered_map> - -#include <irccd/daemon/bot.hpp> -#include <irccd/daemon/server_service.hpp> -#include <irccd/daemon/server_util.hpp> - -#include "irccd_js_api.hpp" -#include "js_plugin.hpp" -#include "server_js_api.hpp" - -using irccd::daemon::bot; -using irccd::daemon::server; -using irccd::daemon::server_error; -using irccd::daemon::server_util::from_json; - -namespace irccd::js { - -namespace { - -const std::string_view signature(DUK_HIDDEN_SYMBOL("Irccd.Server")); -const std::string_view prototype(DUK_HIDDEN_SYMBOL("Irccd.Server.prototype")); - -auto self(duk_context* ctx) -> std::shared_ptr<server> -{ - duk::stack_guard sa(ctx); - - duk_push_this(ctx); - duk_get_prop_string(ctx, -1, signature.data()); - auto ptr = duk_to_pointer(ctx, -1); - duk_pop_2(ctx); - - if (!ptr) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Server object"); - - return *static_cast<std::shared_ptr<server>*>(ptr); -} - -template <typename Handler> -auto wrap(duk_context* ctx, Handler handler) -> duk_ret_t -{ - try { - return handler(ctx); - } catch (const server_error& ex) { - duk::raise(ctx, ex); - } catch (const std::exception& ex) { - duk::raise(ctx, ex); - } - - return 0; -} - -// {{{ Irccd.Server.prototype.info - -/* - * Method: Irccd.Server.prototype.info() - * ------------------------------------------------------------------ - * - * Get the server information as an object containing the following properties: - * - * name: the server unique name - * host: the host name - * port: the port number - * ssl: true if using ssl - * sslVerify: true if ssl was verified - * channels: an array of all channels - */ -auto Server_prototype_info(duk_context* ctx) -> duk_ret_t -{ - const auto server = self(ctx); - const auto& channels = server->get_channels(); - - duk_push_object(ctx); - duk::push(ctx, server->get_id()); - duk_put_prop_string(ctx, -2, "name"); - duk::push(ctx, server->get_hostname()); - duk_put_prop_string(ctx, -2, "hostname"); - duk_push_int(ctx, server->get_port()); - duk_put_prop_string(ctx, -2, "port"); - duk_push_boolean(ctx, (server->get_options() & server::options::ssl) == server::options::ssl); - duk_put_prop_string(ctx, -2, "ssl"); - duk::push(ctx, server->get_command_char()); - duk_put_prop_string(ctx, -2, "commandChar"); - duk::push(ctx, server->get_realname()); - duk_put_prop_string(ctx, -2, "realname"); - duk::push(ctx, server->get_nickname()); - duk_put_prop_string(ctx, -2, "nickname"); - duk::push(ctx, server->get_username()); - duk_put_prop_string(ctx, -2, "username"); - duk::push(ctx, std::vector<std::string>(channels.begin(), channels.end())); - duk_put_prop_string(ctx, -2, "channels"); - - return 1; -} - -// }}} - -// {{{ Irccd.Server.prototype.invite - -/* - * Method: Irccd.Server.prototype.invite(target, channel) - * ------------------------------------------------------------------ - * - * Invite someone to a channel. - * - * Arguments: - * - target, the target to invite, - * - channel, the channel. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -auto Server_prototype_invite(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [] (auto ctx) { - auto target = duk::require<std::string>(ctx, 0); - auto channel = duk::require<std::string>(ctx, 1); - - if (target.empty()) - throw server_error(server_error::invalid_nickname); - if (channel.empty()) - throw server_error(server_error::invalid_channel); - - self(ctx)->invite(std::move(target), std::move(channel)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.isSelf - -/* - * Method: Irccd.Server.prototype.isSelf(nickname) - * ------------------------------------------------------------------ - * - * Arguments: - * - nickname, the nickname to check. - * Returns: - * True if the nickname targets this server. - * Throws: - * - Irccd.SystemError on errors. - */ -auto Server_prototype_isSelf(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [] (auto ctx) { - return duk::push(ctx, self(ctx)->is_self(duk::require<std::string>(ctx, 0))); - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.join - -/* - * Method: Irccd.Server.prototype.join(channel, password = undefined) - * ------------------------------------------------------------------ - * - * Join a channel with an optional password. - * - * Arguments: - * - channel, the channel to join, - * - password, the password or undefined to not use. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -auto Server_prototype_join(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [] (auto ctx) { - auto channel = duk::require<std::string>(ctx, 0); - auto password = duk::get<std::string>(ctx, 1); - - if (channel.empty()) - throw server_error(server_error::invalid_channel); - - self(ctx)->join(std::move(channel), std::move(password)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.kick - -/* - * Method: Irccd.Server.prototype.kick(target, channel, reason = undefined) - * ------------------------------------------------------------------ - * - * Kick someone from a channel. - * - * Arguments: - * - target, the target to kick, - * - channel, the channel, - * - reason, the optional reason or undefined to not set. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -auto Server_prototype_kick(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [] (auto ctx) { - auto target = duk::require<std::string>(ctx, 0); - auto channel = duk::require<std::string>(ctx, 1); - auto reason = duk::get<std::string>(ctx, 2); - - if (target.empty()) - throw server_error(server_error::invalid_nickname); - if (channel.empty()) - throw server_error(server_error::invalid_channel); - - self(ctx)->kick(std::move(target), std::move(channel), std::move(reason)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.me - -/* - * Method: Irccd.Server.prototype.me(target, message) - * ------------------------------------------------------------------ - * - * Send a CTCP Action. - * - * Arguments: - * - target, the target or a channel, - * - message, the message. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -auto Server_prototype_me(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [] (auto ctx) { - auto target = duk::require<std::string>(ctx, 0); - auto message = duk::get<std::string>(ctx, 1); - - if (target.empty()) - throw server_error(server_error::invalid_nickname); - - self(ctx)->me(std::move(target), std::move(message)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.message - -/* - * Method: Irccd.Server.prototype.message(target, message) - * ------------------------------------------------------------------ - * - * Send a message. - * - * Arguments: - * - target, the target or a channel, - * - message, the message. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -auto Server_prototype_message(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [] (auto ctx) { - auto target = duk::require<std::string>(ctx, 0); - auto message = duk::get<std::string>(ctx, 1); - - if (target.empty()) - throw server_error(server_error::invalid_nickname); - - self(ctx)->message(std::move(target), std::move(message)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.mode - -/* - * Method: Irccd.Server.prototype.mode(channel, mode, limit, user, mask) - * ------------------------------------------------------------------ - * - * Change your mode. - * - * Arguments: - * - mode, the new mode. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -auto Server_prototype_mode(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [] (auto ctx) { - auto channel = duk::require<std::string>(ctx, 0); - auto mode = duk::require<std::string>(ctx, 1); - auto limit = duk::get<std::string>(ctx, 2); - auto user = duk::get<std::string>(ctx, 3); - auto mask = duk::get<std::string>(ctx, 4); - - if (channel.empty()) - throw server_error(server_error::invalid_channel); - if (mode.empty()) - throw server_error(server_error::invalid_mode); - - self(ctx)->mode( - std::move(channel), - std::move(mode), - std::move(limit), - std::move(user), - std::move(mask) - ); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.names - -/* - * Method: Irccd.Server.prototype.names(channel) - * ------------------------------------------------------------------ - * - * Get the list of names from a channel. - * - * Arguments: - * - channel, the channel. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -auto Server_prototype_names(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [] (auto ctx) { - auto channel = duk::require<std::string>(ctx, 0); - - if (channel.empty()) - throw server_error(server_error::invalid_channel); - - self(ctx)->names(std::move(channel)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.nick - -/* - * Method: Irccd.Server.prototype.nick(nickname) - * ------------------------------------------------------------------ - * - * Change the nickname. - * - * Arguments: - * - nickname, the nickname. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -auto Server_prototype_nick(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [] (auto ctx) { - auto nickname = duk::require<std::string>(ctx, 0); - - if (nickname.empty()) - throw server_error(server_error::invalid_nickname); - - self(ctx)->set_nickname(std::move(nickname)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.notice - -/* - * Method: Irccd.Server.prototype.notice(target, message) - * ------------------------------------------------------------------ - * - * Send a private notice. - * - * Arguments: - * - target, the target, - * - message, the notice message. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -auto Server_prototype_notice(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [] (auto ctx) { - auto target = duk::require<std::string>(ctx, 0); - auto message = duk::get<std::string>(ctx, 1); - - if (target.empty()) - throw server_error(server_error::invalid_nickname); - - self(ctx)->notice(std::move(target), std::move(message)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.part - -/* - * Method: Irccd.Server.prototype.part(channel, reason = undefined) - * ------------------------------------------------------------------ - * - * Leave a channel. - * - * Arguments: - * - channel, the channel to leave, - * - reason, the optional reason, keep undefined for portability. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -auto Server_prototype_part(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [] (auto ctx) { - auto channel = duk::require<std::string>(ctx, 0); - auto reason = duk::get<std::string>(ctx, 1); - - if (channel.empty()) - throw server_error(server_error::invalid_channel); - - self(ctx)->part(std::move(channel), std::move(reason)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.send - -/* - * Method: Irccd.Server.prototype.send(raw) - * ------------------------------------------------------------------ - * - * Send a raw message to the IRC server. - * - * Arguments: - * - raw, the raw message (without terminators). - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -auto Server_prototype_send(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [] (auto ctx) { - auto raw = duk::require<std::string>(ctx, 0); - - if (raw.empty()) - throw server_error(server_error::invalid_message); - - self(ctx)->send(std::move(raw)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.topic - -/* - * Method: Server.prototype.topic(channel, topic) - * ------------------------------------------------------------------ - * - * Change a channel topic. - * - * Arguments: - * - channel, the channel, - * - topic, the new topic. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -auto Server_prototype_topic(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [] (auto ctx) { - auto channel = duk::require<std::string>(ctx, 0); - auto topic = duk::get<std::string>(ctx, 1); - - if (channel.empty()) - throw server_error(server_error::invalid_channel); - - self(ctx)->topic(std::move(channel), std::move(topic)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.whois - -/* - * Method: Irccd.Server.prototype.whois(target) - * ------------------------------------------------------------------ - * - * Get whois information. - * - * Arguments: - * - target, the target. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -auto Server_prototype_whois(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [] (auto ctx) { - auto target = duk::require<std::string>(ctx, 0); - - if (target.empty()) - throw server_error(server_error::invalid_nickname); - - self(ctx)->whois(std::move(target)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.toString - -/* - * Method: Irccd.Server.prototype.toString() - * ------------------------------------------------------------------ - * - * Convert the object to std::string, convenience for adding the object - * as property key. - * - * Returns: - * The server name (unique). - * Throws: - * - Irccd.SystemError on errors. - */ -auto Server_prototype_toString(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [] (auto ctx) { - duk::push(ctx, self(ctx)->get_id()); - - return 1; - }); -} - -// }}} - -// {{{ Irccd.Server [constructor] - -/* - * Function: Irccd.Server(params) [constructor] - * ------------------------------------------------------------------ - * - * Construct a new server. - * - * Params must be filled with the following properties: - * - * name: the name, - * hostname: the hostname, - * ipv6: true to use ipv6, (Optional: default false) - * port: the port number, (Optional: default 6667) - * password: the password, (Optional: default none) - * channels: array of channels (Optiona: default empty) - * ssl: true to use ssl, (Optional: default false) - * nickname: "nickname", (Optional, default: irccd) - * username: "user name", (Optional, default: irccd) - * realname: "real name", (Optional, default: IRC Client Daemon) - * commandChar: "!", (Optional, the command char, default: "!") - * - * Arguments: - * - params, the server properties - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -auto Server_constructor(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [] (auto ctx) { - if (!duk_is_constructor_call(ctx)) - return 0; - - duk_check_type(ctx, 0, DUK_TYPE_OBJECT); - - auto json = nlohmann::json::parse(duk_json_encode(ctx, 0)); - auto s = from_json(duk::type_traits<bot>::self(ctx).get_service(), json); - - duk_push_this(ctx); - duk_push_pointer(ctx, new std::shared_ptr<server>(std::move(s))); - duk_put_prop_string(ctx, -2, signature.data()); - duk_pop(ctx); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server [destructor] - -/* - * Function: Irccd.Server() [destructor] - * ------------------------------------------------------------------ - * - * Delete the property. - */ -auto Server_destructor(duk_context* ctx) -> duk_ret_t -{ - duk_get_prop_string(ctx, 0, signature.data()); - delete static_cast<std::shared_ptr<server>*>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - duk_del_prop_string(ctx, 0, signature.data()); - - return 0; -} - -// }}} - -// {{{ Irccd.Server.add - -/* - * Function: Irccd.Server.add(s) - * ------------------------------------------------------------------ - * - * Register a new server to the irccd instance. - * - * Arguments: - * - s, the server to add. - * Throws: - * - Irccd.SystemError on errors. - */ -auto Server_add(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [] (auto ctx) { - duk::type_traits<bot>::self(ctx).servers().add( - duk::require<std::shared_ptr<server>>(ctx, 0)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.find - -/* - * Function: Irccd.Server.find(name) - * ------------------------------------------------------------------ - * - * Find a server by name. - * - * Arguments: - * - name, the server name - * Returns: - * The server object or undefined if not found. - * Throws: - * - Irccd.SystemError on errors. - */ -auto Server_find(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [] (auto ctx) { - auto id = duk::require<std::string>(ctx, 0); - auto server = duk::type_traits<bot>::self(ctx).servers().get(id); - - if (!server) - return 0; - - duk::push(ctx, server); - - return 1; - }); -} - -// }}} - -// {{{ Irccd.Server.list - -/* - * Function: Irccd.Server.list() - * ------------------------------------------------------------------ - * - * Get the map of all loaded servers. - * - * Returns: - * An object with string-to-servers pairs. - */ -auto Server_list(duk_context* ctx) -> duk_ret_t -{ - duk_push_object(ctx); - - for (const auto& server : duk::type_traits<bot>::self(ctx).servers().list()) { - duk::push(ctx, server); - duk_put_prop_string(ctx, -2, server->get_id().c_str()); - } - - return 1; -} - -// }}} - -// {{{ Irccd.Server.remove - -/* - * Function: Irccd.Server.remove(name) - * ------------------------------------------------------------------ - * - * Remove a server from the irccd instance. You can pass the server object since - * it's coercible to a string. - * - * Arguments: - * - name the server name. - */ -auto Server_remove(duk_context* ctx) -> duk_ret_t -{ - duk::type_traits<bot>::self(ctx).servers().remove(duk_require_string(ctx, 0)); - - return 0; -} - -// }}} - -// {{{ Irccd.ServerError - -/* - * Function: Irccd.ServerError(code, message) - * ------------------------------------------------------------------ - * - * Create an Irccd.ServerError object. - * - * Arguments: - * - code, the error code, - * - message, the error message. - */ -auto ServerError_constructor(duk_context* ctx) -> duk_ret_t -{ - duk_push_this(ctx); - duk_push_int(ctx, duk_require_int(ctx, 0)); - duk_put_prop_string(ctx, -2, "code"); - duk_push_string(ctx, duk_require_string(ctx, 1)); - duk_put_prop_string(ctx, -2, "message"); - duk_push_string(ctx, "ServerError"); - duk_put_prop_string(ctx, -2, "name"); - duk_pop(ctx); - - return 0; -} - -// }}} - -const duk_function_list_entry methods[] = { - { "info", Server_prototype_info, 0 }, - { "invite", Server_prototype_invite, 2 }, - { "isSelf", Server_prototype_isSelf, 1 }, - { "join", Server_prototype_join, DUK_VARARGS }, - { "kick", Server_prototype_kick, DUK_VARARGS }, - { "me", Server_prototype_me, 2 }, - { "message", Server_prototype_message, 2 }, - { "mode", Server_prototype_mode, 1 }, - { "names", Server_prototype_names, 1 }, - { "nick", Server_prototype_nick, 1 }, - { "notice", Server_prototype_notice, 2 }, - { "part", Server_prototype_part, DUK_VARARGS }, - { "send", Server_prototype_send, 1 }, - { "topic", Server_prototype_topic, 2 }, - { "toString", Server_prototype_toString, 0 }, - { "whois", Server_prototype_whois, 1 }, - { nullptr, nullptr, 0 } -}; - -const duk_function_list_entry functions[] = { - { "add", Server_add, 1 }, - { "find", Server_find, 1 }, - { "list", Server_list, 0 }, - { "remove", Server_remove, 1 }, - { nullptr, nullptr, 0 } -}; - -} // !namespace - -auto server_js_api::get_name() const noexcept -> std::string_view -{ - return "Irccd.Server"; -} - -void server_js_api::load(bot&, std::shared_ptr<js_plugin> plugin) -{ - duk::stack_guard sa(plugin->get_context()); - - duk_get_global_string(plugin->get_context(), "Irccd"); - - // ServerError function. - duk_push_c_function(plugin->get_context(), ServerError_constructor, 2); - duk_push_object(plugin->get_context()); - duk_get_global_string(plugin->get_context(), "Error"); - duk_get_prop_string(plugin->get_context(), -1, "prototype"); - duk_remove(plugin->get_context(), -2); - duk_set_prototype(plugin->get_context(), -2); - duk_put_prop_string(plugin->get_context(), -2, "prototype"); - duk_put_prop_string(plugin->get_context(), -2, "ServerError"); - - // Server constructor. - duk_push_c_function(plugin->get_context(), Server_constructor, 1); - duk_put_function_list(plugin->get_context(), -1, functions); - duk_push_object(plugin->get_context()); - duk_put_function_list(plugin->get_context(), -1, methods); - duk_push_c_function(plugin->get_context(), Server_destructor, 1); - duk_set_finalizer(plugin->get_context(), -2); - duk_dup_top(plugin->get_context()); - duk_put_global_string(plugin->get_context(), prototype.data()); - duk_put_prop_string(plugin->get_context(), -2, "prototype"); - duk_put_prop_string(plugin->get_context(), -2, "Server"); - duk_pop(plugin->get_context()); -} - -namespace duk { - -void type_traits<std::shared_ptr<server>>::push(duk_context* ctx, std::shared_ptr<server> server) -{ - assert(ctx); - assert(server); - - duk::stack_guard sa(ctx, 1); - - duk_push_object(ctx); - duk_push_pointer(ctx, new std::shared_ptr<class server>(std::move(server))); - duk_put_prop_string(ctx, -2, signature.data()); - duk_get_global_string(ctx, prototype.data()); - duk_set_prototype(ctx, -2); -} - -auto type_traits<std::shared_ptr<server>>::require(duk_context* ctx, duk_idx_t index) -> std::shared_ptr<server> -{ - if (!duk_is_object(ctx, index) || !duk_has_prop_string(ctx, index, signature.data())) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Server object"); - - duk_get_prop_string(ctx, index, signature.data()); - auto file = *static_cast<std::shared_ptr<server>*>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - - return file; -} - -void type_traits<server_error>::raise(duk_context* ctx, const server_error& ex) -{ - duk::stack_guard sa(ctx, 1); - - duk_get_global_string(ctx, "Irccd"); - duk_get_prop_string(ctx, -1, "ServerError"); - duk_remove(ctx, -2); - duk::push(ctx, ex.code().value()); - duk::push(ctx, ex.code().message()); - duk_new(ctx, 2); - - (void)duk_throw(ctx); -} - -} // !duk - -} // !irccd::js
--- a/libirccd-js/irccd/js/server_js_api.hpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,96 +0,0 @@ -/* - * server_js_api.hpp -- Irccd.Server API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_SERVER_JS_API_HPP -#define IRCCD_JS_SERVER_JS_API_HPP - -/** - * \file server_js_api.hpp - * \brief Irccd.Server Javascript API. - */ - -#include <irccd/daemon/server.hpp> - -#include "js_api.hpp" - -namespace irccd::js { - -/** - * \ingroup js-api - * \brief Irccd.Server Javascript API. - */ -class server_js_api : public js_api { -public: - /** - * \copydoc js_api::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc js_api::load - */ - void load(daemon::bot& bot, std::shared_ptr<js_plugin> plugin) override; -}; - -namespace duk { - -/** - * \brief Specialization for servers as shared_ptr. - * - * Supports push, require. - */ -template <> -struct type_traits<std::shared_ptr<daemon::server>> { - /** - * Push a server. - * - * \pre server != nullptr - * \param ctx the context - * \param server the server - */ - static void push(duk_context* ctx, std::shared_ptr<daemon::server> server); - - /** - * Require a server. Raise a Javascript error if not a Server. - * - * \param ctx the context - * \param index the index - * \return the server - */ - static auto require(duk_context* ctx, duk_idx_t index) -> std::shared_ptr<daemon::server>; -}; - -/** - * \brief Specialization for server_error. - */ -template <> -struct type_traits<daemon::server_error> { - /** - * Raise a server_error. - * - * \param ctx the context - * \param error the error - */ - static void raise(duk_context* ctx, const daemon::server_error& error); -}; - -} // !duk - -} // !irccd::js - -#endif // !IRCCD_JS_SERVER_JS_API_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/system_api.cpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,332 @@ +/* + * system_api.cpp -- Irccd.System API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <irccd/sysconfig.hpp> + +#include <chrono> +#include <cstdlib> +#include <thread> + +#if defined(IRCCD_HAVE_POPEN) +# include <cstdio> +#endif + +#include <irccd/system.hpp> + +#include "file_api.hpp" +#include "irccd_api.hpp" +#include "plugin.hpp" +#include "system_api.hpp" + +using irccd::daemon::bot; + +namespace irccd::js { + +namespace { + +// {{{ wrap + +template <typename Handler> +auto wrap(duk_context* ctx, Handler handler) -> duk_ret_t +{ + try { + return handler(); + } catch (const std::system_error& ex) { + duk::raise(ctx, ex); + } catch (const std::exception& ex) { + duk::raise(ctx, ex); + } + + return 0; +} + +// }}} + +// {{{ Irccd.System.env + +/* + * Function: Irccd.System.env(key) + * ------------------------------------------------------------------ + * + * Get an environment system variable. + * + * Arguments: + * - key, the environment variable. + * Returns: + * The value. + * Throws: + * - Irccd.SystemError on errors. + */ +auto System_env(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, sys::env(duk::get<std::string>(ctx, 0))); + }); +} + +// }}} + +// {{{ Irccd.System.exec + +/* + * Function: Irccd.System.exec(cmd) + * ------------------------------------------------------------------ + * + * Execute a system command. + * + * Arguments: + * - cmd, the command to execute. + * Throws: + * - Irccd.SystemError on errors. + */ +auto System_exec(duk_context* ctx) -> duk_ret_t +{ + std::system(duk_require_string(ctx, 0)); + + return 0; +} + +// }}} + +// {{{ Irccd.System.home + +/* + * Function: Irccd.System.home() + * ------------------------------------------------------------------ + * + * Get the operating system user's home. + * + * Returns: + * The user home directory. + * Throws: + * - Irccd.SystemError on errors. + */ +auto System_home(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, sys::home()); + }); +} + +// }}} + +// {{{ Irccd.System.name + +/* + * Function: Irccd.System.name() + * ------------------------------------------------------------------ + * + * Get the operating system name. + * + * Returns: + * The system name. + * Throws: + * - Irccd.SystemError on errors. + */ +auto System_name(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, sys::name()); + }); +} + +// }}} + +// {{{ Irccd.System.popen + +#if defined(IRCCD_HAVE_POPEN) + +/* + * Function: Irccd.System.popen(cmd, mode) [optional] + * ------------------------------------------------------------------ + * + * Wrapper for popen(3) if the function is available. + * + * Arguments: + * - cmd, the command to execute, + * - mode, the mode (e.g. "r"). + * Returns: + * A irccd.File object. + * Throws: + * - Irccd.SystemError on errors. + */ +auto System_popen(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + auto fp = ::popen(duk_require_string(ctx, 0), duk_require_string(ctx, 1)); + + if (fp == nullptr) + throw std::system_error(make_error_code(static_cast<std::errc>(errno))); + + return duk::push(ctx, std::make_shared<file>(fp, [] (auto fp) { ::pclose(fp); })); + }); +} + +#endif // !IRCCD_HAVE_POPEN + +// }}} + +// {{{ Icrcd.System.sleep + +/* + * Function: Irccd.System.sleep(delay) + * ------------------------------------------------------------------ + * + * Sleep the main loop for the specific delay in seconds. + * + * Arguments: + * - delay, the delay in seconds. + * Throws: + * - Irccd.SystemError on errors. + */ +auto System_sleep(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + std::this_thread::sleep_for(std::chrono::seconds(duk_get_int(ctx, 0))); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.System.ticks + +/* + * Function: Irccd.System.ticks() + * ------------------------------------------------------------------ + * + * Get the number of milliseconds since irccd was started. + * + * Returns: + * The number of milliseconds. + * Throws: + * - Irccd.SystemError on errors. + */ +auto System_ticks(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push<unsigned>(ctx, sys::ticks()); + }); +} + +// }}} + +// {{{ Irccd.System.usleep + +/* + * Function: Irccd.System.usleep(delay) + * ------------------------------------------------------------------ + * + * Sleep the main loop for the specific delay in microseconds. + * + * Arguments: + * - delay, the delay in microseconds. + * Throws: + * - Irccd.SystemError on errors. + */ +auto System_usleep(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + std::this_thread::sleep_for(std::chrono::microseconds(duk_get_int(ctx, 0))); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.System.uptime + +/* + * Function: Irccd.System.uptime() + * ------------------------------------------------------------------ + * + * Get the system uptime. + * + * Returns: + * The system uptime. + * Throws: + * - Irccd.SystemError on errors. + */ +auto System_uptime(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push<unsigned>(ctx, sys::uptime()); + }); +} + +// }}} + +// {{{ Irccd.System.version + +/* + * Function: Irccd.System.version() + * ------------------------------------------------------------------ + * + * Get the operating system version. + * + * Returns: + * The system version. + * Throws: + * - Irccd.SystemError on errors. + */ +auto System_version(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, sys::version()); + }); +} + +// }}} + +const duk_function_list_entry functions[] = { + { "env", System_env, 1 }, + { "exec", System_exec, 1 }, + { "home", System_home, 0 }, + { "name", System_name, 0 }, +#if defined(IRCCD_HAVE_POPEN) + { "popen", System_popen, 2 }, +#endif + { "sleep", System_sleep, 1 }, + { "ticks", System_ticks, 0 }, + { "uptime", System_uptime, 0 }, + { "usleep", System_usleep, 1 }, + { "version", System_version, 0 }, + { nullptr, nullptr, 0 } +}; + +} // !namespace + +auto system_api::get_name() const noexcept -> std::string_view +{ + return "Irccd.System"; +} + +void system_api::load(bot&, std::shared_ptr<plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + duk_get_global_string(plugin->get_context(), "Irccd"); + duk_push_object(plugin->get_context()); + duk_put_function_list(plugin->get_context(), -1, functions); + duk_put_prop_string(plugin->get_context(), -2, "System"); + duk_pop(plugin->get_context()); +} + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/system_api.hpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,50 @@ +/* + * system_api.hpp -- Irccd.System API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_SYSTEM_API_HPP +#define IRCCD_JS_SYSTEM_API_HPP + +/** + * \file system_api.hpp + * \brief Irccd.System Javascript API. + */ + +#include "api.hpp" + +namespace irccd::js { + +/** + * \ingroup js-api + * \brief Irccd.System Javascript API. + */ +class system_api : public api { +public: + /** + * \copydoc api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc api::load + */ + void load(daemon::bot& bot, std::shared_ptr<plugin> plugin) override; +}; + +} // !irccd::js + +#endif // !IRCCD_JS_SYSTEM_API_HPP
--- a/libirccd-js/irccd/js/system_js_api.cpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,332 +0,0 @@ -/* - * system_js_api.cpp -- Irccd.System API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <irccd/sysconfig.hpp> - -#include <chrono> -#include <cstdlib> -#include <thread> - -#if defined(IRCCD_HAVE_POPEN) -# include <cstdio> -#endif - -#include <irccd/system.hpp> - -#include "file_js_api.hpp" -#include "irccd_js_api.hpp" -#include "js_plugin.hpp" -#include "system_js_api.hpp" - -using irccd::daemon::bot; - -namespace irccd::js { - -namespace { - -// {{{ wrap - -template <typename Handler> -auto wrap(duk_context* ctx, Handler handler) -> duk_ret_t -{ - try { - return handler(); - } catch (const std::system_error& ex) { - duk::raise(ctx, ex); - } catch (const std::exception& ex) { - duk::raise(ctx, ex); - } - - return 0; -} - -// }}} - -// {{{ Irccd.System.env - -/* - * Function: Irccd.System.env(key) - * ------------------------------------------------------------------ - * - * Get an environment system variable. - * - * Arguments: - * - key, the environment variable. - * Returns: - * The value. - * Throws: - * - Irccd.SystemError on errors. - */ -auto System_env(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - return duk::push(ctx, sys::env(duk::get<std::string>(ctx, 0))); - }); -} - -// }}} - -// {{{ Irccd.System.exec - -/* - * Function: Irccd.System.exec(cmd) - * ------------------------------------------------------------------ - * - * Execute a system command. - * - * Arguments: - * - cmd, the command to execute. - * Throws: - * - Irccd.SystemError on errors. - */ -auto System_exec(duk_context* ctx) -> duk_ret_t -{ - std::system(duk_require_string(ctx, 0)); - - return 0; -} - -// }}} - -// {{{ Irccd.System.home - -/* - * Function: Irccd.System.home() - * ------------------------------------------------------------------ - * - * Get the operating system user's home. - * - * Returns: - * The user home directory. - * Throws: - * - Irccd.SystemError on errors. - */ -auto System_home(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - return duk::push(ctx, sys::home()); - }); -} - -// }}} - -// {{{ Irccd.System.name - -/* - * Function: Irccd.System.name() - * ------------------------------------------------------------------ - * - * Get the operating system name. - * - * Returns: - * The system name. - * Throws: - * - Irccd.SystemError on errors. - */ -auto System_name(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - return duk::push(ctx, sys::name()); - }); -} - -// }}} - -// {{{ Irccd.System.popen - -#if defined(IRCCD_HAVE_POPEN) - -/* - * Function: Irccd.System.popen(cmd, mode) [optional] - * ------------------------------------------------------------------ - * - * Wrapper for popen(3) if the function is available. - * - * Arguments: - * - cmd, the command to execute, - * - mode, the mode (e.g. "r"). - * Returns: - * A irccd.File object. - * Throws: - * - Irccd.SystemError on errors. - */ -auto System_popen(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - auto fp = ::popen(duk_require_string(ctx, 0), duk_require_string(ctx, 1)); - - if (fp == nullptr) - throw std::system_error(make_error_code(static_cast<std::errc>(errno))); - - return duk::push(ctx, std::make_shared<file>(fp, [] (auto fp) { ::pclose(fp); })); - }); -} - -#endif // !IRCCD_HAVE_POPEN - -// }}} - -// {{{ Icrcd.System.sleep - -/* - * Function: Irccd.System.sleep(delay) - * ------------------------------------------------------------------ - * - * Sleep the main loop for the specific delay in seconds. - * - * Arguments: - * - delay, the delay in seconds. - * Throws: - * - Irccd.SystemError on errors. - */ -auto System_sleep(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - std::this_thread::sleep_for(std::chrono::seconds(duk_get_int(ctx, 0))); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.System.ticks - -/* - * Function: Irccd.System.ticks() - * ------------------------------------------------------------------ - * - * Get the number of milliseconds since irccd was started. - * - * Returns: - * The number of milliseconds. - * Throws: - * - Irccd.SystemError on errors. - */ -auto System_ticks(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - return duk::push<unsigned>(ctx, sys::ticks()); - }); -} - -// }}} - -// {{{ Irccd.System.usleep - -/* - * Function: Irccd.System.usleep(delay) - * ------------------------------------------------------------------ - * - * Sleep the main loop for the specific delay in microseconds. - * - * Arguments: - * - delay, the delay in microseconds. - * Throws: - * - Irccd.SystemError on errors. - */ -auto System_usleep(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - std::this_thread::sleep_for(std::chrono::microseconds(duk_get_int(ctx, 0))); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.System.uptime - -/* - * Function: Irccd.System.uptime() - * ------------------------------------------------------------------ - * - * Get the system uptime. - * - * Returns: - * The system uptime. - * Throws: - * - Irccd.SystemError on errors. - */ -auto System_uptime(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - return duk::push<unsigned>(ctx, sys::uptime()); - }); -} - -// }}} - -// {{{ Irccd.System.version - -/* - * Function: Irccd.System.version() - * ------------------------------------------------------------------ - * - * Get the operating system version. - * - * Returns: - * The system version. - * Throws: - * - Irccd.SystemError on errors. - */ -auto System_version(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - return duk::push(ctx, sys::version()); - }); -} - -// }}} - -const duk_function_list_entry functions[] = { - { "env", System_env, 1 }, - { "exec", System_exec, 1 }, - { "home", System_home, 0 }, - { "name", System_name, 0 }, -#if defined(IRCCD_HAVE_POPEN) - { "popen", System_popen, 2 }, -#endif - { "sleep", System_sleep, 1 }, - { "ticks", System_ticks, 0 }, - { "uptime", System_uptime, 0 }, - { "usleep", System_usleep, 1 }, - { "version", System_version, 0 }, - { nullptr, nullptr, 0 } -}; - -} // !namespace - -auto system_js_api::get_name() const noexcept -> std::string_view -{ - return "Irccd.System"; -} - -void system_js_api::load(bot&, std::shared_ptr<js_plugin> plugin) -{ - duk::stack_guard sa(plugin->get_context()); - - duk_get_global_string(plugin->get_context(), "Irccd"); - duk_push_object(plugin->get_context()); - duk_put_function_list(plugin->get_context(), -1, functions); - duk_put_prop_string(plugin->get_context(), -2, "System"); - duk_pop(plugin->get_context()); -} - -} // !irccd::js
--- a/libirccd-js/irccd/js/system_js_api.hpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * system_js_api.hpp -- Irccd.System API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_SYSTEM_JS_API_HPP -#define IRCCD_JS_SYSTEM_JS_API_HPP - -/** - * \file system_js_api.hpp - * \brief Irccd.System Javascript API. - */ - -#include "js_api.hpp" - -namespace irccd::js { - -/** - * \ingroup js-api - * \brief Irccd.System Javascript API. - */ -class system_js_api : public js_api { -public: - /** - * \copydoc js_api::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc js_api::load - */ - void load(daemon::bot& bot, std::shared_ptr<js_plugin> plugin) override; -}; - -} // !irccd::js - -#endif // !IRCCD_JS_SYSTEM_JS_API_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/timer_api.cpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,316 @@ +/* + * timer_api.cpp -- Irccd.timer API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <boost/asio.hpp> + +#include <irccd/daemon/bot.hpp> +#include <irccd/daemon/logger.hpp> +#include <irccd/daemon/plugin_service.hpp> + +#include "irccd_api.hpp" +#include "plugin.hpp" +#include "plugin_api.hpp" +#include "timer_api.hpp" + +namespace asio = boost::asio; + +using irccd::daemon::bot; + +namespace irccd::js { + +namespace { + +const std::string_view signature(DUK_HIDDEN_SYMBOL("Irccd.Timer")); +const std::string_view table(DUK_HIDDEN_SYMBOL("Irccd.Timer.callbacks")); + +// {{{ timer + +class timer : public std::enable_shared_from_this<timer> { +public: + enum class type { + single, + repeat + }; + +private: + boost::asio::deadline_timer handle_; + plugin& plugin_; + + std::string key_; + type type_; + int delay_; + + bool is_running_{false}; + bool is_waiting_{false}; + + void handle(); + +public: + timer(boost::asio::io_service&, plugin&, type, int); + + auto key() const noexcept -> const std::string&; + + void start(); + + void stop(); +}; + +void timer::handle() +{ + duk::stack_guard sa(plugin_.get_context()); + + duk_push_global_stash(plugin_.get_context()); + duk_get_prop_string(plugin_.get_context(), -1, table.data()); + duk_remove(plugin_.get_context(), -2); + duk_get_prop_string(plugin_.get_context(), -1, key_.c_str()); + duk_remove(plugin_.get_context(), -2); + + if (duk_pcall(plugin_.get_context(), 0)) { + auto& log = duk::type_traits<bot>::self(plugin_.get_context()).get_log(); + + log.warning(static_cast<const daemon::plugin&>(plugin_)) << "timer error:" << std::endl; + log.warning(static_cast<const daemon::plugin&>(plugin_)) << " " << duk::get_stack(plugin_.get_context(), -1).what() << std::endl; + } else + duk_pop(plugin_.get_context()); +} + +timer::timer(boost::asio::io_service& service, plugin& plugin, type type, int delay) + : handle_(service) + , plugin_(plugin) + , type_(type) + , delay_(delay) +{ +} + +auto timer::key() const noexcept -> const std::string& +{ + return key_; +} + +void timer::start() +{ + if (is_waiting_) + return; + + is_running_ = is_waiting_ = true; + + handle_.expires_from_now(boost::posix_time::milliseconds(delay_)); + handle_.async_wait([this] (auto code) { + is_waiting_ = false; + + if (code) { + is_running_ = false; + return; + } + + handle(); + + if (is_running_ && type_ == type::repeat) + start(); + }); +} + +void timer::stop() +{ + if (is_running_) { + handle_.cancel(); + is_running_ = false; + } +} + +// }}} + +// {{{ self + +auto self(duk_context* ctx) -> timer* +{ + duk::stack_guard sa(ctx); + + duk_push_this(ctx); + duk_get_prop_string(ctx, -1, signature.data()); + auto ptr = duk_to_pointer(ctx, -1); + duk_pop_2(ctx); + + if (!ptr) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Timer object"); + + return static_cast<timer*>(ptr); +} + +// }}} + +// {{{ Irccd.Timer.prototype.start + +/* + * Method: Irccd.Timer.prototype.start() + * -------------------------------------------------------- + * + * Start the timer. If the timer is already started the method is a no-op. + */ +auto Timer_prototype_start(duk_context* ctx) -> duk_ret_t +{ + self(ctx)->start(); + + return 0; +} + +// }}} + +// {{{ Irccd.Timer.prototype.stop + +/* + * Method: Irccd.Timer.prototype.stop() + * -------------------------------------------------------- + * + * Stop the timer. + */ +auto Timer_prototype_stop(duk_context* ctx) -> duk_ret_t +{ + self(ctx)->stop(); + + return 0; +} + +// }}} + +// {{{ Irccd.Timer [destructor] + +/* + * Function: Irccd.Timer() [destructor] + * ------------------------------------------------------------------ + * + * Deleter the timer. + */ +auto Timer_destructor(duk_context* ctx) -> duk_ret_t +{ + duk::stack_guard sa(ctx); + + // Get timer from this. + duk_get_prop_string(ctx, 0, signature.data()); + auto ptr = static_cast<timer*>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + + // Remove callback from timer table. + duk_push_global_stash(ctx); + duk_get_prop_string(ctx, -1, table.data()); + duk_remove(ctx, -2); + duk_del_prop_string(ctx, -1, ptr->key().c_str()); + duk_pop(ctx); + + delete ptr; + + return 0; +} + +// }}} + +// {{{ Irccd.Timer [constructor] + +/* + * Function: Irccd.Timer(type, delay, callback) [constructor] + * -------------------------------------------------------- + * + * Create a new timer object. + * + * Arguments: + * - type, the type of timer (Irccd.Timer.Single or Irccd.Timer.Repeat), + * - delay, the interval in milliseconds, + * - callback, the function to call. + */ +auto Timer_constructor(duk_context* ctx) -> duk_ret_t +{ + if (!duk_is_constructor_call(ctx)) + return 0; + + try { + // Check parameters. + const auto type = duk_require_int(ctx, 0); + const auto delay = duk_require_int(ctx, 1); + + if (type < static_cast<int>(timer::type::single) || type > static_cast<int>(timer::type::repeat)) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid timer type"); + if (delay < 0) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "negative delay given"); + if (!duk_is_callable(ctx, 2)) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "missing callback function"); + + auto& plg = duk::type_traits<plugin>::self(ctx); + auto& daemon = duk::type_traits<bot>::self(ctx); + auto object = new timer(daemon.get_service(), plg, static_cast<timer::type>(type), delay); + + duk_push_this(ctx); + duk_push_pointer(ctx, object); + duk_put_prop_string(ctx, -2, signature.data()); + duk_push_c_function(ctx, Timer_destructor, 1); + duk_set_finalizer(ctx, -2); + duk_pop(ctx); + + // Store the function in a table to be called later. + duk_push_global_stash(ctx); + duk_get_prop_string(ctx, -1, table.data()); + duk_remove(ctx, -2); + duk_dup(ctx, 2); + duk_put_prop_string(ctx, -2, object->key().c_str()); + duk_pop(ctx); + } catch (const std::exception& ex) { + duk::raise(ctx, ex); + } + + return 0; +} + +// }}} + +const duk_function_list_entry methods[] = { + { "start", Timer_prototype_start, 0 }, + { "stop", Timer_prototype_stop, 0 }, + { nullptr, nullptr, 0 } +}; + +const duk_number_list_entry constants[] = { + { "Single", static_cast<int>(timer::type::single) }, + { "Repeat", static_cast<int>(timer::type::repeat) }, + { nullptr, 0 } +}; + +} // !namespace + +auto timer_api::get_name() const noexcept -> std::string_view +{ + return "Irccd.Timer"; +} + +void timer_api::load(bot&, std::shared_ptr<plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + duk_get_global_string(plugin->get_context(), "Irccd"); + duk_push_c_function(plugin->get_context(), Timer_constructor, 3); + duk_put_number_list(plugin->get_context(), -1, constants); + duk_push_object(plugin->get_context()); + duk_put_function_list(plugin->get_context(), -1, methods); + duk_put_prop_string(plugin->get_context(), -2, "prototype"); + duk_put_prop_string(plugin->get_context(), -2, "Timer"); + duk_pop(plugin->get_context()); + duk_push_global_stash(plugin->get_context()); + duk_push_object(plugin->get_context()); + duk_put_prop_string(plugin->get_context(), -2, table.data()); + duk_pop(plugin->get_context()); +} + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/timer_api.hpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,50 @@ +/* + * timer_api.hpp -- Irccd.Timer API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_TIMER_API_HPP +#define IRCCD_JS_TIMER_API_HPP + +/** + * \file timer_api.hpp + * \brief Irccd.Timer Javascript API. + */ + +#include "api.hpp" + +namespace irccd::js { + +/** + * \ingroup js-api + * \brief Irccd.Timer Javascript API. + */ +class timer_api : public api { +public: + /** + * \copydoc api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc api::load + */ + void load(daemon::bot& bot, std::shared_ptr<plugin> plugin) override; +}; + +} // !irccd::js + +#endif // !IRCCD_JS_TIMER_API_HPP
--- a/libirccd-js/irccd/js/timer_js_api.cpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,317 +0,0 @@ -/* - * timer_js_api.cpp -- Irccd.timer API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <boost/asio.hpp> - -#include <irccd/daemon/bot.hpp> -#include <irccd/daemon/logger.hpp> -#include <irccd/daemon/plugin_service.hpp> - -#include "irccd_js_api.hpp" -#include "js_plugin.hpp" -#include "plugin_js_api.hpp" -#include "timer_js_api.hpp" - -namespace asio = boost::asio; - -using irccd::daemon::bot; -using irccd::daemon::plugin; - -namespace irccd::js { - -namespace { - -const std::string_view signature(DUK_HIDDEN_SYMBOL("Irccd.Timer")); -const std::string_view table(DUK_HIDDEN_SYMBOL("Irccd.Timer.callbacks")); - -// {{{ timer - -class timer : public std::enable_shared_from_this<timer> { -public: - enum class type { - single, - repeat - }; - -private: - boost::asio::deadline_timer handle_; - js_plugin& plugin_; - - std::string key_; - type type_; - int delay_; - - bool is_running_{false}; - bool is_waiting_{false}; - - void handle(); - -public: - timer(boost::asio::io_service&, js_plugin&, type, int); - - auto key() const noexcept -> const std::string&; - - void start(); - - void stop(); -}; - -void timer::handle() -{ - duk::stack_guard sa(plugin_.get_context()); - - duk_push_global_stash(plugin_.get_context()); - duk_get_prop_string(plugin_.get_context(), -1, table.data()); - duk_remove(plugin_.get_context(), -2); - duk_get_prop_string(plugin_.get_context(), -1, key_.c_str()); - duk_remove(plugin_.get_context(), -2); - - if (duk_pcall(plugin_.get_context(), 0)) { - auto& log = duk::type_traits<bot>::self(plugin_.get_context()).get_log(); - - log.warning(static_cast<const plugin&>(plugin_)) << "timer error:" << std::endl; - log.warning(static_cast<const plugin&>(plugin_)) << " " << duk::get_stack(plugin_.get_context(), -1).what() << std::endl; - } else - duk_pop(plugin_.get_context()); -} - -timer::timer(boost::asio::io_service& service, js_plugin& plugin, type type, int delay) - : handle_(service) - , plugin_(plugin) - , type_(type) - , delay_(delay) -{ -} - -auto timer::key() const noexcept -> const std::string& -{ - return key_; -} - -void timer::start() -{ - if (is_waiting_) - return; - - is_running_ = is_waiting_ = true; - - handle_.expires_from_now(boost::posix_time::milliseconds(delay_)); - handle_.async_wait([this] (auto code) { - is_waiting_ = false; - - if (code) { - is_running_ = false; - return; - } - - handle(); - - if (is_running_ && type_ == type::repeat) - start(); - }); -} - -void timer::stop() -{ - if (is_running_) { - handle_.cancel(); - is_running_ = false; - } -} - -// }}} - -// {{{ self - -auto self(duk_context* ctx) -> timer* -{ - duk::stack_guard sa(ctx); - - duk_push_this(ctx); - duk_get_prop_string(ctx, -1, signature.data()); - auto ptr = duk_to_pointer(ctx, -1); - duk_pop_2(ctx); - - if (!ptr) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Timer object"); - - return static_cast<timer*>(ptr); -} - -// }}} - -// {{{ Irccd.Timer.prototype.start - -/* - * Method: Irccd.Timer.prototype.start() - * -------------------------------------------------------- - * - * Start the timer. If the timer is already started the method is a no-op. - */ -auto Timer_prototype_start(duk_context* ctx) -> duk_ret_t -{ - self(ctx)->start(); - - return 0; -} - -// }}} - -// {{{ Irccd.Timer.prototype.stop - -/* - * Method: Irccd.Timer.prototype.stop() - * -------------------------------------------------------- - * - * Stop the timer. - */ -auto Timer_prototype_stop(duk_context* ctx) -> duk_ret_t -{ - self(ctx)->stop(); - - return 0; -} - -// }}} - -// {{{ Irccd.Timer [destructor] - -/* - * Function: Irccd.Timer() [destructor] - * ------------------------------------------------------------------ - * - * Deleter the timer. - */ -auto Timer_destructor(duk_context* ctx) -> duk_ret_t -{ - duk::stack_guard sa(ctx); - - // Get timer from this. - duk_get_prop_string(ctx, 0, signature.data()); - auto ptr = static_cast<timer*>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - - // Remove callback from timer table. - duk_push_global_stash(ctx); - duk_get_prop_string(ctx, -1, table.data()); - duk_remove(ctx, -2); - duk_del_prop_string(ctx, -1, ptr->key().c_str()); - duk_pop(ctx); - - delete ptr; - - return 0; -} - -// }}} - -// {{{ Irccd.Timer [constructor] - -/* - * Function: Irccd.Timer(type, delay, callback) [constructor] - * -------------------------------------------------------- - * - * Create a new timer object. - * - * Arguments: - * - type, the type of timer (Irccd.Timer.Single or Irccd.Timer.Repeat), - * - delay, the interval in milliseconds, - * - callback, the function to call. - */ -auto Timer_constructor(duk_context* ctx) -> duk_ret_t -{ - if (!duk_is_constructor_call(ctx)) - return 0; - - try { - // Check parameters. - const auto type = duk_require_int(ctx, 0); - const auto delay = duk_require_int(ctx, 1); - - if (type < static_cast<int>(timer::type::single) || type > static_cast<int>(timer::type::repeat)) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid timer type"); - if (delay < 0) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "negative delay given"); - if (!duk_is_callable(ctx, 2)) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "missing callback function"); - - auto& plugin = duk::type_traits<js_plugin>::self(ctx); - auto& daemon = duk::type_traits<bot>::self(ctx); - auto object = new timer(daemon.get_service(), plugin, static_cast<timer::type>(type), delay); - - duk_push_this(ctx); - duk_push_pointer(ctx, object); - duk_put_prop_string(ctx, -2, signature.data()); - duk_push_c_function(ctx, Timer_destructor, 1); - duk_set_finalizer(ctx, -2); - duk_pop(ctx); - - // Store the function in a table to be called later. - duk_push_global_stash(ctx); - duk_get_prop_string(ctx, -1, table.data()); - duk_remove(ctx, -2); - duk_dup(ctx, 2); - duk_put_prop_string(ctx, -2, object->key().c_str()); - duk_pop(ctx); - } catch (const std::exception& ex) { - duk::raise(ctx, ex); - } - - return 0; -} - -// }}} - -const duk_function_list_entry methods[] = { - { "start", Timer_prototype_start, 0 }, - { "stop", Timer_prototype_stop, 0 }, - { nullptr, nullptr, 0 } -}; - -const duk_number_list_entry constants[] = { - { "Single", static_cast<int>(timer::type::single) }, - { "Repeat", static_cast<int>(timer::type::repeat) }, - { nullptr, 0 } -}; - -} // !namespace - -auto timer_js_api::get_name() const noexcept -> std::string_view -{ - return "Irccd.Timer"; -} - -void timer_js_api::load(bot&, std::shared_ptr<js_plugin> plugin) -{ - duk::stack_guard sa(plugin->get_context()); - - duk_get_global_string(plugin->get_context(), "Irccd"); - duk_push_c_function(plugin->get_context(), Timer_constructor, 3); - duk_put_number_list(plugin->get_context(), -1, constants); - duk_push_object(plugin->get_context()); - duk_put_function_list(plugin->get_context(), -1, methods); - duk_put_prop_string(plugin->get_context(), -2, "prototype"); - duk_put_prop_string(plugin->get_context(), -2, "Timer"); - duk_pop(plugin->get_context()); - duk_push_global_stash(plugin->get_context()); - duk_push_object(plugin->get_context()); - duk_put_prop_string(plugin->get_context(), -2, table.data()); - duk_pop(plugin->get_context()); -} - -} // !irccd::js
--- a/libirccd-js/irccd/js/timer_js_api.hpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * timer_js_api.hpp -- Irccd.Timer API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_TIMER_JS_API_HPP -#define IRCCD_JS_TIMER_JS_API_HPP - -/** - * \file timer_js_api.hpp - * \brief Irccd.Timer Javascript API. - */ - -#include "js_api.hpp" - -namespace irccd::js { - -/** - * \ingroup js-api - * \brief Irccd.Timer Javascript API. - */ -class timer_js_api : public js_api { -public: - /** - * \copydoc js_api::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc js_api::load - */ - void load(daemon::bot& bot, std::shared_ptr<js_plugin> plugin) override; -}; - -} // !irccd::js - -#endif // !IRCCD_JS_TIMER_JS_API_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/unicode_api.cpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,165 @@ +/* + * unicode_api.cpp -- Irccd.Unicode API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "plugin.hpp" +#include "unicode.hpp" +#include "unicode_api.hpp" + +using irccd::daemon::bot; + +namespace irccd::js { + +namespace { + +// {{{ Irccd.Unicode.isDigit + +/* + * Function: Irccd.Unicode.isDigit(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is in the digit category. + */ +auto Unicode_isDigit(duk_context* ctx) noexcept -> duk_ret_t +{ + return duk::push(ctx, unicode::isdigit(duk_get_int(ctx, 0))); +} + +// }}} + +// {{{ Irccd.Unicode.isLetter + +/* + * Function: Irccd.Unicode.isLetter(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is in the letter category. + */ +auto Unicode_isLetter(duk_context* ctx) noexcept -> duk_ret_t +{ + return duk::push(ctx, unicode::isalpha(duk_get_int(ctx, 0))); +} + +// }}} + +// {{{ Irccd.Unicode.isLower + +/* + * Function: Irccd.Unicode.isLower(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is lower case. + */ +auto Unicode_isLower(duk_context* ctx) noexcept -> duk_ret_t +{ + return duk::push(ctx, unicode::islower(duk_get_int(ctx, 0))); +} + +// }}} + +// {{{ Irccd.Unicode.isSpace + +/* + * Function: Irccd.Unicode.isSpace(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is in the space category. + */ +auto Unicode_isSpace(duk_context* ctx) noexcept -> duk_ret_t +{ + return duk::push(ctx, unicode::isspace(duk_get_int(ctx, 0))); +} + +// }}} + +// {{{ Irccd.Unicode.isTitle + +/* + * Function: Irccd.Unicode.isTitle(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is title case. + */ +auto Unicode_isTitle(duk_context* ctx) noexcept -> duk_ret_t +{ + return duk::push(ctx, unicode::istitle(duk_get_int(ctx, 0))); +} + +// }}} + +// {{{ Irccd.Unicode.isUpper + +/* + * Function: Irccd.Unicode.isUpper(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is upper case. + */ +auto Unicode_isUpper(duk_context* ctx) noexcept -> duk_ret_t +{ + return duk::push(ctx, unicode::isupper(duk_get_int(ctx, 0))); +} + +// }}} + +const duk_function_list_entry functions[] = { + { "isDigit", Unicode_isDigit, 1 }, + { "isLetter", Unicode_isLetter, 1 }, + { "isLower", Unicode_isLower, 1 }, + { "isSpace", Unicode_isSpace, 1 }, + { "isTitle", Unicode_isTitle, 1 }, + { "isUpper", Unicode_isUpper, 1 }, + { nullptr, nullptr, 0 } +}; + +} // !namespace + +auto unicode_api::get_name() const noexcept -> std::string_view +{ + return "Irccd.Unicode"; +} + +void unicode_api::load(bot&, std::shared_ptr<plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + duk_get_global_string(plugin->get_context(), "Irccd"); + duk_push_object(plugin->get_context()); + duk_put_function_list(plugin->get_context(), -1, functions); + duk_put_prop_string(plugin->get_context(), -2, "Unicode"); + duk_pop(plugin->get_context()); +} + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/unicode_api.hpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,50 @@ +/* + * unicode_api.hpp -- Irccd.Unicode API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_UNICODE_API_HPP +#define IRCCD_JS_UNICODE_API_HPP + +/** + * \file unicode_api.hpp + * \brief Irccd.Unicode Javascript API. + */ + +#include "api.hpp" + +namespace irccd::js { + +/** + * \ingroup js-api + * \brief Irccd.Unicode Javascript API. + */ +class unicode_api : public api { +public: + /** + * \copydoc api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc api::load + */ + void load(daemon::bot& bot, std::shared_ptr<plugin> plugin) override; +}; + +} // !irccd::js + +#endif // !IRCCD_JS_UNICODE_API_HPP
--- a/libirccd-js/irccd/js/unicode_js_api.cpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,165 +0,0 @@ -/* - * unicode_js_api.cpp -- Irccd.Unicode API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "js_plugin.hpp" -#include "unicode.hpp" -#include "unicode_js_api.hpp" - -using irccd::daemon::bot; - -namespace irccd::js { - -namespace { - -// {{{ Irccd.Unicode.isDigit - -/* - * Function: Irccd.Unicode.isDigit(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is in the digit category. - */ -auto Unicode_isDigit(duk_context* ctx) noexcept -> duk_ret_t -{ - return duk::push(ctx, unicode::isdigit(duk_get_int(ctx, 0))); -} - -// }}} - -// {{{ Irccd.Unicode.isLetter - -/* - * Function: Irccd.Unicode.isLetter(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is in the letter category. - */ -auto Unicode_isLetter(duk_context* ctx) noexcept -> duk_ret_t -{ - return duk::push(ctx, unicode::isalpha(duk_get_int(ctx, 0))); -} - -// }}} - -// {{{ Irccd.Unicode.isLower - -/* - * Function: Irccd.Unicode.isLower(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is lower case. - */ -auto Unicode_isLower(duk_context* ctx) noexcept -> duk_ret_t -{ - return duk::push(ctx, unicode::islower(duk_get_int(ctx, 0))); -} - -// }}} - -// {{{ Irccd.Unicode.isSpace - -/* - * Function: Irccd.Unicode.isSpace(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is in the space category. - */ -auto Unicode_isSpace(duk_context* ctx) noexcept -> duk_ret_t -{ - return duk::push(ctx, unicode::isspace(duk_get_int(ctx, 0))); -} - -// }}} - -// {{{ Irccd.Unicode.isTitle - -/* - * Function: Irccd.Unicode.isTitle(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is title case. - */ -auto Unicode_isTitle(duk_context* ctx) noexcept -> duk_ret_t -{ - return duk::push(ctx, unicode::istitle(duk_get_int(ctx, 0))); -} - -// }}} - -// {{{ Irccd.Unicode.isUpper - -/* - * Function: Irccd.Unicode.isUpper(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is upper case. - */ -auto Unicode_isUpper(duk_context* ctx) noexcept -> duk_ret_t -{ - return duk::push(ctx, unicode::isupper(duk_get_int(ctx, 0))); -} - -// }}} - -const duk_function_list_entry functions[] = { - { "isDigit", Unicode_isDigit, 1 }, - { "isLetter", Unicode_isLetter, 1 }, - { "isLower", Unicode_isLower, 1 }, - { "isSpace", Unicode_isSpace, 1 }, - { "isTitle", Unicode_isTitle, 1 }, - { "isUpper", Unicode_isUpper, 1 }, - { nullptr, nullptr, 0 } -}; - -} // !namespace - -auto unicode_js_api::get_name() const noexcept -> std::string_view -{ - return "Irccd.Unicode"; -} - -void unicode_js_api::load(bot&, std::shared_ptr<js_plugin> plugin) -{ - duk::stack_guard sa(plugin->get_context()); - - duk_get_global_string(plugin->get_context(), "Irccd"); - duk_push_object(plugin->get_context()); - duk_put_function_list(plugin->get_context(), -1, functions); - duk_put_prop_string(plugin->get_context(), -2, "Unicode"); - duk_pop(plugin->get_context()); -} - -} // !irccd::js
--- a/libirccd-js/irccd/js/unicode_js_api.hpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * unicode_js_api.hpp -- Irccd.Unicode API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_UNICODE_JS_API_HPP -#define IRCCD_JS_UNICODE_JS_API_HPP - -/** - * \file unicode_js_api.hpp - * \brief Irccd.Unicode Javascript API. - */ - -#include "js_api.hpp" - -namespace irccd::js { - -/** - * \ingroup js-api - * \brief Irccd.Unicode Javascript API. - */ -class unicode_js_api : public js_api { -public: - /** - * \copydoc js_api::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc js_api::load - */ - void load(daemon::bot& bot, std::shared_ptr<js_plugin> plugin) override; -}; - -} // !irccd::js - -#endif // !IRCCD_JS_UNICODE_JS_API_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/util_api.cpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,354 @@ +/* + * util_api.cpp -- Irccd.Util API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <climits> + +#include <irccd/string_util.hpp> + +#include "irccd_api.hpp" +#include "plugin.hpp" +#include "util_api.hpp" + +using irccd::daemon::bot; +using irccd::daemon::irc::user; + +namespace irccd::js { + +namespace { + +// {{{ subst + +/* + * Read parameters for Irccd.Util.format function, the object is defined as + * following: + * + * { + * date: the date object + * flags: the flags (not implemented yet) + * field1: a field to substitute in #{} pattern + * field2: a field to substitute in #{} pattern + * fieldn: ... + * } + */ +auto subst(duk_context* ctx, int index) -> string_util::subst +{ + string_util::subst params; + + if (!duk_is_object(ctx, index)) + return params; + + duk_enum(ctx, index, 0); + + while (duk_next(ctx, -1, true)) { + if (duk::get<std::string>(ctx, -2) == "date") + params.time = static_cast<time_t>(duk_get_number(ctx, -1) / 1000); + else + params.keywords.insert({ + duk::get<std::string>(ctx, -2), + duk::get<std::string>(ctx, -1) + }); + + duk_pop_n(ctx, 2); + } + + return params; +} + +// }}} + +// {{{ split + +/* + * split (for Irccd.Util.cut) + * ------------------------------------------------------------------ + * + * Extract individual tokens in array or a whole string as a std:::vector. + */ +auto split(duk_context* ctx) -> std::vector<std::string> +{ + duk_require_type_mask(ctx, 0, DUK_TYPE_MASK_OBJECT | DUK_TYPE_MASK_STRING); + + std::vector<std::string> result; + std::string pattern = " \t\n"; + + if (duk_is_string(ctx, 0)) + result = string_util::split(duk::get<std::string>(ctx, 0), pattern); + else if (duk_is_array(ctx, 0)) { + duk_enum(ctx, 0, DUK_ENUM_ARRAY_INDICES_ONLY); + + while (duk_next(ctx, -1, 1)) { + // Split individual tokens as array if spaces are found. + const auto tmp = string_util::split(duk_to_string(ctx, -1), pattern); + + result.insert(result.end(), tmp.begin(), tmp.end()); + duk_pop_2(ctx); + } + } + + return result; +} + +// }}} + +// {{{ limit + +/* + * limit (for Irccd.Util.cut) + * ------------------------------------------------------------------ + * + * Get the maxl/maxc argument. + * + * The argument value is the default and also used as the result returned. + */ +auto limit(duk_context* ctx, int index, const char* name, int value) -> int +{ + if (duk_get_top(ctx) < index || !duk_is_number(ctx, index)) + return value; + + value = duk_to_int(ctx, index); + + if (value <= 0) + duk_error(ctx, DUK_ERR_RANGE_ERROR, "argument %d (%s) must be positive", index, name); + + return value; +} + +// }}} + +// {{{ lines + +/* + * lines (for Irccd.Util.cut) + * ------------------------------------------------------------------ + * + * Build a list of lines. + * + * Several cases possible: + * + * - s is the current line + * - abc is the token to add + * + * s = "" (new line) + * s -> "abc" + * + * s = "hello world" (enough room) + * s -> "hello world abc" + * + * s = "hello world" (not enough room: maxc is smaller) + * s+1 = "abc" + */ +auto lines(duk_context* ctx, const std::vector<std::string>& tokens, int maxc) -> std::vector<std::string> +{ + std::vector<std::string> result{""}; + + for (const auto& s : tokens) { + if (s.length() > static_cast<std::size_t>(maxc)) + duk_error(ctx, DUK_ERR_RANGE_ERROR, "word '%s' could not fit in maxc limit (%d)", s.c_str(), maxc); + + // Compute the length required (prepend a space if needed) + auto required = s.length() + (result.back().empty() ? 0 : 1); + + if (result.back().length() + required > static_cast<std::size_t>(maxc)) + result.push_back(s); + else { + if (!result.back().empty()) + result.back() += ' '; + + result.back() += s; + } + } + + return result; +} + +// }}} + +// {{{ wrap + +template <typename Handler> +auto wrap(duk_context* ctx, Handler handler) -> duk_ret_t +{ + try { + return handler(); + } catch (const std::system_error& ex) { + duk::raise(ctx, ex); + } catch (const std::exception& ex) { + duk::raise(ctx, ex); + } + + return 0; +} + +// }}} + +// {{{ Irccd.Util.cut + +/* + * Function: Irccd.Util.cut(data, maxc, maxl) + * -------------------------------------------------------- + * + * Cut a piece of data into several lines. + * + * The argument data is a string or a list of strings. In any case, all strings + * are first splitted by spaces and trimmed. This ensure that useless + * whitespaces are discarded. + * + * The argument maxc controls the maximum of characters allowed per line, it can + * be a positive integer. If undefined is given, a default of 72 is used. + * + * The argument maxl controls the maximum of lines allowed. It can be a positive + * integer or undefined for an infinite list. + * + * If maxl is used as a limit and the data can not fit within the bounds, + * undefined is returned. + * + * An empty list may be returned if empty strings were found. + * + * Arguments: + * - data, a string or an array of strings, + * - maxc, max number of colums (Optional, default: 72), + * - maxl, max number of lines (Optional, default: undefined). + * Returns: + * A list of strings ready to be sent or undefined if the data is too big. + * Throws: + * - RangeError if maxl or maxc are negative numbers, + * - RangeError if one word length was bigger than maxc, + * - TypeError if data is not a string or a list of strings, + * - Irccd.SystemError on other errors. + */ +auto Util_cut(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + const auto list = lines(ctx, split(ctx), limit(ctx, 1, "maxc", 72)); + const auto maxl = limit(ctx, 2, "maxl", INT_MAX); + + if (list.size() > static_cast<std::size_t>(maxl)) + return 0; + + // Empty list but lines() returns at least one. + if (list.size() == 1 && list[0].empty()) { + duk_push_array(ctx); + return 1; + } + + return duk::push(ctx, list); + }); +} + +// }}} + +// {{{ Irccd.Util.format + +/* + * Function: Irccd.Util.format(text, parameters) + * -------------------------------------------------------- + * + * Format a string with templates. + * + * Arguments: + * - input, the text to update, + * - params, the parameters. + * Returns: + * The converted text. + * Throws: + * - Irccd.SystemError on errors. + */ +auto Util_format(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, string_util::format(duk::get<std::string>(ctx, 0), subst(ctx, 1))); + }); +} + +// }}} + +// {{{ Irccd.Util.splituser + +/* + * Function: Irccd.Util.splituser(ident) + * -------------------------------------------------------- + * + * Return the nickname part from a full username. + * + * Arguments: + * - ident, the full identity. + * Returns: + * The nickname. + * Throws: + * - Irccd.SystemError on errors. + */ +auto Util_splituser(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, user::parse(duk::require<std::string>(ctx, 0)).nick); + }); +} + +// }}} + +// {{{ Irccd.Util.splithost + +/* + * Function: Irccd.Util.splithost(ident) + * -------------------------------------------------------- + * + * Return the hostname part from a full username. + * + * Arguments: + * - ident, the full identity. + * Returns: + * The hostname. + * Throws: + * - Irccd.SystemError on errors. + */ +auto Util_splithost(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, user::parse(duk::require<std::string>(ctx, 0)).host); + }); +} + +// }}} + +const duk_function_list_entry functions[] = { + { "cut", Util_cut, DUK_VARARGS }, + { "format", Util_format, DUK_VARARGS }, + { "splituser", Util_splituser, 1 }, + { "splithost", Util_splithost, 1 }, + { nullptr, nullptr, 0 } +}; + +} // !namespace + +auto util_api::get_name() const noexcept -> std::string_view +{ + return "Irccd.Util"; +} + +void util_api::load(bot&, std::shared_ptr<plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + duk_get_global_string(plugin->get_context(), "Irccd"); + duk_push_object(plugin->get_context()); + duk_put_function_list(plugin->get_context(), -1, functions); + duk_put_prop_string(plugin->get_context(), -2, "Util"); + duk_pop(plugin->get_context()); +} + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/util_api.hpp Thu Nov 29 21:28:48 2018 +0100 @@ -0,0 +1,50 @@ +/* + * util_api.hpp -- Irccd.Util API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_UTIL_API_HPP +#define IRCCD_JS_UTIL_API_HPP + +/** + * \file util_api.hpp + * \brief Irccd.Util Javascript API. + */ + +#include "api.hpp" + +namespace irccd::js { + +/** + * \ingroup js-api + * \brief Irccd.Util Javascript API. + */ +class util_api : public api { +public: + /** + * \copydoc api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc api::load + */ + void load(daemon::bot& bot, std::shared_ptr<plugin> plugin) override; +}; + +} // !irccd::js + +#endif // !IRCCD_JS_UTIL_API_HPP
--- a/libirccd-js/irccd/js/util_js_api.cpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,354 +0,0 @@ -/* - * util_js_api.cpp -- Irccd.Util API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <climits> - -#include <irccd/string_util.hpp> - -#include "irccd_js_api.hpp" -#include "js_plugin.hpp" -#include "util_js_api.hpp" - -using irccd::daemon::bot; -using irccd::daemon::irc::user; - -namespace irccd::js { - -namespace { - -// {{{ subst - -/* - * Read parameters for Irccd.Util.format function, the object is defined as - * following: - * - * { - * date: the date object - * flags: the flags (not implemented yet) - * field1: a field to substitute in #{} pattern - * field2: a field to substitute in #{} pattern - * fieldn: ... - * } - */ -auto subst(duk_context* ctx, int index) -> string_util::subst -{ - string_util::subst params; - - if (!duk_is_object(ctx, index)) - return params; - - duk_enum(ctx, index, 0); - - while (duk_next(ctx, -1, true)) { - if (duk::get<std::string>(ctx, -2) == "date") - params.time = static_cast<time_t>(duk_get_number(ctx, -1) / 1000); - else - params.keywords.insert({ - duk::get<std::string>(ctx, -2), - duk::get<std::string>(ctx, -1) - }); - - duk_pop_n(ctx, 2); - } - - return params; -} - -// }}} - -// {{{ split - -/* - * split (for Irccd.Util.cut) - * ------------------------------------------------------------------ - * - * Extract individual tokens in array or a whole string as a std:::vector. - */ -auto split(duk_context* ctx) -> std::vector<std::string> -{ - duk_require_type_mask(ctx, 0, DUK_TYPE_MASK_OBJECT | DUK_TYPE_MASK_STRING); - - std::vector<std::string> result; - std::string pattern = " \t\n"; - - if (duk_is_string(ctx, 0)) - result = string_util::split(duk::get<std::string>(ctx, 0), pattern); - else if (duk_is_array(ctx, 0)) { - duk_enum(ctx, 0, DUK_ENUM_ARRAY_INDICES_ONLY); - - while (duk_next(ctx, -1, 1)) { - // Split individual tokens as array if spaces are found. - const auto tmp = string_util::split(duk_to_string(ctx, -1), pattern); - - result.insert(result.end(), tmp.begin(), tmp.end()); - duk_pop_2(ctx); - } - } - - return result; -} - -// }}} - -// {{{ limit - -/* - * limit (for Irccd.Util.cut) - * ------------------------------------------------------------------ - * - * Get the maxl/maxc argument. - * - * The argument value is the default and also used as the result returned. - */ -auto limit(duk_context* ctx, int index, const char* name, int value) -> int -{ - if (duk_get_top(ctx) < index || !duk_is_number(ctx, index)) - return value; - - value = duk_to_int(ctx, index); - - if (value <= 0) - duk_error(ctx, DUK_ERR_RANGE_ERROR, "argument %d (%s) must be positive", index, name); - - return value; -} - -// }}} - -// {{{ lines - -/* - * lines (for Irccd.Util.cut) - * ------------------------------------------------------------------ - * - * Build a list of lines. - * - * Several cases possible: - * - * - s is the current line - * - abc is the token to add - * - * s = "" (new line) - * s -> "abc" - * - * s = "hello world" (enough room) - * s -> "hello world abc" - * - * s = "hello world" (not enough room: maxc is smaller) - * s+1 = "abc" - */ -auto lines(duk_context* ctx, const std::vector<std::string>& tokens, int maxc) -> std::vector<std::string> -{ - std::vector<std::string> result{""}; - - for (const auto& s : tokens) { - if (s.length() > static_cast<std::size_t>(maxc)) - duk_error(ctx, DUK_ERR_RANGE_ERROR, "word '%s' could not fit in maxc limit (%d)", s.c_str(), maxc); - - // Compute the length required (prepend a space if needed) - auto required = s.length() + (result.back().empty() ? 0 : 1); - - if (result.back().length() + required > static_cast<std::size_t>(maxc)) - result.push_back(s); - else { - if (!result.back().empty()) - result.back() += ' '; - - result.back() += s; - } - } - - return result; -} - -// }}} - -// {{{ wrap - -template <typename Handler> -auto wrap(duk_context* ctx, Handler handler) -> duk_ret_t -{ - try { - return handler(); - } catch (const std::system_error& ex) { - duk::raise(ctx, ex); - } catch (const std::exception& ex) { - duk::raise(ctx, ex); - } - - return 0; -} - -// }}} - -// {{{ Irccd.Util.cut - -/* - * Function: Irccd.Util.cut(data, maxc, maxl) - * -------------------------------------------------------- - * - * Cut a piece of data into several lines. - * - * The argument data is a string or a list of strings. In any case, all strings - * are first splitted by spaces and trimmed. This ensure that useless - * whitespaces are discarded. - * - * The argument maxc controls the maximum of characters allowed per line, it can - * be a positive integer. If undefined is given, a default of 72 is used. - * - * The argument maxl controls the maximum of lines allowed. It can be a positive - * integer or undefined for an infinite list. - * - * If maxl is used as a limit and the data can not fit within the bounds, - * undefined is returned. - * - * An empty list may be returned if empty strings were found. - * - * Arguments: - * - data, a string or an array of strings, - * - maxc, max number of colums (Optional, default: 72), - * - maxl, max number of lines (Optional, default: undefined). - * Returns: - * A list of strings ready to be sent or undefined if the data is too big. - * Throws: - * - RangeError if maxl or maxc are negative numbers, - * - RangeError if one word length was bigger than maxc, - * - TypeError if data is not a string or a list of strings, - * - Irccd.SystemError on other errors. - */ -auto Util_cut(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - const auto list = lines(ctx, split(ctx), limit(ctx, 1, "maxc", 72)); - const auto maxl = limit(ctx, 2, "maxl", INT_MAX); - - if (list.size() > static_cast<std::size_t>(maxl)) - return 0; - - // Empty list but lines() returns at least one. - if (list.size() == 1 && list[0].empty()) { - duk_push_array(ctx); - return 1; - } - - return duk::push(ctx, list); - }); -} - -// }}} - -// {{{ Irccd.Util.format - -/* - * Function: Irccd.Util.format(text, parameters) - * -------------------------------------------------------- - * - * Format a string with templates. - * - * Arguments: - * - input, the text to update, - * - params, the parameters. - * Returns: - * The converted text. - * Throws: - * - Irccd.SystemError on errors. - */ -auto Util_format(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - return duk::push(ctx, string_util::format(duk::get<std::string>(ctx, 0), subst(ctx, 1))); - }); -} - -// }}} - -// {{{ Irccd.Util.splituser - -/* - * Function: Irccd.Util.splituser(ident) - * -------------------------------------------------------- - * - * Return the nickname part from a full username. - * - * Arguments: - * - ident, the full identity. - * Returns: - * The nickname. - * Throws: - * - Irccd.SystemError on errors. - */ -auto Util_splituser(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - return duk::push(ctx, user::parse(duk::require<std::string>(ctx, 0)).nick); - }); -} - -// }}} - -// {{{ Irccd.Util.splithost - -/* - * Function: Irccd.Util.splithost(ident) - * -------------------------------------------------------- - * - * Return the hostname part from a full username. - * - * Arguments: - * - ident, the full identity. - * Returns: - * The hostname. - * Throws: - * - Irccd.SystemError on errors. - */ -auto Util_splithost(duk_context* ctx) -> duk_ret_t -{ - return wrap(ctx, [&] { - return duk::push(ctx, user::parse(duk::require<std::string>(ctx, 0)).host); - }); -} - -// }}} - -const duk_function_list_entry functions[] = { - { "cut", Util_cut, DUK_VARARGS }, - { "format", Util_format, DUK_VARARGS }, - { "splituser", Util_splituser, 1 }, - { "splithost", Util_splithost, 1 }, - { nullptr, nullptr, 0 } -}; - -} // !namespace - -auto util_js_api::get_name() const noexcept -> std::string_view -{ - return "Irccd.Util"; -} - -void util_js_api::load(bot&, std::shared_ptr<js_plugin> plugin) -{ - duk::stack_guard sa(plugin->get_context()); - - duk_get_global_string(plugin->get_context(), "Irccd"); - duk_push_object(plugin->get_context()); - duk_put_function_list(plugin->get_context(), -1, functions); - duk_put_prop_string(plugin->get_context(), -2, "Util"); - duk_pop(plugin->get_context()); -} - -} // !irccd::js
--- a/libirccd-js/irccd/js/util_js_api.hpp Mon Nov 26 21:53:27 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * util_js_api.hpp -- Irccd.Util API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_UTIL_JS_API_HPP -#define IRCCD_JS_UTIL_JS_API_HPP - -/** - * \file util_js_api.hpp - * \brief Irccd.Util Javascript API. - */ - -#include "js_api.hpp" - -namespace irccd::js { - -/** - * \ingroup js-api - * \brief Irccd.Util Javascript API. - */ -class util_js_api : public js_api { -public: - /** - * \copydoc js_api::get_name - */ - auto get_name() const noexcept -> std::string_view override; - - /** - * \copydoc js_api::load - */ - void load(daemon::bot& bot, std::shared_ptr<js_plugin> plugin) override; -}; - -} // !irccd::js - -#endif // !IRCCD_JS_UTIL_JS_API_HPP
--- a/libirccd-test/irccd/test/js_fixture.cpp Mon Nov 26 21:53:27 2018 +0100 +++ b/libirccd-test/irccd/test/js_fixture.cpp Thu Nov 29 21:28:48 2018 +0100 @@ -21,9 +21,9 @@ namespace irccd::test { js_fixture::js_fixture(const std::string& path) - : plugin_(new js::js_plugin("test", path)) + : plugin_(new js::plugin("test", path)) { - for (const auto& f : js::js_api::registry()) + for (const auto& f : js::api::registry()) f()->load(bot_, plugin_); if (!path.empty())
--- a/libirccd-test/irccd/test/js_fixture.hpp Mon Nov 26 21:53:27 2018 +0100 +++ b/libirccd-test/irccd/test/js_fixture.hpp Thu Nov 29 21:28:48 2018 +0100 @@ -26,10 +26,10 @@ #include "irccd_fixture.hpp" -#include <irccd/js/js_plugin.hpp> -#include <irccd/js/js_api.hpp> +#include <irccd/js/plugin.hpp> +#include <irccd/js/api.hpp> -#include <irccd/js/irccd_js_api.hpp> +#include <irccd/js/irccd_api.hpp> namespace irccd::test { @@ -41,7 +41,7 @@ /** * \brief Javascript plugin. */ - std::shared_ptr<js::js_plugin> plugin_; + std::shared_ptr<js::plugin> plugin_; /** * Constructor.
--- a/libirccd-test/irccd/test/js_plugin_fixture.cpp Mon Nov 26 21:53:27 2018 +0100 +++ b/libirccd-test/irccd/test/js_plugin_fixture.cpp Thu Nov 29 21:28:48 2018 +0100 @@ -1,5 +1,5 @@ /* - * js_plugin_fixture.cpp -- test fixture helper for Javascript plugins + * plugin_fixture.cpp -- test fixture helper for Javascript plugins * * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> * @@ -22,22 +22,22 @@ #include <irccd/daemon/plugin_service.hpp> #include <irccd/daemon/server_service.hpp> -#include <irccd/js/directory_js_api.hpp> -#include <irccd/js/elapsed_timer_js_api.hpp> -#include <irccd/js/file_js_api.hpp> -#include <irccd/js/irccd_js_api.hpp> -#include <irccd/js/js_plugin.hpp> -#include <irccd/js/logger_js_api.hpp> -#include <irccd/js/plugin_js_api.hpp> -#include <irccd/js/server_js_api.hpp> -#include <irccd/js/system_js_api.hpp> -#include <irccd/js/timer_js_api.hpp> -#include <irccd/js/unicode_js_api.hpp> -#include <irccd/js/util_js_api.hpp> +#include <irccd/js/directory_api.hpp> +#include <irccd/js/elapsed_timer_api.hpp> +#include <irccd/js/file_api.hpp> +#include <irccd/js/irccd_api.hpp> +#include <irccd/js/plugin.hpp> +#include <irccd/js/logger_api.hpp> +#include <irccd/js/plugin_api.hpp> +#include <irccd/js/server_api.hpp> +#include <irccd/js/system_api.hpp> +#include <irccd/js/timer_api.hpp> +#include <irccd/js/unicode_api.hpp> +#include <irccd/js/util_api.hpp> #include "js_plugin_fixture.hpp" -using irccd::js::js_plugin; +using irccd::js::plugin; using irccd::daemon::logger::silent_sink; @@ -46,7 +46,7 @@ js_plugin_fixture::js_plugin_fixture(std::string path) : server_(std::make_shared<mock_server>(service_, "test", "local")) { - plugin_ = std::make_unique<js_plugin>("test", std::move(path)); + plugin_ = std::make_unique<plugin>("test", std::move(path)); bot_.set_log(std::make_unique<silent_sink>()); bot_.get_log().set_verbose(false); @@ -57,7 +57,7 @@ server_->set_nickname("irccd"); server_->clear(); - for (const auto& f : js::js_api::registry()) + for (const auto& f : js::api::registry()) f()->load(bot_, plugin_); plugin_->open();
--- a/libirccd-test/irccd/test/js_plugin_fixture.hpp Mon Nov 26 21:53:27 2018 +0100 +++ b/libirccd-test/irccd/test/js_plugin_fixture.hpp Thu Nov 29 21:28:48 2018 +0100 @@ -26,7 +26,7 @@ #include <irccd/daemon/bot.hpp> -#include <irccd/js/js_plugin.hpp> +#include <irccd/js/plugin.hpp> #include "mock_server.hpp" @@ -41,7 +41,7 @@ protected: boost::asio::io_service service_; //!< The I/O service. daemon::bot bot_{service_}; //!< The irccd instance. - std::shared_ptr<js::js_plugin> plugin_; //!< The plugin to test. + std::shared_ptr<js::plugin> plugin_; //!< The plugin to test. std::shared_ptr<mock_server> server_; //!< A mock server. public:
--- a/tests/src/libirccd-js/js-plugin/main.cpp Mon Nov 26 21:53:27 2018 +0100 +++ b/tests/src/libirccd-js/js-plugin/main.cpp Thu Nov 29 21:28:48 2018 +0100 @@ -21,17 +21,15 @@ #include <irccd/daemon/plugin_service.hpp> -#include <irccd/js/js_api.hpp> -#include <irccd/js/js_plugin.hpp> +#include <irccd/js/api.hpp> +#include <irccd/js/plugin.hpp> #include <irccd/test/irccd_fixture.hpp> -using irccd::daemon::plugin; - using irccd::test::irccd_fixture; -using irccd::js::js_plugin; -using irccd::js::js_api; +using irccd::js::plugin; +using irccd::js::api; namespace irccd { @@ -39,13 +37,13 @@ class js_plugin_fixture : public irccd_fixture { protected: - std::shared_ptr<js_plugin> plugin_; + std::shared_ptr<plugin> plugin_; void load(const std::string& path) { - plugin_ = std::make_unique<js_plugin>("test", path); + plugin_ = std::make_unique<plugin>("test", path); - for (const auto& f : js_api::registry()) + for (const auto& f : api::registry()) f()->load(bot_, plugin_); plugin_->open(); @@ -103,15 +101,15 @@ class js_plugin_loader_fixture : public irccd_fixture { protected: - std::shared_ptr<plugin> plugin_; + std::shared_ptr<daemon::plugin> plugin_; js_plugin_loader_fixture() { bot_.set_config(config(CMAKE_CURRENT_SOURCE_DIR "/irccd.conf")); - auto loader = std::make_unique<js::js_plugin_loader>(bot_); + auto loader = std::make_unique<js::plugin_loader>(bot_); - for (const auto& f : js::js_api::registry()) + for (const auto& f : js::api::registry()) loader->get_modules().push_back(f()); bot_.plugins().add_loader(std::move(loader));