Mercurial > irccd
changeset 134:dc7d6ba08122
Irccd: load modules and apply them to the plugins
line wrap: on
line diff
--- a/irccd/main.cpp Fri May 13 12:06:06 2016 +0200 +++ b/irccd/main.cpp Sun May 15 21:36:04 2016 +0200 @@ -249,9 +249,7 @@ } // [plugin] section. - for (const auto &plugin : config.loadPlugins()) { - instance->pluginService().add(plugin); - } + config.loadPlugins(*instance); } } // !namespace @@ -266,9 +264,7 @@ instance = std::make_unique<Irccd>(); try { - Config cfg = open(options); - - load(cfg, options); + load(open(options), options); } catch (const std::exception &ex) { log::warning() << "irccd: " << ex.what() << std::endl; return 1;
--- a/lib/irccd/CMakeSources.cmake Fri May 13 12:06:06 2016 +0200 +++ b/lib/irccd/CMakeSources.cmake Sun May 15 21:36:04 2016 +0200 @@ -36,18 +36,19 @@ ${CMAKE_CURRENT_LIST_DIR}/irccdctl.hpp ${CMAKE_CURRENT_LIST_DIR}/js.hpp ${CMAKE_CURRENT_LIST_DIR}/json.hpp - ${CMAKE_CURRENT_LIST_DIR}/js-directory.hpp - ${CMAKE_CURRENT_LIST_DIR}/js-elapsed-timer.hpp - ${CMAKE_CURRENT_LIST_DIR}/js-file.hpp - ${CMAKE_CURRENT_LIST_DIR}/js-irccd.hpp - ${CMAKE_CURRENT_LIST_DIR}/js-logger.hpp - ${CMAKE_CURRENT_LIST_DIR}/js-plugin.hpp - ${CMAKE_CURRENT_LIST_DIR}/js-server.hpp - ${CMAKE_CURRENT_LIST_DIR}/js-system.hpp - ${CMAKE_CURRENT_LIST_DIR}/js-timer.hpp - ${CMAKE_CURRENT_LIST_DIR}/js-unicode.hpp - ${CMAKE_CURRENT_LIST_DIR}/js-util.hpp + ${CMAKE_CURRENT_LIST_DIR}/mod-directory.hpp + ${CMAKE_CURRENT_LIST_DIR}/mod-elapsed-timer.hpp + ${CMAKE_CURRENT_LIST_DIR}/mod-file.hpp + ${CMAKE_CURRENT_LIST_DIR}/mod-irccd.hpp + ${CMAKE_CURRENT_LIST_DIR}/mod-logger.hpp + ${CMAKE_CURRENT_LIST_DIR}/mod-plugin.hpp + ${CMAKE_CURRENT_LIST_DIR}/mod-server.hpp + ${CMAKE_CURRENT_LIST_DIR}/mod-system.hpp + ${CMAKE_CURRENT_LIST_DIR}/mod-timer.hpp + ${CMAKE_CURRENT_LIST_DIR}/mod-unicode.hpp + ${CMAKE_CURRENT_LIST_DIR}/mod-util.hpp ${CMAKE_CURRENT_LIST_DIR}/logger.hpp + ${CMAKE_CURRENT_LIST_DIR}/module.hpp ${CMAKE_CURRENT_LIST_DIR}/options.hpp ${CMAKE_CURRENT_LIST_DIR}/path.hpp ${CMAKE_CURRENT_LIST_DIR}/plugin.hpp @@ -62,6 +63,7 @@ ${CMAKE_CURRENT_LIST_DIR}/server-state-disconnected.hpp ${CMAKE_CURRENT_LIST_DIR}/service.hpp ${CMAKE_CURRENT_LIST_DIR}/service-interrupt.hpp + ${CMAKE_CURRENT_LIST_DIR}/service-module.hpp ${CMAKE_CURRENT_LIST_DIR}/service-plugin.hpp ${CMAKE_CURRENT_LIST_DIR}/service-rule.hpp ${CMAKE_CURRENT_LIST_DIR}/service-server.hpp @@ -112,17 +114,17 @@ ${CMAKE_CURRENT_LIST_DIR}/irccd.cpp ${CMAKE_CURRENT_LIST_DIR}/irccdctl.cpp ${CMAKE_CURRENT_LIST_DIR}/json.cpp - ${CMAKE_CURRENT_LIST_DIR}/js-directory.cpp - ${CMAKE_CURRENT_LIST_DIR}/js-elapsed-timer.cpp - ${CMAKE_CURRENT_LIST_DIR}/js-file.cpp - ${CMAKE_CURRENT_LIST_DIR}/js-irccd.cpp - ${CMAKE_CURRENT_LIST_DIR}/js-logger.cpp - ${CMAKE_CURRENT_LIST_DIR}/js-plugin.cpp - ${CMAKE_CURRENT_LIST_DIR}/js-server.cpp - ${CMAKE_CURRENT_LIST_DIR}/js-system.cpp - ${CMAKE_CURRENT_LIST_DIR}/js-timer.cpp - ${CMAKE_CURRENT_LIST_DIR}/js-unicode.cpp - ${CMAKE_CURRENT_LIST_DIR}/js-util.cpp + ${CMAKE_CURRENT_LIST_DIR}/mod-directory.cpp + ${CMAKE_CURRENT_LIST_DIR}/mod-elapsed-timer.cpp + ${CMAKE_CURRENT_LIST_DIR}/mod-file.cpp + ${CMAKE_CURRENT_LIST_DIR}/mod-irccd.cpp + ${CMAKE_CURRENT_LIST_DIR}/mod-logger.cpp + ${CMAKE_CURRENT_LIST_DIR}/mod-plugin.cpp + ${CMAKE_CURRENT_LIST_DIR}/mod-server.cpp + ${CMAKE_CURRENT_LIST_DIR}/mod-system.cpp + ${CMAKE_CURRENT_LIST_DIR}/mod-timer.cpp + ${CMAKE_CURRENT_LIST_DIR}/mod-unicode.cpp + ${CMAKE_CURRENT_LIST_DIR}/mod-util.cpp ${CMAKE_CURRENT_LIST_DIR}/logger.cpp ${CMAKE_CURRENT_LIST_DIR}/options.cpp ${CMAKE_CURRENT_LIST_DIR}/path.cpp @@ -134,6 +136,7 @@ ${CMAKE_CURRENT_LIST_DIR}/server-state-connecting.cpp ${CMAKE_CURRENT_LIST_DIR}/server-state-disconnected.cpp ${CMAKE_CURRENT_LIST_DIR}/service-interrupt.cpp + ${CMAKE_CURRENT_LIST_DIR}/service-module.cpp ${CMAKE_CURRENT_LIST_DIR}/service-plugin.cpp ${CMAKE_CURRENT_LIST_DIR}/service-rule.cpp ${CMAKE_CURRENT_LIST_DIR}/service-server.cpp
--- a/lib/irccd/config.cpp Fri May 13 12:06:06 2016 +0200 +++ b/lib/irccd/config.cpp Sun May 15 21:36:04 2016 +0200 @@ -28,6 +28,7 @@ #include "plugin-js.hpp" #include "rule.hpp" #include "server.hpp" +#include "service-plugin.hpp" #include "sysconfig.hpp" #include "transport-server.hpp" #include "util.hpp" @@ -610,39 +611,20 @@ return servers; } -std::vector<std::shared_ptr<Plugin>> Config::loadPlugins() const +void Config::loadPlugins(Irccd &irccd) const { - std::vector<std::shared_ptr<Plugin>> plugins; - - // Plugins are defined in only one section. auto it = m_document.find("plugins"); - if (it == m_document.end()) { - return plugins; - } - - for (const auto &option : *it) { - std::string name = option.key(); - std::string path = option.value(); - - try { - log::info("plugin {}: loading"_format(name)); + if (it != m_document.end()) { + for (const auto &option : *it) { + if (!util::isIdentifierValid(option.key())) { + continue; + } - if (path.empty()) { - // plugins.push_back(Plugin::find(name, findPluginConfig(name))); - } else { - log::info("plugin {}: trying {}"_format(name, path)); - plugins.push_back(std::make_shared<JsPlugin>(name, path, findPluginConfig(name))); - } - } catch (const duk::ErrorInfo &ex) { - log::warning("plugin {}: {}"_format(option.key(), ex.what())); - log::warning("plugin {}: {}"_format(option.key(), ex.stack)); - } catch (const std::exception &ex) { - log::warning("plugin {}: {}"_format(option.key(), ex.what())); + irccd.pluginService().configure(option.key(), findPluginConfig(option.key())); + irccd.pluginService().load(option.key(), option.value()); } } - - return plugins; } } // !irccd
--- a/lib/irccd/config.hpp Fri May 13 12:06:06 2016 +0200 +++ b/lib/irccd/config.hpp Sun May 15 21:36:04 2016 +0200 @@ -33,11 +33,10 @@ namespace irccd { +class Irccd; class Rule; - class Server; class ServerIdentity; - class TransportServer; /** @@ -164,9 +163,10 @@ /** * Get the list of defined plugins. * + * \param irccd the irccd instance * \return the list of plugins */ - std::vector<std::shared_ptr<Plugin>> loadPlugins() const; + void loadPlugins(Irccd &irccd) const; }; } // !irccd
--- a/lib/irccd/irccd.cpp Fri May 13 12:06:06 2016 +0200 +++ b/lib/irccd/irccd.cpp Sun May 15 21:36:04 2016 +0200 @@ -19,6 +19,7 @@ #include "irccd.hpp" #include "logger.hpp" #include "service-interrupt.hpp" +#include "service-module.hpp" #include "service-plugin.hpp" #include "service-rule.hpp" #include "service-server.hpp" @@ -36,6 +37,7 @@ , m_serverService(std::make_shared<ServerService>(*this)) , m_transportService(std::make_shared<TransportService>(*this)) , m_ruleService(std::make_shared<RuleService>()) + , m_moduleService(std::make_shared<ModuleService>()) , m_pluginService(std::make_shared<PluginService>(*this)) { m_services.push_back(m_interruptService);
--- a/lib/irccd/irccd.hpp Fri May 13 12:06:06 2016 +0200 +++ b/lib/irccd/irccd.hpp Sun May 15 21:36:04 2016 +0200 @@ -37,6 +37,7 @@ class InterruptService; class Irccd; +class ModuleService; class PluginService; class RuleService; class ServerService; @@ -59,6 +60,7 @@ std::shared_ptr<ServerService> m_serverService; std::shared_ptr<TransportService> m_transportService; std::shared_ptr<RuleService> m_ruleService; + std::shared_ptr<ModuleService> m_moduleService; std::shared_ptr<PluginService> m_pluginService; std::vector<std::shared_ptr<Service>> m_services; @@ -116,6 +118,16 @@ } /** + * Access the module service. + * + * \return the service + */ + inline ModuleService &moduleService() noexcept + { + return *m_moduleService; + } + + /** * Access the plugin service. * * \return the service
--- a/lib/irccd/js-directory.cpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,383 +0,0 @@ -/* - * js-directory.cpp -- Irccd.Directory API - * - * Copyright (c) 2013-2016 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 <cerrno> -#include <cstdio> -#include <cstring> -#include <fstream> -#include <regex> -#include <stdexcept> -#include <string> - -#include "fs.hpp" -#include "js.hpp" -#include "js-irccd.hpp" -#include "path.hpp" -#include "sysconfig.hpp" - -namespace irccd { - -namespace { - -std::string path(duk::ContextPtr ctx) -{ - duk::push(ctx, duk::This{}); - duk::getProperty<void>(ctx, -1, "path"); - - if (duk::type(ctx, -1) != DUK_TYPE_STRING) { - duk::raise(ctx, duk::TypeError("invalid this binding")); - } - - std::string ret = duk::get<std::string>(ctx, -1); - - if (ret.empty()) { - duk::raise(ctx, duk::TypeError("invalid directory with empty path")); - } - - duk::pop(ctx, 2); - - return ret; -} - -/* - * Find an entry recursively (or not) in a directory using a predicate - * which can be used to test for regular expression, equality. - * - * Do not use this function directly, use: - * - * - findName - * - findRegex - */ -template <typename Pred> -std::string findPath(const std::string &base, bool recursive, Pred pred) -{ - /* - * For performance reason, we first iterate over all entries that are - * not directories to avoid going deeper recursively if the requested - * file is in the current directory. - */ - auto entries = fs::readdir(base); - - for (const auto &entry : entries) { - if (entry.type != fs::Entry::Dir && pred(entry.name)) { - return base + entry.name; - } - } - - if (!recursive) { - return ""; - } - - for (const auto &entry : entries) { - if (entry.type == fs::Entry::Dir) { - std::string next = base + entry.name + fs::separator(); - std::string path = findPath(next, true, pred); - - if (!path.empty()) { - return path; - } - } - } - - return ""; -} - -/* - * Helper for finding by equality. - */ -std::string findName(std::string base, const std::string &pattern, bool recursive) -{ - return findPath(base, recursive, [&] (const std::string &entryname) -> bool { - return pattern == entryname; - }); -} - -/* - * Helper for finding by regular expression - */ -std::string findRegex(const std::string &base, std::string pattern, bool recursive) -{ - std::regex regexp(pattern, std::regex::ECMAScript); - std::smatch smatch; - - return findPath(base, recursive, [&] (const std::string &entryname) -> bool { - return std::regex_match(entryname, smatch, regexp); - }); -} - -/* - * Generic find function for: - * - * - Directory.find - * - Directory.prototype.find - * - * The patternIndex is the argument where to test if the argument is a regex or a string. - */ -duk::Ret find(duk::ContextPtr ctx, std::string base, bool recursive, int patternIndex) -{ - base = path::clean(base); - - try { - std::string path; - - if (duk::is<std::string>(ctx, patternIndex)) { - path = findName(base, duk::get<std::string>(ctx, patternIndex), recursive); - } else { - /* Check if it's a valid RegExp object */ - duk::getGlobal<void>(ctx, "RegExp"); - - bool isRegex = duk::instanceof(ctx, patternIndex, -1); - - duk::pop(ctx); - - if (isRegex) { - path = findRegex(base, duk::getProperty<std::string>(ctx, patternIndex, "source"), recursive); - } else { - duk::raise(ctx, duk::TypeError("pattern must be a string or a regex expression")); - } - } - - if (path.empty()) { - return 0; - } - - duk::push(ctx, path); - } catch (const std::exception &ex) { - duk::raise(ctx, duk::Error(ex.what())); - } - - return 1; -} - -/* - * Generic remove function for: - * - * - Directory.remove - * - Directory.prototype.remove - */ -duk::Ret remove(duk::ContextPtr ctx, const std::string &path, bool recursive) -{ - if (!fs::isDirectory(path)) { - duk::raise(ctx, SystemError(EINVAL, "not a directory")); - } - - if (!recursive) { -#if defined(_WIN32) - ::RemoveDirectory(path.c_str()); -#else - ::remove(path.c_str()); -#endif - } else { - fs::rmdir(path.c_str()); - } - - return 0; -} - -/* - * Method: Directory.find(pattern, recursive) - * -------------------------------------------------------- - * - * Synonym of 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: - * - Any exception on error. - */ -duk::Ret methodFind(duk::ContextPtr ctx) -{ - return find(ctx, path(ctx), duk::optional<bool>(ctx, 1, false), 0); -} - -/* - * Method: Directory.remove(recursive) - * -------------------------------------------------------- - * - * Synonym of Directory.remove(recursive) but the path is taken from the - * directory object. - * - * Arguments: - * - recursive, recursively or not (default: false). - * Throws: - * - Any exception on error. - */ -duk::Ret methodRemove(duk::ContextPtr ctx) -{ - return remove(ctx, path(ctx), duk::optional<bool>(ctx, 0, false)); -} - -const duk::FunctionMap methods{ - { "find", { methodFind, DUK_VARARGS } }, - { "remove", { methodRemove, 1 } } -}; - -/* -------------------------------------------------------- - * Directory "static" functions - * -------------------------------------------------------- */ - -/* - * Function: Irccd.Directory(path, flags) [constructor] - * -------------------------------------------------------- - * - * Opens and read the directory at the specified path. - * - * Arguments: - * - path, the path to the directory, - * - flags, the optional flags (default: 0). - * Throws: - * - Any exception on error - */ -duk::Ret constructor(duk::ContextPtr ctx) -{ - if (!duk_is_constructor_call(ctx)) { - return 0; - } - - try { - std::string path = duk::require<std::string>(ctx, 0); - std::int8_t flags = duk::optional<int>(ctx, 1, 0); - - if (!fs::isDirectory(path)) { - duk::raise(ctx, SystemError(EINVAL, "not a directory")); - } - - std::vector<fs::Entry> list = fs::readdir(path, flags); - - duk::push(ctx, duk::This{}); - duk::push(ctx, "count"); - duk::push(ctx, (int)list.size()); - duk::defineProperty(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); - duk::push(ctx, "path"); - duk::push(ctx, path); - duk::defineProperty(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); - duk::push(ctx, "entries"); - duk::push(ctx, duk::Array{}); - - for (unsigned i = 0; i < list.size(); ++i) { - duk::push(ctx, duk::Object{}); - duk::putProperty(ctx, -1, "name", list[i].name); - duk::putProperty(ctx, -1, "type", static_cast<int>(list[i].type)); - duk::putProperty(ctx, -2, (int)i); - } - - duk::defineProperty(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); - } catch (const std::exception &ex) { - duk::raise(ctx, SystemError(errno, ex.what())); - } - - return 0; -} - -/* - * 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. - */ -duk::Ret funcFind(duk::ContextPtr ctx) -{ - return find(ctx, duk::require<std::string>(ctx, 0), duk::optional<bool>(ctx, 2, false), 1); -} - -/* - * 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: - * - Any exception on error. - */ -duk::Ret funcRemove(duk::ContextPtr ctx) -{ - return remove(ctx, duk::require<std::string>(ctx, 0), duk::optional<bool>(ctx, 1, false)); -} - -/* - * 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, - * - mode, the mode, not available on all platforms. - * Throws: - * - Any exception on error. - */ -duk::Ret funcMkdir(duk::ContextPtr ctx) -{ - try { - fs::mkdir(duk::require<std::string>(ctx, 0), duk::optional<int>(ctx, 1, 0700)); - } catch (const std::exception &ex) { - duk::raise(ctx, SystemError(errno, ex.what())); - } - - return 0; -} - -const duk::FunctionMap functions{ - { "find", { funcFind, DUK_VARARGS } }, - { "mkdir", { funcMkdir, DUK_VARARGS } }, - { "remove", { funcRemove, DUK_VARARGS } } -}; - -const duk::Map<int> constants{ - { "Dot", static_cast<int>(fs::Dot) }, - { "DotDot", static_cast<int>(fs::DotDot) }, - { "TypeUnknown", static_cast<int>(fs::Entry::Unknown) }, - { "TypeDir", static_cast<int>(fs::Entry::Dir) }, - { "TypeFile", static_cast<int>(fs::Entry::File) }, - { "TypeLink", static_cast<int>(fs::Entry::Link) } -}; - -} // !namespace - -void loadJsDirectory(duk::ContextPtr ctx) noexcept -{ - duk::StackAssert sa(ctx); - - duk::getGlobal<void>(ctx, "Irccd"); - duk::push(ctx, duk::Function{constructor, 2}); - duk::push(ctx, constants); - duk::push(ctx, functions); - duk::putProperty(ctx, -1, "separator", std::string{fs::separator()}); - duk::push(ctx, duk::Object{}); - duk::push(ctx, methods); - duk::putProperty(ctx, -2, "prototype"); - duk::putProperty(ctx, -2, "Directory"); - duk::pop(ctx); -} - -} // !irccd
--- a/lib/irccd/js-directory.hpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -/* - * js-directory.hpp -- Irccd.Directory API - * - * Copyright (c) 2013-2016 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_HPP -#define IRCCD_JS_DIRECTORY_HPP - -/** - * \file js-directory.hpp - * \brief Irccd.Directory JavaScript API. - */ - -#include "js.hpp" - -namespace irccd { - -/** - * Load the module. - * - * \param ctx the context. - */ -void loadJsDirectory(duk::ContextPtr ctx) noexcept; - -} // !irccd - -#endif // !IRCCD_JS_DIRECTORY_HPP -
--- a/lib/irccd/js-elapsed-timer.cpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,134 +0,0 @@ -/* - * js-elapsed-timer.cpp -- Irccd.ElapsedTimer API - * - * Copyright (c) 2013-2016 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 "elapsed-timer.hpp" -#include "js.hpp" - -namespace irccd { - -namespace duk { - -template <> -class TypeTraits<ElapsedTimer> { -public: - static inline std::string name() - { - return "\xff""\xff""ElapsedTimer"; - } - - static inline std::vector<std::string> inherits() - { - return {}; - } -}; - -} // !duk - -namespace { - -/* - * Method: ElapsedTimer.pause - * ------------------------------------------------------------------ - * - * Pause the timer, without resetting the current elapsed time stored. - */ -duk::Ret pause(duk::ContextPtr ctx) -{ - duk::self<duk::Pointer<ElapsedTimer>>(ctx)->pause(); - - return 0; -} - -/* - * Method: ElapsedTimer.reset - * ------------------------------------------------------------------ - * - * Reset the elapsed time to 0, the status is not modified. - */ -duk::Ret reset(duk::ContextPtr ctx) -{ - duk::self<duk::Pointer<ElapsedTimer>>(ctx)->reset(); - - return 0; -} - -/* - * Method: ElapsedTimer.restart - * ------------------------------------------------------------------ - * - * Restart the timer without resetting the current elapsed time. - */ -duk::Ret restart(duk::ContextPtr ctx) -{ - duk::self<duk::Pointer<ElapsedTimer>>(ctx)->restart(); - - return 0; -} - -/* - * Method: ElapsedTimer.elapsed - * ------------------------------------------------------------------ - * - * Get the number of elapsed milliseconds. - * - * Returns: - * The time elapsed. - */ -duk::Ret elapsed(duk::ContextPtr ctx) -{ - duk::push(ctx, (int)duk::self<duk::Pointer<ElapsedTimer>>(ctx)->elapsed()); - - return 1; -} - -/* - * Function: Irccd.ElapsedTimer() [constructor] - * ------------------------------------------------------------------ - * - * Construct a new ElapsedTimer object. - */ -duk::Ret constructor(duk::ContextPtr ctx) -{ - duk::construct(ctx, duk::Pointer<ElapsedTimer>{new ElapsedTimer}); - - return 0; -} - -const duk::FunctionMap methods{ - { "elapsed", { elapsed, 0 } }, - { "pause", { pause, 0 } }, - { "reset", { reset, 0 } }, - { "restart", { restart, 0 } } -}; - -} // !namespace - -void loadJsElapsedTimer(duk::ContextPtr ctx) noexcept -{ - duk::StackAssert sa(ctx); - - duk::getGlobal<void>(ctx, "Irccd"); - duk::push(ctx, duk::Function{constructor, 0}); - duk::push(ctx, duk::Object{}); - duk::push(ctx, methods); - duk::putProperty(ctx, -2, "prototype"); - duk::putProperty(ctx, -2, "ElapsedTimer"); - duk::pop(ctx); -} - -} // !irccd
--- a/lib/irccd/js-elapsed-timer.hpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -/* - * js-elapsed-timer.hpp -- Irccd.ElapsedTimer API - * - * Copyright (c) 2013-2016 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_HPP -#define IRCCD_JS_ELAPSED_TIMER_HPP - -/** - * \file js-elapsed-timer.hpp - * \brief Irccd.ElapsedTimer JavaScript API. - */ - -#include "js.hpp" - -namespace irccd { - -/** - * Load the module. - * - * \param ctx the context. - */ -void loadJsElapsedTimer(duk::ContextPtr ctx) noexcept; - -} // !irccd - -#endif // !IRCCD_JS_ELAPSED_TIMER_HPP -
--- a/lib/irccd/js-file.cpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,626 +0,0 @@ -/* - * js-file.cpp -- Irccd.File API - * - * Copyright (c) 2013-2016 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 <iterator> -#include <vector> - -#include "sysconfig.hpp" - -#if defined(HAVE_STAT) -# include <sys/types.h> -# include <sys/stat.h> -#endif - -#include "fs.hpp" -#include "js-irccd.hpp" -#include "js-file.hpp" - -namespace irccd { - -#if defined(HAVE_STAT) - -/* - * duk::TypeInfo specialization for struct stat - * ------------------------------------------------------------------ - */ - -namespace duk { - -template <> -class TypeTraits<struct stat> { -public: - static void push(ContextPtr ctx, const struct stat &st) - { - duk::push(ctx, Object{}); - -#if defined(HAVE_STAT_ST_ATIME) - duk::putProperty(ctx, -2, "atime", static_cast<int>(st.st_atime)); -#endif -#if defined(HAVE_STAT_ST_BLKSIZE) - duk::putProperty(ctx, -2, "blksize", static_cast<int>(st.st_blksize)); -#endif -#if defined(HAVE_STAT_ST_BLOCKS) - duk::putProperty(ctx, -2, "blocks", static_cast<int>(st.st_blocks)); -#endif -#if defined(HAVE_STAT_ST_CTIME) - duk::putProperty(ctx, -2, "ctime", static_cast<int>(st.st_ctime)); -#endif -#if defined(HAVE_STAT_ST_DEV) - duk::putProperty(ctx, -2, "dev", static_cast<int>(st.st_dev)); -#endif -#if defined(HAVE_STAT_ST_GID) - duk::putProperty(ctx, -2, "gid", static_cast<int>(st.st_gid)); -#endif -#if defined(HAVE_STAT_ST_INO) - duk::putProperty(ctx, -2, "ino", static_cast<int>(st.st_ino)); -#endif -#if defined(HAVE_STAT_ST_MODE) - duk::putProperty(ctx, -2, "mode", static_cast<int>(st.st_mode)); -#endif -#if defined(HAVE_STAT_ST_MTIME) - duk::putProperty(ctx, -2, "mtime", static_cast<int>(st.st_mtime)); -#endif -#if defined(HAVE_STAT_ST_NLINK) - duk::putProperty(ctx, -2, "nlink", static_cast<int>(st.st_nlink)); -#endif -#if defined(HAVE_STAT_ST_RDEV) - duk::putProperty(ctx, -2, "rdev", static_cast<int>(st.st_rdev)); -#endif -#if defined(HAVE_STAT_ST_SIZE) - duk::putProperty(ctx, -2, "size", static_cast<int>(st.st_size)); -#endif -#if defined(HAVE_STAT_ST_UID) - duk::putProperty(ctx, -2, "uid", static_cast<int>(st.st_uid)); -#endif - } -}; - -} // !duk - -#endif // !HAVE_STAT - -namespace { - -/* - * Anonymous helpers. - * ------------------------------------------------------------------ - */ - -/* Remove trailing \r for CRLF line style */ -inline std::string clearCr(std::string input) -{ - if (input.length() > 0 && input.back() == '\r') { - input.pop_back(); - } - - return input; -} - -/* - * File methods. - * ------------------------------------------------------------------ - */ - -/* - * Method: File.basename() - * -------------------------------------------------------- - * - * Synonym of `Irccd.File.basename(path)` but with the path from the file. - * - * Returns: - * The base name. - */ -duk::Ret methodBasename(duk::ContextPtr ctx) -{ - duk::push(ctx, fs::baseName(duk::self<duk::Pointer<File>>(ctx)->path())); - - return 1; -} - -/* - * Method: File.close() - * -------------------------------------------------------- - * - * Force close of the file, automatically called when object is collected. - */ -duk::Ret methodClose(duk::ContextPtr ctx) -{ - duk::self<duk::Pointer<File>>(ctx)->close(); - - return 0; -} - -/* - * Method: File.dirname() - * -------------------------------------------------------- - * - * Synonym of `Irccd.File.dirname(path)` but with the path from the file. - * - * Returns: - * The directory name. - */ -duk::Ret methodDirname(duk::ContextPtr ctx) -{ - duk::push(ctx, fs::dirName(duk::self<duk::Pointer<File>>(ctx)->path())); - - return 1; -} - -/* - * Method: File.lines() - * -------------------------------------------------------- - * - * Read all lines and return an array. - * - * Returns: - * An array with all lines. - * Throws - * - Any exception on error. - */ -duk::Ret methodLines(duk::ContextPtr ctx) -{ - duk::push(ctx, duk::Array{}); - - std::FILE *fp = duk::self<duk::Pointer<File>>(ctx)->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(); - - auto pos = buffer.find('\n'); - - if (pos != std::string::npos) { - duk::putProperty(ctx, -1, i++, clearCr(buffer.substr(0, pos))); - buffer.erase(0, pos + 1); - } - } - - /* Maybe an error in the stream */ - if (std::ferror(fp)) { - duk::raise(ctx, SystemError()); - } - - /* Missing '\n' in end of file */ - if (!buffer.empty()) { - duk::putProperty(ctx, -1, i++, clearCr(buffer)); - } - - return 1; -} - -/* - * Method: File.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: - * - Any exception on error. - */ -duk::Ret methodRead(duk::ContextPtr ctx) -{ - auto amount = duk::optional<int>(ctx, 0, -1); - auto file = duk::self<duk::Pointer<File>>(ctx); - - if (amount == 0 || file->handle() == nullptr) { - return 0; - } - - try { - std::string data; - std::size_t total = 0; - - if (amount < 0) { - std::array<char, 128> buffer; - std::size_t nread; - - while (!std::feof(file->handle())) { - nread = std::fread(&buffer[0], sizeof (buffer[0]), buffer.size(), file->handle()); - - if (std::ferror(file->handle())) { - duk::raise(ctx, SystemError()); - } - - std::copy(buffer.begin(), buffer.begin() + nread, std::back_inserter(data)); - total += nread; - } - } else { - data.resize((std::size_t)amount); - total = std::fread(&data[0], sizeof (data[0]), (std::size_t)amount, file->handle()); - - if (std::ferror(file->handle())) { - duk::raise(ctx, SystemError()); - } - - data.resize(total); - } - - duk::push(ctx, data); - } catch (const std::exception &) { - duk::raise(ctx, SystemError()); - } - - return 1; -} - -/* - * Method: File.readline() - * -------------------------------------------------------- - * - * Read the next line available. - * - * Returns: - * The next line or undefined if eof. - * Throws: - * - Any exception on error. - */ -duk::Ret methodReadline(duk::ContextPtr ctx) -{ - std::FILE *fp = duk::self<duk::Pointer<File>>(ctx)->handle(); - std::string result; - - if (fp == nullptr || std::feof(fp)) { - return 0; - } - - for (int ch; (ch = std::fgetc(fp)) != EOF && ch != '\n'; ) { - result += (char)ch; - } - - if (std::ferror(fp)) { - duk::raise(ctx, SystemError()); - } - - duk::push(ctx, clearCr(result)); - - return 1; -} - -/* - * Method: File.remove() - * -------------------------------------------------------- - * - * Synonym of File.remove(path) but with the path from the file. - * - * Throws: - * - Any exception on error. - */ -duk::Ret methodRemove(duk::ContextPtr ctx) -{ - if (::remove(duk::self<duk::Pointer<File>>(ctx)->path().c_str()) < 0) { - duk::raise(ctx, SystemError()); - } - - return 0; -} - -/* - * Method: File.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: - * - Any exception on error. - */ -duk::Ret methodSeek(duk::ContextPtr ctx) -{ - auto type = duk::require<int>(ctx, 0); - auto amount = duk::require<int>(ctx, 1); - auto fp = duk::self<duk::Pointer<File>>(ctx)->handle(); - - if (fp != nullptr && std::fseek(fp, amount, type) != 0) { - duk::raise(ctx, SystemError()); - } - - return 0; -} - -#if defined(HAVE_STAT) - -/* - * Method: File.stat() [optional] - * -------------------------------------------------------- - * - * Synonym of File.stat(path) but with the path from the file. - * - * Returns: - * The stat information. - * Throws: - * - Any exception on error. - */ -duk::Ret methodStat(duk::ContextPtr ctx) -{ - struct stat st; - auto file = duk::self<duk::Pointer<File>>(ctx); - - if (file->handle() == nullptr && ::stat(file->path().c_str(), &st) < 0) { - duk::raise(ctx, SystemError()); - } else { - duk::push(ctx, st); - } - - return 1; -} - -#endif // !HAVE_STAT - -/* - * Method: File.tell() - * -------------------------------------------------------- - * - * Get the actual position in the file. - * - * Returns: - * The position. - * Throws: - * - Any exception on error. - */ -duk::Ret methodTell(duk::ContextPtr ctx) -{ - auto fp = duk::self<duk::Pointer<File>>(ctx)->handle(); - long pos; - - if (fp == nullptr) { - return 0; - } - - if ((pos = std::ftell(fp)) == -1L) { - duk::raise(ctx, SystemError()); - } else { - duk::push(ctx, (int)pos); - } - - return 1; -} - -/* - * Method: File.write(data) - * -------------------------------------------------------- - * - * Write some characters to the file. - * - * Arguments: - * - data, the character to write. - * Returns: - * The number of bytes written. - * Throws: - * - Any exception on error. - */ -duk::Ret methodWrite(duk::ContextPtr ctx) -{ - std::FILE *fp = duk::self<duk::Pointer<File>>(ctx)->handle(); - std::string data = duk::require<std::string>(ctx, 0); - - if (fp == nullptr) { - return 0; - } - - std::size_t nwritten = std::fwrite(data.c_str(), 1, data.length(), fp); - - if (std::ferror(fp)) { - duk::raise(ctx, SystemError()); - } - - duk::push(ctx, (int)nwritten); - - return 1; -} - -const duk::FunctionMap methods{ - { "basename", { methodBasename, 0 } }, - { "close", { methodClose, 0 } }, - { "dirname", { methodDirname, 0 } }, - { "lines", { methodLines, 0 } }, - { "read", { methodRead, 1 } }, - { "readline", { methodReadline, 0 } }, - { "remove", { methodRemove, 0 } }, - { "seek", { methodSeek, 2 } }, -#if defined(HAVE_STAT) - { "stat", { methodStat, 0 } }, -#endif - { "tell", { methodTell, 0 } }, - { "write", { methodWrite, 1 } }, -}; - -/* - * File "static" functions - * ------------------------------------------------------------------ - */ - -/* - * 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: - * - Any exception on error. - */ -duk::Ret constructor(duk::ContextPtr ctx) -{ - if (!duk_is_constructor_call(ctx)) { - return 0; - } - - std::string path = duk::require<std::string>(ctx, 0); - std::string mode = duk::require<std::string>(ctx, 1); - - try { - duk::construct(ctx, duk::Pointer<File>{new File(path, mode)}); - } catch (const std::exception &) { - duk::raise(ctx, SystemError()); - } - - return 0; -} - -/* - * Function: Irccd.File.basename(path) - * -------------------------------------------------------- - * - * Return the file basename as specified in `basename(3)` C function. - * - * Arguments: - * - path, the path to the file. - * Returns: - * The base name. - */ -duk::Ret functionBasename(duk::ContextPtr ctx) -{ - duk::push(ctx, fs::baseName(duk::require<std::string>(ctx, 0))); - - return 1; -} - -/* - * Function: Irccd.File.dirname(path) - * -------------------------------------------------------- - * - * Return the file directory name as specified in `dirname(3)` C function. - * - * Arguments: - * - path, the path to the file. - * Returns: - * The directory name. - */ -duk::Ret functionDirname(duk::ContextPtr ctx) -{ - duk::push(ctx, fs::dirName( duk::require<std::string>(ctx, 0))); - - return 1; -} - -/* - * Function: Irccd.File.exists(path) - * -------------------------------------------------------- - * - * Check if the file exists. - * - * Arguments: - * - path, the path to the file. - * Returns: - * True if exists. - * Throws: - * - Any exception if we don't have access. - */ -duk::Ret functionExists(duk::ContextPtr ctx) -{ - duk::push(ctx, fs::exists(duk::require<std::string>(ctx, 0))); - - return 1; -} - -/* - * function Irccd.File.remove(path) - * -------------------------------------------------------- - * - * Remove the file at the specified path. - * - * Arguments: - * - path, the path to the file. - * Throws: - * - Any exception on error. - */ -duk::Ret functionRemove(duk::ContextPtr ctx) -{ - if (::remove(duk::require<std::string>(ctx, 0).c_str()) < 0) { - duk::raise(ctx, SystemError()); - } - - return 0; -} - -#if defined(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: - * - Any exception on error. - */ -duk::Ret functionStat(duk::ContextPtr ctx) -{ - struct stat st; - - if (::stat(duk::require<std::string>(ctx, 0).c_str(), &st) < 0) { - duk::raise(ctx, SystemError()); - } - - duk::push(ctx, st); - - return 1; -} - -#endif // !HAVE_STAT - -const duk::FunctionMap functions{ - { "basename", { functionBasename, 1 } }, - { "dirname", { functionDirname, 1 } }, - { "exists", { functionExists, 1 } }, - { "remove", { functionRemove, 1 } }, -#if defined(HAVE_STAT) - { "stat", { functionStat, 1 } }, -#endif -}; - -const duk::Map<int> constants{ - { "SeekCur", SEEK_CUR }, - { "SeekEnd", SEEK_END }, - { "SeekSet", SEEK_SET }, -}; - -} // !namespace - -void loadJsFile(duk::ContextPtr ctx) -{ - duk::StackAssert sa(ctx); - - duk::getGlobal<void>(ctx, "Irccd"); - duk::push(ctx, duk::Function{constructor, 2}); - duk::push(ctx, constants); - duk::push(ctx, functions); - duk::push(ctx, duk::Object{}); - duk::push(ctx, methods); - duk::putProperty(ctx, -2, "prototype"); - duk::putProperty(ctx, -2, "File"); - duk::pop(ctx); -} - -} // !irccd
--- a/lib/irccd/js-file.hpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,191 +0,0 @@ -/* - * js-file.hpp -- Irccd.File API - * - * Copyright (c) 2013-2016 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_HPP -#define IRCCD_JS_FILE_HPP - -/** - * \file js-file.hpp - * \brief Irccd.File JavaScript API. - */ - -#include <cassert> -#include <cerrno> -#include <cstdio> -#include <cstring> -#include <functional> -#include <stdexcept> -#include <string> - -#include "js.hpp" - -namespace irccd { - -/** - * \class File - * \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 m_path; - std::FILE *m_stream; - std::function<void (std::FILE *)> m_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) - : m_path(std::move(path)) - , m_destructor([] (std::FILE *fp) { std::fclose(fp); }) - { - if ((m_stream = std::fopen(m_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 - : m_stream(fp) - , m_destructor(std::move(destructor)) - { - assert(m_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 &path() const noexcept - { - return m_path; - } - - /** - * Get the handle. - * - * \return the handle or nullptr if the stream was closed - */ - inline std::FILE *handle() noexcept - { - return m_stream; - } - - /** - * Force close, can be safely called multiple times. - */ - inline void close() noexcept - { - if (m_stream) { - m_destructor(m_stream); - m_stream = nullptr; - } - } -}; - -namespace duk { - -/** - * \brief JavaScript binding for File. - */ -template <> -class TypeTraits<File> { -public: - /** - * Push the File prototype. - * - * \param ctx the context - */ - static inline void prototype(ContextPtr ctx) - { - getGlobal<void>(ctx, "Irccd"); - getGlobal<void>(ctx, "File"); - getProperty<void>(ctx, -1, "prototype"); - remove(ctx, -2); - remove(ctx, -2); - } - - /** - * Get the File signature. - * - * \return File - */ - static inline std::string name() - { - return "\xff""\xff""File"; - } - - /** - * Get the inheritance list. - * - * \return empty - */ - static inline std::vector<std::string> inherits() - { - return {}; - } -}; - -} // !duk - -/** - * Load the module. - * - * \param ctx the context. - */ -void loadJsFile(duk::ContextPtr ctx); - -} // !irccd - -#endif // !IRCCD_JS_FILE_HPP -
--- a/lib/irccd/js-irccd.cpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,88 +0,0 @@ -/* - * js-irccd.cpp -- Irccd API - * - * Copyright (c) 2013-2016 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-irccd.hpp" -#include "sysconfig.hpp" - -namespace irccd { - -SystemError::SystemError() - : m_errno(errno) - , m_message(std::strerror(m_errno)) -{ -} - -SystemError::SystemError(int e, std::string message) - : m_errno(e) - , m_message(std::move(message)) -{ -} - -void SystemError::raise(duk::ContextPtr ctx) const -{ - duk::StackAssert sa(ctx, 1); - - duk::getGlobal<void>(ctx, "Irccd"); - duk::getProperty<void>(ctx, -1, "SystemError"); - duk::remove(ctx, -2); - duk::push(ctx, m_errno); - duk::push(ctx, m_message); - duk::create(ctx, 2); - duk::raise(ctx); -} - -duk::Ret constructor(duk::ContextPtr ctx) -{ - duk::push(ctx, duk::This{}); - duk::putProperty(ctx, -1, "errno", duk::require<int>(ctx, 0)); - duk::putProperty(ctx, -1, "message", duk::require<std::string>(ctx, 1)); - duk::putProperty(ctx, -1, "name", "SystemError"); - duk::pop(ctx); - - return 0; -} - -void loadJsIrccd(duk::Context &ctx) -{ - duk::StackAssert sa(ctx); - - /* Irccd */ - duk::push(ctx, duk::Object{}); - - /* Version */ - duk::push(ctx, duk::Object{}); - duk::putProperty(ctx, -1, "major", IRCCD_VERSION_MAJOR); - duk::putProperty(ctx, -1, "minor", IRCCD_VERSION_MINOR); - duk::putProperty(ctx, -1, "patch", IRCCD_VERSION_PATCH); - duk::putProperty(ctx, -2, "version"); - - /* Create the SystemError that inherits from Error */ - duk::push(ctx, duk::Function{constructor, 2}); - duk::push(ctx, duk::Object{}); - duk::getGlobal<void>(ctx, "Error"); - duk::getProperty<void>(ctx, -1, "prototype"); - duk::remove(ctx, -2); - duk::setPrototype(ctx, -2); - duk::putProperty(ctx, -2, "prototype"); - duk::putProperty(ctx, -2, "SystemError"); - - /* Set Irccd as global */ - duk::putGlobal(ctx, "Irccd"); -} - -} // !irccd
--- a/lib/irccd/js-irccd.hpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -/* - * js-irccd.hpp -- Irccd API - * - * Copyright (c) 2013-2016 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_HPP -#define IRCCD_JS_IRCCD_HPP - -/** - * \file js-irccd.hpp - * \brief Irccd.Irccd JavaScript API. - */ - -#include <cerrno> -#include <cstring> - -#include "js.hpp" - -namespace irccd { - -/** - * \brief Custom JavaScript exception for system error. - */ -class SystemError { -private: - int m_errno; - std::string m_message; - -public: - /** - * Create a system error from the current errno value. - */ - SystemError(); - - /** - * Create a system error with the given errno and message. - * - * \param e the errno number - * \param message the message - */ - SystemError(int e, std::string message); - - /** - * Raise the SystemError. - * - * \param ctx the context - */ - void raise(duk::ContextPtr ctx) const; -}; - -/** - * Load the module. - * - * \param ctx the context. - */ -void loadJsIrccd(duk::Context &ctx); - -} // !irccd - -#endif // !IRCCD_JS_IRCCD_HPP
--- a/lib/irccd/js-logger.cpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,99 +0,0 @@ -/* - * js-logger.cpp -- Irccd.Logger API - * - * Copyright (c) 2013-2016 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-logger.hpp" -#include "logger.hpp" - -namespace irccd { - -namespace { - -duk::Ret print(duk::ContextPtr ctx, std::ostream &out) -{ - /* - * Get the message before we start printing stuff to avoid - * empty lines. - */ - out << "plugin " << duk::getGlobal<std::string>(ctx, "\xff""\xff""name"); - out << ": " << duk::require<std::string>(ctx, 0) << std::endl; - - return 0; -} - -/* - * Function: Irccd.Logger.info(message) - * -------------------------------------------------------- - * - * Write a verbose message. - * - * Arguments: - * - message, the message. - */ -duk::Ret info(duk::ContextPtr ctx) -{ - return print(ctx, log::info()); -} - -/* - * Function: Irccd.Logger.warning(message) - * -------------------------------------------------------- - * - * Write a warning message. - * - * Arguments: - * - message, the warning. - */ -duk::Ret warning(duk::ContextPtr ctx) -{ - return print(ctx, log::warning()); -} - -/* - * Function: Logger.debug(message) - * -------------------------------------------------------- - * - * Write a debug message, only shown if irccd is compiled in debug. - * - * Arguments: - * - message, the message. - */ -duk::Ret debug(duk::ContextPtr ctx) -{ - return print(ctx, log::debug()); -} - -const duk::FunctionMap functions{ - { "info", { info, 1 } }, - { "warning", { warning, 1 } }, - { "debug", { debug, 1 } } -}; - -} // !namespace - -void loadJsLogger(duk::ContextPtr ctx) -{ - duk::StackAssert sa(ctx); - - duk::getGlobal<void>(ctx, "Irccd"); - duk::push(ctx, duk::Object{}); - duk::push(ctx, functions); - duk::putProperty(ctx, -2, "Logger"); - duk::pop(ctx); -} - -} // !irccd
--- a/lib/irccd/js-logger.hpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -/* - * js-logger.hpp -- Irccd.Logger API - * - * Copyright (c) 2013-2016 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_HPP -#define IRCCD_JS_LOGGER_HPP - -/** - * \file js-logger.hpp - * \brief Irccd.Logger JavaScript API. - */ - -#include "js.hpp" - -namespace irccd { - -/** - * Load the module. - * - * \param ctx the context. - */ -void loadJsLogger(duk::ContextPtr ctx); - -} // !irccd - -#endif // !IRCCD_JS_LOGGER_HPP
--- a/lib/irccd/js-plugin.cpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,195 +0,0 @@ -/* - * js-plugin.cpp -- Irccd.Plugin API - * - * Copyright (c) 2013-2016 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.hpp" -#include "plugin.hpp" -#include "service-plugin.hpp" -#include "js-plugin.hpp" - -namespace irccd { - -namespace { - -/* - * Wrap function for these functions because they all takes the same arguments. - * - * - load, - * - reload, - * - unload. - */ -template <typename Func> -duk::Ret wrap(duk::ContextPtr ctx, int nret, Func &&func) -{ - std::string name = duk::require<std::string>(ctx, 0); - - try { - func(*duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd"), name); - } catch (const std::out_of_range &ex) { - duk::raise(ctx, duk::ReferenceError(ex.what())); - } catch (const std::exception &ex) { - duk::raise(ctx, duk::Error(ex.what())); - } - - return nret; -} - -/* - * 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. - */ -duk::Ret info(duk::ContextPtr ctx) -{ - Plugin *plugin = nullptr; - - if (duk::top(ctx) >= 1) { - plugin = duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->pluginService().get(duk::require<std::string>(ctx, 0)).get(); - } else { - plugin = duk::getGlobal<duk::RawPointer<Plugin>>(ctx, "\xff""\xff""plugin"); - } - - if (!plugin) { - return 0; - } - - duk::push(ctx, duk::Object{}); - duk::putProperty(ctx, -1, "name", plugin->name()); - duk::putProperty(ctx, -1, "author", plugin->author()); - duk::putProperty(ctx, -1, "license", plugin->license()); - duk::putProperty(ctx, -1, "summary", plugin->summary()); - duk::putProperty(ctx, -1, "version", plugin->version()); - - return 1; -} - -/* - * Function: Irccd.Plugin.list() - * ------------------------------------------------------------------ - * - * Get the list of plugins, the array returned contains all plugin names. - * - * Returns: - * The list of all plugin names. - */ -duk::Ret list(duk::ContextPtr ctx) -{ - duk::push(ctx, duk::Array{}); - - int i = 0; - for (const auto &plugin : duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->pluginService().plugins()) { - duk::putProperty(ctx, -1, i++, plugin->name()); - } - - return 1; -} - -/* - * Function: Irccd.Plugin.load(name) - * ------------------------------------------------------------------ - * - * Load a plugin by name. This function will search through the standard directories. - * - * Arguments: - * - name, the plugin identifier. - * Throws: - * - Error on errors, - * - ReferenceError if the plugin was not found. - */ -duk::Ret load(duk::ContextPtr ctx) -{ - return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) { - irccd.pluginService().load(name); - }); -} - -/* - * Function: Irccd.Plugin.reload(name) - * ------------------------------------------------------------------ - * - * Reload a plugin by name. - * - * Arguments: - * - name, the plugin identifier. - * Throws: - * - Error on errors, - * - ReferenceError if the plugin was not found. - */ -duk::Ret reload(duk::ContextPtr ctx) -{ - return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) { - irccd.pluginService().reload(name); - }); -} - -/* - * Function: Irccd.Plugin.unload(name) - * ------------------------------------------------------------------ - * - * Unload a plugin by name. - * - * Arguments: - * - name, the plugin identifier. - * Throws: - * - Error on errors, - * - ReferenceError if the plugin was not found. - */ -duk::Ret unload(duk::ContextPtr ctx) -{ - return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) { - irccd.pluginService().unload(name); - }); -} - -const duk::FunctionMap functions{ - { "info", { info, DUK_VARARGS } }, - { "list", { list, 0 } }, - { "load", { load, 1 } }, - { "reload", { reload, 1 } }, - { "unload", { unload, 1 } } -}; - -} // !namespace - -void loadJsPlugin(duk::Context &ctx) noexcept -{ - duk::StackAssert sa(ctx); - - duk::getGlobal<void>(ctx, "Irccd"); - duk::push(ctx, duk::Object{}); - duk::push(ctx, functions); - duk::push(ctx, duk::Object{}); - duk::putProperty(ctx, -2, "config"); - duk::putProperty(ctx, -2, "Plugin"); - duk::pop(ctx); -} - -} // !irccd
--- a/lib/irccd/js-plugin.hpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -/* - * js-plugin.hpp -- Irccd.Plugin API - * - * Copyright (c) 2013-2016 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 Irccd.Plugin JavaScript API. - */ - -#include "js.hpp" - -namespace irccd { - -/** - * Load the module. - * - * \param ctx the context. - */ -void loadJsPlugin(duk::Context &ctx) noexcept; - -} // !irccd - -#endif // !IRCCD_JS_PLUGIN_HPP -
--- a/lib/irccd/js-server.cpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,540 +0,0 @@ -/* - * js-server.cpp -- Irccd.Server API - * - * Copyright (c) 2013-2016 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 <sstream> -#include <unordered_map> - -#include "irccd.hpp" -#include "js-server.hpp" -#include "server.hpp" -#include "service-server.hpp" - -namespace irccd { - -namespace { - -/* - * Method: Server.cmode(channel, mode) - * ------------------------------------------------------------------ - * - * Change a channel mode. - * - * Arguments: - * - channel, the channel, - * - mode, the mode. - */ -duk::Ret cmode(duk::ContextPtr ctx) -{ - duk::self<duk::Shared<Server>>(ctx)->cmode(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); - - return 0; -} - -/* - * Method: Server.cnotice(channel, message) - * ------------------------------------------------------------------ - * - * Send a channel notice. - * - * Arguments: - * - channel, the channel, - * - message, the message. - */ -duk::Ret cnotice(duk::ContextPtr ctx) -{ - duk::self<duk::Shared<Server>>(ctx)->cnotice(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); - - return 0; -} - -/* - * Method: Server.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 - */ -duk::Ret info(duk::ContextPtr ctx) -{ - auto server = duk::self<duk::Shared<Server>>(ctx); - - duk::push(ctx, duk::Object{}); - duk::putProperty(ctx, -1, "name", server->name()); - duk::putProperty(ctx, -1, "host", server->info().host); - duk::putProperty<int>(ctx, -1, "port", server->info().port); - duk::putProperty<bool>(ctx, -1, "ssl", server->info().flags & ServerInfo::Ssl); - duk::putProperty<bool>(ctx, -1, "sslVerify", server->info().flags & ServerInfo::SslVerify); - duk::putProperty(ctx, -1, "commandChar", server->settings().command); - duk::putProperty(ctx, -1, "realname", server->identity().realname); - duk::putProperty(ctx, -1, "nickname", server->identity().nickname); - duk::putProperty(ctx, -1, "username", server->identity().username); - - /* Channels */ - duk::push(ctx, duk::Array{}); - - int i = 0; - for (const auto &channel : server->settings().channels) { - duk::putProperty(ctx, -1, i++, channel.name); - } - - duk::putProperty(ctx, -2, "channels"); - - return 1; -} - -/* - * Method: Server.invite(target, channel) - * ------------------------------------------------------------------ - * - * Invite someone to a channel. - * - * Arguments: - * - target, the target to invite, - * - channel, the channel. - */ -duk::Ret invite(duk::ContextPtr ctx) -{ - duk::self<duk::Shared<Server>>(ctx)->invite(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); - - return 0; -} - -/* - * Method: Server.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. - */ -duk::Ret join(duk::ContextPtr ctx) -{ - duk::self<duk::Shared<Server>>(ctx)->join(duk::require<std::string>(ctx, 0), duk::optional<std::string>(ctx, 1, "")); - - return 0; -} - -/* - * Method: Server.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. - */ -duk::Ret kick(duk::ContextPtr ctx) -{ - duk::self<duk::Shared<Server>>(ctx)->kick( - duk::require<std::string>(ctx, 0), - duk::require<std::string>(ctx, 1), - duk::optional<std::string>(ctx, 2, "") - ); - - return 0; -} - -/* - * Method: Server.me(target, message) - * ------------------------------------------------------------------ - * - * Send a CTCP Action. - * - * Arguments: - * - target, the target or a channel, - * - message, the message. - */ -duk::Ret me(duk::ContextPtr ctx) -{ - duk::self<duk::Shared<Server>>(ctx)->me(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); - - return 0; -} - -/* - * Method: Server.message(target, message) - * ------------------------------------------------------------------ - * - * Send a message. - * - * Arguments: - * - target, the target or a channel, - * - message, the message. - */ -duk::Ret message(duk::ContextPtr ctx) -{ - duk::self<duk::Shared<Server>>(ctx)->message(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); - - return 0; -} - -/* - * Method: Server.mode(mode) - * ------------------------------------------------------------------ - * - * Change your mode. - * - * Arguments: - * - mode, the new mode. - */ -duk::Ret mode(duk::ContextPtr ctx) -{ - duk::self<duk::Shared<Server>>(ctx)->mode(duk::require<std::string>(ctx, 0)); - - return 0; -} - -/* - * Method: Server.names(channel) - * ------------------------------------------------------------------ - * - * Get the list of names from a channel. - * - * Arguments: - * - channel, the channel. - */ -duk::Ret names(duk::ContextPtr ctx) -{ - duk::self<duk::Shared<Server>>(ctx)->names(duk::require<std::string>(ctx, 0)); - - return 0; -} - -/* - * Method: Server.nick(nickname) - * ------------------------------------------------------------------ - * - * Change the nickname. - * - * Arguments: - * - nickname, the nickname. - */ -duk::Ret nick(duk::ContextPtr ctx) -{ - duk::self<duk::Shared<Server>>(ctx)->nick(duk::require<std::string>(ctx, 0)); - - return 0; -} - -/* - * Method: Server.notice(target, message) - * ------------------------------------------------------------------ - * - * Send a private notice. - * - * Arguments: - * - target, the target, - * - message, the notice message. - */ -duk::Ret notice(duk::ContextPtr ctx) -{ - duk::self<duk::Shared<Server>>(ctx)->notice(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); - - return 0; -} - -/* - * Method: Server.part(channel, reason = undefined) - * ------------------------------------------------------------------ - * - * Leave a channel. - * - * Arguments: - * - channel, the channel to leave, - * - reason, the optional reason, keep undefined for portability. - */ -duk::Ret part(duk::ContextPtr ctx) -{ - duk::self<duk::Shared<Server>>(ctx)->part(duk::require<std::string>(ctx, 0), duk::optional<std::string>(ctx, 1, "")); - - return 0; -} - -/* - * Method: Server.send(raw) - * ------------------------------------------------------------------ - * - * Send a raw message to the IRC server. - * - * Arguments: - * - raw, the raw message (without terminators). - */ -duk::Ret send(duk::ContextPtr ctx) -{ - duk::self<duk::Shared<Server>>(ctx)->send(duk::require<std::string>(ctx, 0)); - - return 0; -} - -/* - * Method: Server.topic(channel, topic) - * ------------------------------------------------------------------ - * - * Change a channel topic. - * - * Arguments: - * - channel, the channel, - * - topic, the new topic. - */ -duk::Ret topic(duk::ContextPtr ctx) -{ - duk::self<duk::Shared<Server>>(ctx)->topic(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); - - return 0; -} - -/* - * Method: Server.whois(target) - * ------------------------------------------------------------------ - * - * Get whois information. - * - * Arguments: - * - target, the target. - */ -duk::Ret whois(duk::ContextPtr ctx) -{ - duk::self<duk::Shared<Server>>(ctx)->whois(duk::require<std::string>(ctx, 0)); - - return 0; -} - -/* - * Method: Server.toString() - * ------------------------------------------------------------------ - * - * Convert the object to std::string, convenience for adding the object - * as property key. - * - * Returns: - * The server name (unique). - */ -duk::Ret toString(duk::ContextPtr ctx) -{ - duk::push(ctx, duk::self<duk::Shared<Server>>(ctx)->name()); - - return 1; -} - -/* - * Function: Irccd.Server(params) [constructor] - * ------------------------------------------------------------------ - * - * Construct a new server. - * - * Params must be filled with the following properties: - * - * name: the name, - * host: the host, - * 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) - * sslVerify: true to verify (Optional: default true) - * 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: "!") - */ -duk::Ret constructor(duk::ContextPtr ctx) -{ - if (!duk_is_constructor_call(ctx)) - return 0; - - std::string name; - ServerInfo info; - ServerIdentity identity; - ServerSettings settings; - - // Information part. - name = duk::getProperty<std::string>(ctx, 0, "name"); - info.host = duk::getProperty<std::string>(ctx, 0, "host"); - info.port = duk::optionalProperty<int>(ctx, 0, "port", (int)info.port); - info.password = duk::optionalProperty<std::string>(ctx, 0, "password", ""); - - if (duk::optionalProperty<bool>(ctx, 0, "ipv6", false)) { - info.flags |= ServerInfo::Ipv6; - } - - // Identity part. - identity.nickname = duk::optionalProperty<std::string>(ctx, 0, "nickname", identity.nickname); - identity.username = duk::optionalProperty<std::string>(ctx, 0, "username", identity.username); - identity.realname = duk::optionalProperty<std::string>(ctx, 0, "realname", identity.realname); - identity.ctcpversion = duk::optionalProperty<std::string>(ctx, 0, "version", identity.ctcpversion); - - // Settings part. - for (const auto &chan: duk::getProperty<std::vector<std::string>>(ctx, 0, "channels")) { - settings.channels.push_back(Server::splitChannel(chan)); - } - - settings.reconnectTries = duk::optionalProperty<int>(ctx, 0, "recoTries", (int)settings.reconnectTries); - settings.reconnectDelay = duk::optionalProperty<int>(ctx, 0, "recoTimeout", (int)settings.reconnectDelay); - - if (duk::optionalProperty<bool>(ctx, 0, "joinInvite", false)) { - settings.flags |= ServerSettings::JoinInvite; - } - if (duk::optionalProperty<bool>(ctx, 0, "autoRejoin", false)) { - settings.flags |= ServerSettings::AutoRejoin; - } - - try { - duk::construct(ctx, duk::Shared<Server>{std::make_shared<Server>(std::move(name), std::move(info), - std::move(identity), std::move(settings))}); - } catch (const std::exception &ex) { - duk::raise(ctx, duk::Error(ex.what())); - } - - return 0; -} - -/* - * Function: Irccd.Server.add(s) - * ------------------------------------------------------------------ - * - * Register a new server to the irccd instance. - * - * Arguments: - * - s, the server to add. - */ -duk::Ret add(duk::ContextPtr ctx) -{ - auto server = duk::get<duk::Shared<Server>>(ctx, 0); - - if (server) { - duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->serverService().add(server); - } - - return 0; -} - -/* - * Function: Irccd.Server.find(name) - * ------------------------------------------------------------------ - * - * Find a server by name. - * - * Arguments: - * - name, the server name - * Returns: - * The server object or undefined if not found. - */ -duk::Ret find(duk::ContextPtr ctx) -{ - const auto name = duk::require<std::string>(ctx, 0); - const auto irccd = duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd"); - - try { - duk::push(ctx, duk::Shared<Server>{irccd->serverService().require(name)}); - } catch (...) { - return 0; - } - - return 1; -} - -/* - * Function: Irccd.Server.list() - * ------------------------------------------------------------------ - * - * Get the map of all loaded servers. - * - * Returns: - * An object with string-to-servers pairs. - */ -duk::Ret list(duk::ContextPtr ctx) -{ - duk::push(ctx, duk::Object{}); - - for (const auto &server : duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->serverService().servers()) { - duk::putProperty(ctx, -1, server->name(), duk::Shared<Server>{server}); - } - - return 1; -} - -/* - * 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. - */ -duk::Ret remove(duk::ContextPtr ctx) -{ - duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->serverService().remove(duk::require<std::string>(ctx, 0)); - - return 0; -} - -const duk::FunctionMap methods{ - { "cmode", { cmode, 2 } }, - { "cnotice", { cnotice, 2 } }, - { "info", { info, 0 } }, - { "invite", { invite, 2 } }, - { "join", { join, DUK_VARARGS } }, - { "kick", { kick, DUK_VARARGS } }, - { "me", { me, 2 } }, - { "message", { message, 2 } }, - { "mode", { mode, 1 } }, - { "names", { names, 1 } }, - { "nick", { nick, 1 } }, - { "notice", { notice, 2 } }, - { "part", { part, DUK_VARARGS } }, - { "send", { send, 1 } }, - { "topic", { topic, 2 } }, - { "whois", { whois, 1 } }, - { "toString", { toString, 0 } } -}; - -const duk::FunctionMap functions{ - { "add", { add, 1 } }, - { "find", { find, 1 } }, - { "list", { list, 0 } }, - { "remove", { remove, 1 } } -}; - -} // !namespace - -void loadJsServer(duk::ContextPtr ctx) -{ - duk::StackAssert sa(ctx); - - duk::getGlobal<void>(ctx, "Irccd"); - duk::push(ctx, duk::Function{constructor, 1}); - duk::push(ctx, functions); - duk::push(ctx, duk::Object()); - duk::push(ctx, methods); - duk::putProperty(ctx, -2, "prototype"); - duk::putProperty(ctx, -2, "Server"); - duk::pop(ctx); -} - -} // !irccd
--- a/lib/irccd/js-server.hpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,86 +0,0 @@ -/* - * js-server.hpp -- Irccd.Server API - * - * Copyright (c) 2013-2016 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_HPP -#define IRCCD_JS_SERVER_HPP - -/** - * \file js-server.hpp - * \brief Irccd.Server JavaScript API. - */ - -#include "js.hpp" -#include "server.hpp" - -namespace irccd { - -namespace duk { - -/** - * \brief JavaScript binding for Server. - */ -template <> -class TypeTraits<irccd::Server> { -public: - /** - * Push the Server prototype. - * - * \param ctx the context - */ - static inline void prototype(ContextPtr ctx) - { - getGlobal<void>(ctx, "Irccd"); - getProperty<void>(ctx, -1, "Server"); - getProperty<void>(ctx, -1, "prototype"); - remove(ctx, -2); - remove(ctx, -2); - } - - /** - * Get the Server signature. - * - * \return Server - */ - static inline std::string name() - { - return "\xff""\xff""Server"; - } - - /** - * Get the inheritance list. - * - * \return empty - */ - static inline std::vector<std::string> inherits() - { - return {}; - } -}; - -} // !duk - -/** - * Load the module. - * - * \param ctx the context. - */ -void loadJsServer(duk::ContextPtr ctx); - -} // !irccd - -#endif // !IRCCD_JS_SERVER_HPP
--- a/lib/irccd/js-system.cpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,237 +0,0 @@ -/* - * js-system.cpp -- Irccd.System API - * - * Copyright (c) 2013-2016 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 <chrono> -#include <cstdlib> -#include <thread> - -#include "sysconfig.hpp" - -#if defined(HAVE_POPEN) -# include <cstdio> -#endif - -#include "js-file.hpp" -#include "js-irccd.hpp" -#include "js-system.hpp" -#include "system.hpp" - -namespace irccd { - -namespace { - -/* - * Function: Irccd.System.env(key) - * ------------------------------------------------------------------ - * - * Get an environment system variable. - * - * Arguments: - * - key, the environment variable. - * Returns: - * The value. - */ -int env(duk::ContextPtr ctx) -{ - duk::push(ctx, sys::env(duk::get<std::string>(ctx, 0))); - - return 1; -} - -/* - * Function: Irccd.System.exec(cmd) - * ------------------------------------------------------------------ - * - * Execute a system command. - * - * Arguments: - * - cmd, the command to execute. - */ -int exec(duk::ContextPtr ctx) -{ - std::system(duk::get<const char *>(ctx, 0)); - - return 0; -} - -/* - * Function: Irccd.System.home() - * ------------------------------------------------------------------ - * - * Get the operating system user's home. - * - * Returns: - * The user home directory. - */ -int home(duk::ContextPtr ctx) -{ - duk::push(ctx, sys::home()); - - return 1; -} - -/* - * Function: Irccd.System.name() - * ------------------------------------------------------------------ - * - * Get the operating system name. - * - * Returns: - * The system name. - */ -int name(duk::ContextPtr ctx) -{ - duk::push(ctx, sys::name()); - - return 1; -} - -#if defined(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 failures. - */ -int popen(duk::ContextPtr ctx) -{ - auto fp = ::popen(duk::require<const char *>(ctx, 0), duk::require<const char *>(ctx, 1)); - - if (fp == nullptr) { - duk::raise(ctx, SystemError{}); - } - - duk::push(ctx, duk::Pointer<File>{new File(fp, [] (std::FILE *fp) { ::pclose(fp); })}); - - return 1; -} - -#endif // !HAVE_POPEN - -/* - * Function: Irccd.System.sleep(delay) - * ------------------------------------------------------------------ - * - * Sleep the main loop for the specific delay in seconds. - */ -int sleep(duk::ContextPtr ctx) -{ - std::this_thread::sleep_for(std::chrono::seconds(duk::get<int>(ctx, 0))); - - return 0; -} - -/* - * Function: Irccd.System.ticks() - * ------------------------------------------------------------------ - * - * Get the number of milliseconds since irccd was started. - * - * Returns: - * The number of milliseconds. - */ -int ticks(duk::ContextPtr ctx) -{ - duk::push(ctx, static_cast<int>(sys::ticks())); - - return 1; -} - -/* - * Function: Irccd.System.usleep(delay) - * ------------------------------------------------------------------ - * - * Sleep the main loop for the specific delay in microseconds. - */ -int usleep(duk::ContextPtr ctx) -{ - std::this_thread::sleep_for(std::chrono::microseconds(duk::get<int>(ctx, 0))); - - return 0; -} - -/* - * Function: Irccd.System.uptime() - * ------------------------------------------------------------------ - * - * Get the system uptime. - * - * Returns: - * The system uptime. - */ -int uptime(duk::ContextPtr ctx) -{ - duk::push<int>(ctx, sys::uptime()); - - return 0; -} - -/* - * Function: Irccd.System.version() - * ------------------------------------------------------------------ - * - * Get the operating system version. - * - * Returns: - * The system version. - */ -int version(duk::ContextPtr ctx) -{ - duk::push(ctx, sys::version()); - - return 1; -} - -const duk::FunctionMap functions{ - { "env", { env, 1 } }, - { "exec", { exec, 1 } }, - { "home", { home, 0 } }, - { "name", { name, 0 } }, -#if defined(HAVE_POPEN) - { "popen", { popen, 2 } }, -#endif - { "sleep", { sleep, 1 } }, - { "ticks", { ticks, 0 } }, - { "uptime", { uptime, 0 } }, - { "usleep", { usleep, 1 } }, - { "version", { version, 0 } } -}; - -} // !namespace - -void loadJsSystem(duk::ContextPtr ctx) -{ - duk::StackAssert sa(ctx); - - duk::getGlobal<void>(ctx, "Irccd"); - duk::push(ctx, duk::Object{}); - duk::push(ctx, functions); - duk::putProperty(ctx, -2, "System"); - duk::pop(ctx); -} - -} // !irccd
--- a/lib/irccd/js-system.hpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -/* - * js-system.hpp -- Irccd.System API - * - * Copyright (c) 2013-2016 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_HPP -#define IRCCD_JS_SYSTEM_HPP - -/** - * \file js-system.hpp - * \brief Irccd.System JavaScript API. - */ - -#include "js.hpp" - -namespace irccd { - -/** - * Load the module. - * - * \param ctx the context. - */ -void loadJsSystem(duk::ContextPtr ctx); - -} // !irccd - -#endif // !IRCCD_JS_SYSTEM_HPP
--- a/lib/irccd/js-timer.cpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,144 +0,0 @@ -/* - * js-timer.cpp -- Irccd.Timer API - * - * Copyright (c) 2013-2016 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 <cstdint> - -#include "js.hpp" -#include "plugin-js.hpp" -#include "timer.hpp" - -namespace irccd { - -namespace duk { - -template <> -class TypeTraits<Timer> { -public: - static std::string name() - { - return "\xff""\xff""Timer"; - } - - static std::vector<std::string> inherits() - { - return {}; - } -}; - -} // !duk - -namespace { - -/* - * Method: Timer.start() - * -------------------------------------------------------- - * - * Start the timer. If the timer is already started the method - * is a no-op. - */ -duk::Ret start(duk::ContextPtr ctx) -{ - auto timer = duk::self<duk::Shared<Timer>>(ctx); - - if (!timer->isRunning()) { - timer->start(); - } - - return 0; -} - -/* - * Method: Timer.stop() - * -------------------------------------------------------- - * - * Stop the timer. - */ -duk::Ret stop(duk::ContextPtr ctx) -{ - auto timer = duk::self<duk::Shared<Timer>>(ctx); - - if (timer->isRunning()) { - timer->stop(); - } - - return 0; -} - -const duk::FunctionMap methods{ - { "start", { start, 0 } }, - { "stop", { stop, 0 } } -}; - -/* - * 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. - */ -duk::Ret constructor(duk::ContextPtr ctx) -{ - int type = duk::require<int>(ctx, 0); - int delay = duk::require<int>(ctx, 1); - - if (!duk::is<duk::Function>(ctx, 2)) { - duk::raise(ctx, duk::TypeError("missing callback function")); - } - - auto timer = std::make_shared<Timer>(static_cast<TimerType>(type), delay); - - /* Add this timer to the underlying plugin */ - duk::getGlobal<duk::RawPointer<JsPlugin>>(ctx, "\xff""\xff""plugin")->addTimer(timer); - - /* Construct object */ - duk::construct(ctx, duk::Shared<Timer>{timer}); - - /* Now store the JavaScript function to call */ - duk::dup(ctx, 2); - duk::putGlobal(ctx, "\xff""\xff""timer-" + std::to_string(reinterpret_cast<std::intptr_t>(timer.get()))); - - return 0; -} - -const duk::Map<int> constants{ - { "Single", static_cast<int>(TimerType::Single) }, - { "Repeat", static_cast<int>(TimerType::Repeat) }, -}; - -} // !namespace - -void loadJsTimer(duk::ContextPtr ctx) noexcept -{ - duk::StackAssert sa(ctx); - - duk::getGlobal<void>(ctx, "Irccd"); - duk::push(ctx, duk::Function{constructor, 3}); - duk::push(ctx, constants); - duk::push(ctx, duk::Object{}); - duk::push(ctx, methods); - duk::putProperty(ctx, -2, "prototype"); - duk::putProperty(ctx, -2, "Timer"); - duk::pop(ctx); -} - -} // !irccd
--- a/lib/irccd/js-timer.hpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -/* - * js-timer.hpp -- Irccd.Timer API - * - * Copyright (c) 2013-2016 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_HPP -#define IRCCD_JS_TIMER_HPP - -/** - * \file js-timer.hpp - * \brief Irccd.Timer JavaScript API. - */ - -#include "js.hpp" - -namespace irccd { - -/** - * Load the module. - * - * \param ctx the context. - */ -void loadJsTimer(duk::ContextPtr ctx) noexcept; - -} // !irccd - -#endif // !IRCCD_JS_TIMER_HPP -
--- a/lib/irccd/js-unicode.cpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,144 +0,0 @@ -/* - * js-unicode.cpp -- Irccd.Unicode API - * - * Copyright (c) 2013-2016 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.hpp" -#include "unicode.hpp" - -namespace irccd { - -namespace { - -/* - * Function: Irccd.Unicode.isDigit(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is in the digit category. - */ -duk::Ret isDigit(duk::ContextPtr ctx) -{ - duk::push(ctx, unicode::isdigit(duk::get<int>(ctx, 0))); - - return 1; -} - -/* - * Function: Irccd.Unicode.isLetter(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is in the letter category. - */ -duk::Ret isLetter(duk::ContextPtr ctx) -{ - duk::push(ctx, unicode::isalpha(duk::get<int>(ctx, 0))); - - return 1; -} - -/* - * Function: Irccd.Unicode.isLower(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is lower case. - */ -duk::Ret isLower(duk::ContextPtr ctx) -{ - duk::push(ctx, unicode::islower(duk::get<int>(ctx, 0))); - - return 1; -} - -/* - * Function: Irccd.Unicode.isSpace(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is in the space category. - */ -duk::Ret isSpace(duk::ContextPtr ctx) -{ - duk::push(ctx, unicode::isspace(duk::get<int>(ctx, 0))); - - return 1; -} - -/* - * Function: Irccd.Unicode.isTitle(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is title case. - */ -duk::Ret isTitle(duk::ContextPtr ctx) -{ - duk::push(ctx, unicode::istitle(duk::get<int>(ctx, 0))); - - return 1; -} - -/* - * Function: Irccd.Unicode.isUpper(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is upper case. - */ -duk::Ret isUpper(duk::ContextPtr ctx) -{ - duk::push(ctx, unicode::isupper(duk::get<int>(ctx, 0))); - - return 1; -} - -const duk::FunctionMap functions{ - { "isDigit", { isDigit, 1 } }, - { "isLetter", { isLetter, 1 } }, - { "isLower", { isLower, 1 } }, - { "isSpace", { isSpace, 1 } }, - { "isTitle", { isTitle, 1 } }, - { "isUpper", { isUpper, 1 } }, -}; - -} // !namespace - -void loadJsUnicode(duk::ContextPtr ctx) -{ - duk::StackAssert sa(ctx); - - duk::getGlobal<void>(ctx, "Irccd"); - duk::push(ctx, duk::Object{}); - duk::push(ctx, functions); - duk::putProperty(ctx, -2, "Unicode"); - duk::pop(ctx); -} - -} // !irccd
--- a/lib/irccd/js-unicode.hpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -/* - * js-unicode.cpp -- Irccd.Unicode API - * - * Copyright (c) 2013-2016 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_HPP -#define IRCCD_JS_UNICODE_HPP - -/** - * \file js-unicode.hpp - * \brief Irccd.Unicode JavaScript API. - */ - -#include "js.hpp" - -namespace irccd { - -/** - * Load the module. - * - * \param ctx the context. - */ -void loadJsUnicode(duk::ContextPtr ctx); - -} // !irccd - -#endif // !IRCCD_JS_UNICODE_HPP
--- a/lib/irccd/js-util.cpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,152 +0,0 @@ -/* - * js-util.cpp -- Irccd.Util API - * - * Copyright (c) 2013-2016 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 <libircclient.h> - -#include "js-util.hpp" -#include "util.hpp" - -namespace irccd { - -namespace duk { - -/** - * Read parameters for Irccd.Util.format function, the object is defined as follow: - * - * { - * 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: ... - * } - */ -template <> -class TypeTraits<util::Substitution> { -public: - static util::Substitution get(ContextPtr ctx, int index) - { - util::Substitution params; - - if (!duk::is<Object>(ctx, index)) { - return params; - } - - duk::enumerate(ctx, index, 0, true, [&] (ContextPtr) { - if (duk::get<std::string>(ctx, -2) == "date") { - params.time = static_cast<time_t>(duk::get<double>(ctx, -1) / 1000); - } else { - params.keywords.insert({duk::get<std::string>(ctx, -2), duk::get<std::string>(ctx, -1)}); - } - }); - - return params; - } -}; - -} // !duk - -namespace { - -/* - * Function: Irccd.Util.format(text, parameters) - * -------------------------------------------------------- - * - * Format a string with templates. - * - * Arguments: - * - input, the text to update, - * - params, the parameters. - * Returns: - * The converted text. - */ -duk::Ret format(duk::ContextPtr ctx) -{ - try { - duk::push(ctx, util::format(duk::get<std::string>(ctx, 0), duk::get<util::Substitution>(ctx, 1))); - } catch (const std::exception &ex) { - duk::raise(ctx, duk::SyntaxError(ex.what())); - } - - return 1; -} - -/* - * Function: Irccd.Util.splituser(ident) - * -------------------------------------------------------- - * - * Return the nickname part from a full username. - * - * Arguments: - * - ident, the full identity. - * Returns: - * The nickname. - */ -duk::Ret splituser(duk::ContextPtr ctx) -{ - const char *target = duk::require<const char *>(ctx, 0); - char nick[32] = {0}; - - irc_target_get_nick(target, nick, sizeof (nick) -1); - duk::push(ctx, std::string(nick)); - - return 1; -} - -/* - * Function: Irccd.Util.splithost(ident) - * -------------------------------------------------------- - * - * Return the hostname part from a full username. - * - * Arguments: - * - ident, the full identity. - * Returns: - * The hostname. - */ -duk::Ret splithost(duk::ContextPtr ctx) -{ - const char *target = duk::require<const char *>(ctx, 0); - char host[32] = {0}; - - irc_target_get_host(target, host, sizeof (host) -1); - duk::push(ctx, std::string(host)); - - return 1; -} - -const duk::FunctionMap functions{ - { "format", { format, DUK_VARARGS } }, - { "splituser", { splituser, 1 } }, - { "splithost", { splithost, 1 } } -}; - -} // !namespace - -void loadJsUtil(duk::ContextPtr ctx) -{ - duk::StackAssert sa(ctx); - - duk::getGlobal<void>(ctx, "Irccd"); - duk::push(ctx, duk::Object{}); - duk::push(ctx, functions); - duk::putProperty(ctx, -2, "Util"); - duk::pop(ctx); -} - -} // !irccd
--- a/lib/irccd/js-util.hpp Fri May 13 12:06:06 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -/* - * js-util.hpp -- Irccd.Util API - * - * Copyright (c) 2013-2016 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_HPP -#define IRCCD_JS_UTIL_HPP - -/** - * \file js-util.hpp - * \brief Irccd.Util JavaScript API. - */ - -#include "js.hpp" - -namespace irccd { - -/** - * Load the module. - * - * \param ctx the context. - */ -void loadJsUtil(duk::ContextPtr ctx); - -} // !irccd - -#endif // !IRCCD_JS_UTIL_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-directory.cpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,390 @@ +/* + * js-directory.cpp -- Irccd.Directory API + * + * Copyright (c) 2013-2016 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 <cerrno> +#include <cstdio> +#include <cstring> +#include <fstream> +#include <regex> +#include <stdexcept> +#include <string> + +#include "fs.hpp" +#include "js.hpp" +#include "mod-directory.hpp" +#include "mod-irccd.hpp" +#include "path.hpp" +#include "plugin-js.hpp" +#include "sysconfig.hpp" + +namespace irccd { + +namespace { + +std::string path(duk::ContextPtr ctx) +{ + duk::push(ctx, duk::This{}); + duk::getProperty<void>(ctx, -1, "path"); + + if (duk::type(ctx, -1) != DUK_TYPE_STRING) { + duk::raise(ctx, duk::TypeError("invalid this binding")); + } + + std::string ret = duk::get<std::string>(ctx, -1); + + if (ret.empty()) { + duk::raise(ctx, duk::TypeError("invalid directory with empty path")); + } + + duk::pop(ctx, 2); + + return ret; +} + +/* + * Find an entry recursively (or not) in a directory using a predicate + * which can be used to test for regular expression, equality. + * + * Do not use this function directly, use: + * + * - findName + * - findRegex + */ +template <typename Pred> +std::string findPath(const std::string &base, bool recursive, Pred pred) +{ + /* + * For performance reason, we first iterate over all entries that are + * not directories to avoid going deeper recursively if the requested + * file is in the current directory. + */ + auto entries = fs::readdir(base); + + for (const auto &entry : entries) { + if (entry.type != fs::Entry::Dir && pred(entry.name)) { + return base + entry.name; + } + } + + if (!recursive) { + return ""; + } + + for (const auto &entry : entries) { + if (entry.type == fs::Entry::Dir) { + std::string next = base + entry.name + fs::separator(); + std::string path = findPath(next, true, pred); + + if (!path.empty()) { + return path; + } + } + } + + return ""; +} + +/* + * Helper for finding by equality. + */ +std::string findName(std::string base, const std::string &pattern, bool recursive) +{ + return findPath(base, recursive, [&] (const std::string &entryname) -> bool { + return pattern == entryname; + }); +} + +/* + * Helper for finding by regular expression + */ +std::string findRegex(const std::string &base, std::string pattern, bool recursive) +{ + std::regex regexp(pattern, std::regex::ECMAScript); + std::smatch smatch; + + return findPath(base, recursive, [&] (const std::string &entryname) -> bool { + return std::regex_match(entryname, smatch, regexp); + }); +} + +/* + * Generic find function for: + * + * - Directory.find + * - Directory.prototype.find + * + * The patternIndex is the argument where to test if the argument is a regex or a string. + */ +duk::Ret find(duk::ContextPtr ctx, std::string base, bool recursive, int patternIndex) +{ + base = path::clean(base); + + try { + std::string path; + + if (duk::is<std::string>(ctx, patternIndex)) { + path = findName(base, duk::get<std::string>(ctx, patternIndex), recursive); + } else { + /* Check if it's a valid RegExp object */ + duk::getGlobal<void>(ctx, "RegExp"); + + bool isRegex = duk::instanceof(ctx, patternIndex, -1); + + duk::pop(ctx); + + if (isRegex) { + path = findRegex(base, duk::getProperty<std::string>(ctx, patternIndex, "source"), recursive); + } else { + duk::raise(ctx, duk::TypeError("pattern must be a string or a regex expression")); + } + } + + if (path.empty()) { + return 0; + } + + duk::push(ctx, path); + } catch (const std::exception &ex) { + duk::raise(ctx, duk::Error(ex.what())); + } + + return 1; +} + +/* + * Generic remove function for: + * + * - Directory.remove + * - Directory.prototype.remove + */ +duk::Ret remove(duk::ContextPtr ctx, const std::string &path, bool recursive) +{ + if (!fs::isDirectory(path)) { + duk::raise(ctx, SystemError(EINVAL, "not a directory")); + } + + if (!recursive) { +#if defined(_WIN32) + ::RemoveDirectory(path.c_str()); +#else + ::remove(path.c_str()); +#endif + } else { + fs::rmdir(path.c_str()); + } + + return 0; +} + +/* + * Method: Directory.find(pattern, recursive) + * -------------------------------------------------------- + * + * Synonym of 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: + * - Any exception on error. + */ +duk::Ret methodFind(duk::ContextPtr ctx) +{ + return find(ctx, path(ctx), duk::optional<bool>(ctx, 1, false), 0); +} + +/* + * Method: Directory.remove(recursive) + * -------------------------------------------------------- + * + * Synonym of Directory.remove(recursive) but the path is taken from the + * directory object. + * + * Arguments: + * - recursive, recursively or not (default: false). + * Throws: + * - Any exception on error. + */ +duk::Ret methodRemove(duk::ContextPtr ctx) +{ + return remove(ctx, path(ctx), duk::optional<bool>(ctx, 0, false)); +} + +const duk::FunctionMap methods{ + { "find", { methodFind, DUK_VARARGS } }, + { "remove", { methodRemove, 1 } } +}; + +/* -------------------------------------------------------- + * Directory "static" functions + * -------------------------------------------------------- */ + +/* + * Function: Irccd.Directory(path, flags) [constructor] + * -------------------------------------------------------- + * + * Opens and read the directory at the specified path. + * + * Arguments: + * - path, the path to the directory, + * - flags, the optional flags (default: 0). + * Throws: + * - Any exception on error + */ +duk::Ret constructor(duk::ContextPtr ctx) +{ + if (!duk_is_constructor_call(ctx)) { + return 0; + } + + try { + std::string path = duk::require<std::string>(ctx, 0); + std::int8_t flags = duk::optional<int>(ctx, 1, 0); + + if (!fs::isDirectory(path)) { + duk::raise(ctx, SystemError(EINVAL, "not a directory")); + } + + std::vector<fs::Entry> list = fs::readdir(path, flags); + + duk::push(ctx, duk::This{}); + duk::push(ctx, "count"); + duk::push(ctx, (int)list.size()); + duk::defineProperty(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); + duk::push(ctx, "path"); + duk::push(ctx, path); + duk::defineProperty(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); + duk::push(ctx, "entries"); + duk::push(ctx, duk::Array{}); + + for (unsigned i = 0; i < list.size(); ++i) { + duk::push(ctx, duk::Object{}); + duk::putProperty(ctx, -1, "name", list[i].name); + duk::putProperty(ctx, -1, "type", static_cast<int>(list[i].type)); + duk::putProperty(ctx, -2, (int)i); + } + + duk::defineProperty(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); + } catch (const std::exception &ex) { + duk::raise(ctx, SystemError(errno, ex.what())); + } + + return 0; +} + +/* + * 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. + */ +duk::Ret funcFind(duk::ContextPtr ctx) +{ + return find(ctx, duk::require<std::string>(ctx, 0), duk::optional<bool>(ctx, 2, false), 1); +} + +/* + * 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: + * - Any exception on error. + */ +duk::Ret funcRemove(duk::ContextPtr ctx) +{ + return remove(ctx, duk::require<std::string>(ctx, 0), duk::optional<bool>(ctx, 1, false)); +} + +/* + * 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, + * - mode, the mode, not available on all platforms. + * Throws: + * - Any exception on error. + */ +duk::Ret funcMkdir(duk::ContextPtr ctx) +{ + try { + fs::mkdir(duk::require<std::string>(ctx, 0), duk::optional<int>(ctx, 1, 0700)); + } catch (const std::exception &ex) { + duk::raise(ctx, SystemError(errno, ex.what())); + } + + return 0; +} + +const duk::FunctionMap functions{ + { "find", { funcFind, DUK_VARARGS } }, + { "mkdir", { funcMkdir, DUK_VARARGS } }, + { "remove", { funcRemove, DUK_VARARGS } } +}; + +const duk::Map<int> constants{ + { "Dot", static_cast<int>(fs::Dot) }, + { "DotDot", static_cast<int>(fs::DotDot) }, + { "TypeUnknown", static_cast<int>(fs::Entry::Unknown) }, + { "TypeDir", static_cast<int>(fs::Entry::Dir) }, + { "TypeFile", static_cast<int>(fs::Entry::File) }, + { "TypeLink", static_cast<int>(fs::Entry::Link) } +}; + +} // !namespace + +DirectoryModule::DirectoryModule() noexcept + : Module("Irccd.Directory") +{ +} + +void DirectoryModule::load(Irccd &, JsPlugin &plugin) +{ + duk::StackAssert sa(plugin.context()); + + duk::getGlobal<void>(plugin.context(), "Irccd"); + duk::push(plugin.context(), duk::Function{constructor, 2}); + duk::push(plugin.context(), constants); + duk::push(plugin.context(), functions); + duk::putProperty(plugin.context(), -1, "separator", std::string{fs::separator()}); + duk::push(plugin.context(), duk::Object{}); + duk::push(plugin.context(), methods); + duk::putProperty(plugin.context(), -2, "prototype"); + duk::putProperty(plugin.context(), -2, "Directory"); + duk::pop(plugin.context()); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-directory.hpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,51 @@ +/* + * mod-directory.hpp -- Irccd.Directory API + * + * Copyright (c) 2013-2016 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_MOD_DIRECTORY_HPP +#define IRCCD_MOD_DIRECTORY_HPP + +/** + * \file mod-directory.hpp + * \brief Irccd.Directory JavaScript API. + */ + +#include "module.hpp" + +namespace irccd { + +class Irccd; + +/** + * \brief Irccd.Directory JavaScript API. + */ +class DirectoryModule : public Module { +public: + /** + * Irccd.Directory. + */ + DirectoryModule() noexcept; + + /** + * \copydoc Module::load + */ + virtual void load(Irccd &irccd, JsPlugin &plugin); +}; + +} // !irccd + +#endif // !IRCCD_MOD_DIRECTORY_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-elapsed-timer.cpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,140 @@ +/* + * js-elapsed-timer.cpp -- Irccd.ElapsedTimer API + * + * Copyright (c) 2013-2016 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 "elapsed-timer.hpp" +#include "mod-elapsed-timer.hpp" +#include "plugin-js.hpp" + +namespace irccd { + +namespace duk { + +template <> +class TypeTraits<ElapsedTimer> { +public: + static inline std::string name() + { + return "\xff""\xff""ElapsedTimer"; + } + + static inline std::vector<std::string> inherits() + { + return {}; + } +}; + +} // !duk + +namespace { + +/* + * Method: ElapsedTimer.pause + * ------------------------------------------------------------------ + * + * Pause the timer, without resetting the current elapsed time stored. + */ +duk::Ret pause(duk::ContextPtr ctx) +{ + duk::self<duk::Pointer<ElapsedTimer>>(ctx)->pause(); + + return 0; +} + +/* + * Method: ElapsedTimer.reset + * ------------------------------------------------------------------ + * + * Reset the elapsed time to 0, the status is not modified. + */ +duk::Ret reset(duk::ContextPtr ctx) +{ + duk::self<duk::Pointer<ElapsedTimer>>(ctx)->reset(); + + return 0; +} + +/* + * Method: ElapsedTimer.restart + * ------------------------------------------------------------------ + * + * Restart the timer without resetting the current elapsed time. + */ +duk::Ret restart(duk::ContextPtr ctx) +{ + duk::self<duk::Pointer<ElapsedTimer>>(ctx)->restart(); + + return 0; +} + +/* + * Method: ElapsedTimer.elapsed + * ------------------------------------------------------------------ + * + * Get the number of elapsed milliseconds. + * + * Returns: + * The time elapsed. + */ +duk::Ret elapsed(duk::ContextPtr ctx) +{ + duk::push(ctx, (int)duk::self<duk::Pointer<ElapsedTimer>>(ctx)->elapsed()); + + return 1; +} + +/* + * Function: Irccd.ElapsedTimer() [constructor] + * ------------------------------------------------------------------ + * + * Construct a new ElapsedTimer object. + */ +duk::Ret constructor(duk::ContextPtr ctx) +{ + duk::construct(ctx, duk::Pointer<ElapsedTimer>{new ElapsedTimer}); + + return 0; +} + +const duk::FunctionMap methods{ + { "elapsed", { elapsed, 0 } }, + { "pause", { pause, 0 } }, + { "reset", { reset, 0 } }, + { "restart", { restart, 0 } } +}; + +} // !namespace + +ElapsedTimerModule::ElapsedTimerModule() noexcept + : Module("Irccd.ElapsedTimer") +{ +} + +void ElapsedTimerModule::load(Irccd &, JsPlugin &plugin) +{ + duk::StackAssert sa(plugin.context()); + + duk::getGlobal<void>(plugin.context(), "Irccd"); + duk::push(plugin.context(), duk::Function{constructor, 0}); + duk::push(plugin.context(), duk::Object{}); + duk::push(plugin.context(), methods); + duk::putProperty(plugin.context(), -2, "prototype"); + duk::putProperty(plugin.context(), -2, "ElapsedTimer"); + duk::pop(plugin.context()); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-elapsed-timer.hpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,49 @@ +/* + * mod-elapsed-timer.hpp -- Irccd.ElapsedTimer API + * + * Copyright (c) 2013-2016 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_MOD_ELAPSED_TIMER_HPP +#define IRCCD_MOD_ELAPSED_TIMER_HPP + +/** + * \file mod-elapsed-timer.hpp + * \brief Irccd.ElapsedTimer JavaScript API. + */ + +#include "module.hpp" + +namespace irccd { + +/** + * \brief Irccd.ElapsedTimer JavaScript API. + */ +class ElapsedTimerModule : public Module { +public: + /** + * Irccd.ElapsedTimer. + */ + ElapsedTimerModule() noexcept; + + /** + * \copydoc Module::load + */ + virtual void load(Irccd &irccd, JsPlugin &plugin); +}; + +} // !irccd + +#endif // !IRCCD_MOD_ELAPSED_TIMER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-file.cpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,632 @@ +/* + * js-file.cpp -- Irccd.File API + * + * Copyright (c) 2013-2016 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 <iterator> +#include <vector> + +#include "sysconfig.hpp" + +#if defined(HAVE_STAT) +# include <sys/types.h> +# include <sys/stat.h> +#endif + +#include "fs.hpp" +#include "mod-file.hpp" +#include "mod-irccd.hpp" +#include "plugin-js.hpp" + +namespace irccd { + +#if defined(HAVE_STAT) + +/* + * duk::TypeInfo specialization for struct stat + * ------------------------------------------------------------------ + */ + +namespace duk { + +template <> +class TypeTraits<struct stat> { +public: + static void push(ContextPtr ctx, const struct stat &st) + { + duk::push(ctx, Object{}); + +#if defined(HAVE_STAT_ST_ATIME) + duk::putProperty(ctx, -2, "atime", static_cast<int>(st.st_atime)); +#endif +#if defined(HAVE_STAT_ST_BLKSIZE) + duk::putProperty(ctx, -2, "blksize", static_cast<int>(st.st_blksize)); +#endif +#if defined(HAVE_STAT_ST_BLOCKS) + duk::putProperty(ctx, -2, "blocks", static_cast<int>(st.st_blocks)); +#endif +#if defined(HAVE_STAT_ST_CTIME) + duk::putProperty(ctx, -2, "ctime", static_cast<int>(st.st_ctime)); +#endif +#if defined(HAVE_STAT_ST_DEV) + duk::putProperty(ctx, -2, "dev", static_cast<int>(st.st_dev)); +#endif +#if defined(HAVE_STAT_ST_GID) + duk::putProperty(ctx, -2, "gid", static_cast<int>(st.st_gid)); +#endif +#if defined(HAVE_STAT_ST_INO) + duk::putProperty(ctx, -2, "ino", static_cast<int>(st.st_ino)); +#endif +#if defined(HAVE_STAT_ST_MODE) + duk::putProperty(ctx, -2, "mode", static_cast<int>(st.st_mode)); +#endif +#if defined(HAVE_STAT_ST_MTIME) + duk::putProperty(ctx, -2, "mtime", static_cast<int>(st.st_mtime)); +#endif +#if defined(HAVE_STAT_ST_NLINK) + duk::putProperty(ctx, -2, "nlink", static_cast<int>(st.st_nlink)); +#endif +#if defined(HAVE_STAT_ST_RDEV) + duk::putProperty(ctx, -2, "rdev", static_cast<int>(st.st_rdev)); +#endif +#if defined(HAVE_STAT_ST_SIZE) + duk::putProperty(ctx, -2, "size", static_cast<int>(st.st_size)); +#endif +#if defined(HAVE_STAT_ST_UID) + duk::putProperty(ctx, -2, "uid", static_cast<int>(st.st_uid)); +#endif + } +}; + +} // !duk + +#endif // !HAVE_STAT + +namespace { + +/* + * Anonymous helpers. + * ------------------------------------------------------------------ + */ + +/* Remove trailing \r for CRLF line style */ +inline std::string clearCr(std::string input) +{ + if (input.length() > 0 && input.back() == '\r') { + input.pop_back(); + } + + return input; +} + +/* + * File methods. + * ------------------------------------------------------------------ + */ + +/* + * Method: File.basename() + * -------------------------------------------------------- + * + * Synonym of `Irccd.File.basename(path)` but with the path from the file. + * + * Returns: + * The base name. + */ +duk::Ret methodBasename(duk::ContextPtr ctx) +{ + duk::push(ctx, fs::baseName(duk::self<duk::Pointer<File>>(ctx)->path())); + + return 1; +} + +/* + * Method: File.close() + * -------------------------------------------------------- + * + * Force close of the file, automatically called when object is collected. + */ +duk::Ret methodClose(duk::ContextPtr ctx) +{ + duk::self<duk::Pointer<File>>(ctx)->close(); + + return 0; +} + +/* + * Method: File.dirname() + * -------------------------------------------------------- + * + * Synonym of `Irccd.File.dirname(path)` but with the path from the file. + * + * Returns: + * The directory name. + */ +duk::Ret methodDirname(duk::ContextPtr ctx) +{ + duk::push(ctx, fs::dirName(duk::self<duk::Pointer<File>>(ctx)->path())); + + return 1; +} + +/* + * Method: File.lines() + * -------------------------------------------------------- + * + * Read all lines and return an array. + * + * Returns: + * An array with all lines. + * Throws + * - Any exception on error. + */ +duk::Ret methodLines(duk::ContextPtr ctx) +{ + duk::push(ctx, duk::Array{}); + + std::FILE *fp = duk::self<duk::Pointer<File>>(ctx)->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(); + + auto pos = buffer.find('\n'); + + if (pos != std::string::npos) { + duk::putProperty(ctx, -1, i++, clearCr(buffer.substr(0, pos))); + buffer.erase(0, pos + 1); + } + } + + /* Maybe an error in the stream */ + if (std::ferror(fp)) { + duk::raise(ctx, SystemError()); + } + + /* Missing '\n' in end of file */ + if (!buffer.empty()) { + duk::putProperty(ctx, -1, i++, clearCr(buffer)); + } + + return 1; +} + +/* + * Method: File.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: + * - Any exception on error. + */ +duk::Ret methodRead(duk::ContextPtr ctx) +{ + auto amount = duk::optional<int>(ctx, 0, -1); + auto file = duk::self<duk::Pointer<File>>(ctx); + + if (amount == 0 || file->handle() == nullptr) { + return 0; + } + + try { + std::string data; + std::size_t total = 0; + + if (amount < 0) { + std::array<char, 128> buffer; + std::size_t nread; + + while (!std::feof(file->handle())) { + nread = std::fread(&buffer[0], sizeof (buffer[0]), buffer.size(), file->handle()); + + if (std::ferror(file->handle())) { + duk::raise(ctx, SystemError()); + } + + std::copy(buffer.begin(), buffer.begin() + nread, std::back_inserter(data)); + total += nread; + } + } else { + data.resize((std::size_t)amount); + total = std::fread(&data[0], sizeof (data[0]), (std::size_t)amount, file->handle()); + + if (std::ferror(file->handle())) { + duk::raise(ctx, SystemError()); + } + + data.resize(total); + } + + duk::push(ctx, data); + } catch (const std::exception &) { + duk::raise(ctx, SystemError()); + } + + return 1; +} + +/* + * Method: File.readline() + * -------------------------------------------------------- + * + * Read the next line available. + * + * Returns: + * The next line or undefined if eof. + * Throws: + * - Any exception on error. + */ +duk::Ret methodReadline(duk::ContextPtr ctx) +{ + std::FILE *fp = duk::self<duk::Pointer<File>>(ctx)->handle(); + std::string result; + + if (fp == nullptr || std::feof(fp)) { + return 0; + } + + for (int ch; (ch = std::fgetc(fp)) != EOF && ch != '\n'; ) { + result += (char)ch; + } + + if (std::ferror(fp)) { + duk::raise(ctx, SystemError()); + } + + duk::push(ctx, clearCr(result)); + + return 1; +} + +/* + * Method: File.remove() + * -------------------------------------------------------- + * + * Synonym of File.remove(path) but with the path from the file. + * + * Throws: + * - Any exception on error. + */ +duk::Ret methodRemove(duk::ContextPtr ctx) +{ + if (::remove(duk::self<duk::Pointer<File>>(ctx)->path().c_str()) < 0) { + duk::raise(ctx, SystemError()); + } + + return 0; +} + +/* + * Method: File.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: + * - Any exception on error. + */ +duk::Ret methodSeek(duk::ContextPtr ctx) +{ + auto type = duk::require<int>(ctx, 0); + auto amount = duk::require<int>(ctx, 1); + auto fp = duk::self<duk::Pointer<File>>(ctx)->handle(); + + if (fp != nullptr && std::fseek(fp, amount, type) != 0) { + duk::raise(ctx, SystemError()); + } + + return 0; +} + +#if defined(HAVE_STAT) + +/* + * Method: File.stat() [optional] + * -------------------------------------------------------- + * + * Synonym of File.stat(path) but with the path from the file. + * + * Returns: + * The stat information. + * Throws: + * - Any exception on error. + */ +duk::Ret methodStat(duk::ContextPtr ctx) +{ + struct stat st; + auto file = duk::self<duk::Pointer<File>>(ctx); + + if (file->handle() == nullptr && ::stat(file->path().c_str(), &st) < 0) { + duk::raise(ctx, SystemError()); + } else { + duk::push(ctx, st); + } + + return 1; +} + +#endif // !HAVE_STAT + +/* + * Method: File.tell() + * -------------------------------------------------------- + * + * Get the actual position in the file. + * + * Returns: + * The position. + * Throws: + * - Any exception on error. + */ +duk::Ret methodTell(duk::ContextPtr ctx) +{ + auto fp = duk::self<duk::Pointer<File>>(ctx)->handle(); + long pos; + + if (fp == nullptr) { + return 0; + } + + if ((pos = std::ftell(fp)) == -1L) { + duk::raise(ctx, SystemError()); + } else { + duk::push(ctx, (int)pos); + } + + return 1; +} + +/* + * Method: File.write(data) + * -------------------------------------------------------- + * + * Write some characters to the file. + * + * Arguments: + * - data, the character to write. + * Returns: + * The number of bytes written. + * Throws: + * - Any exception on error. + */ +duk::Ret methodWrite(duk::ContextPtr ctx) +{ + std::FILE *fp = duk::self<duk::Pointer<File>>(ctx)->handle(); + std::string data = duk::require<std::string>(ctx, 0); + + if (fp == nullptr) { + return 0; + } + + std::size_t nwritten = std::fwrite(data.c_str(), 1, data.length(), fp); + + if (std::ferror(fp)) { + duk::raise(ctx, SystemError()); + } + + duk::push(ctx, (int)nwritten); + + return 1; +} + +const duk::FunctionMap methods{ + { "basename", { methodBasename, 0 } }, + { "close", { methodClose, 0 } }, + { "dirname", { methodDirname, 0 } }, + { "lines", { methodLines, 0 } }, + { "read", { methodRead, 1 } }, + { "readline", { methodReadline, 0 } }, + { "remove", { methodRemove, 0 } }, + { "seek", { methodSeek, 2 } }, +#if defined(HAVE_STAT) + { "stat", { methodStat, 0 } }, +#endif + { "tell", { methodTell, 0 } }, + { "write", { methodWrite, 1 } }, +}; + +/* + * File "static" functions + * ------------------------------------------------------------------ + */ + +/* + * 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: + * - Any exception on error. + */ +duk::Ret constructor(duk::ContextPtr ctx) +{ + if (!duk_is_constructor_call(ctx)) { + return 0; + } + + std::string path = duk::require<std::string>(ctx, 0); + std::string mode = duk::require<std::string>(ctx, 1); + + try { + duk::construct(ctx, duk::Pointer<File>{new File(path, mode)}); + } catch (const std::exception &) { + duk::raise(ctx, SystemError()); + } + + return 0; +} + +/* + * Function: Irccd.File.basename(path) + * -------------------------------------------------------- + * + * Return the file basename as specified in `basename(3)` C function. + * + * Arguments: + * - path, the path to the file. + * Returns: + * The base name. + */ +duk::Ret functionBasename(duk::ContextPtr ctx) +{ + duk::push(ctx, fs::baseName(duk::require<std::string>(ctx, 0))); + + return 1; +} + +/* + * Function: Irccd.File.dirname(path) + * -------------------------------------------------------- + * + * Return the file directory name as specified in `dirname(3)` C function. + * + * Arguments: + * - path, the path to the file. + * Returns: + * The directory name. + */ +duk::Ret functionDirname(duk::ContextPtr ctx) +{ + duk::push(ctx, fs::dirName( duk::require<std::string>(ctx, 0))); + + return 1; +} + +/* + * Function: Irccd.File.exists(path) + * -------------------------------------------------------- + * + * Check if the file exists. + * + * Arguments: + * - path, the path to the file. + * Returns: + * True if exists. + * Throws: + * - Any exception if we don't have access. + */ +duk::Ret functionExists(duk::ContextPtr ctx) +{ + duk::push(ctx, fs::exists(duk::require<std::string>(ctx, 0))); + + return 1; +} + +/* + * function Irccd.File.remove(path) + * -------------------------------------------------------- + * + * Remove the file at the specified path. + * + * Arguments: + * - path, the path to the file. + * Throws: + * - Any exception on error. + */ +duk::Ret functionRemove(duk::ContextPtr ctx) +{ + if (::remove(duk::require<std::string>(ctx, 0).c_str()) < 0) { + duk::raise(ctx, SystemError()); + } + + return 0; +} + +#if defined(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: + * - Any exception on error. + */ +duk::Ret functionStat(duk::ContextPtr ctx) +{ + struct stat st; + + if (::stat(duk::require<std::string>(ctx, 0).c_str(), &st) < 0) { + duk::raise(ctx, SystemError()); + } + + duk::push(ctx, st); + + return 1; +} + +#endif // !HAVE_STAT + +const duk::FunctionMap functions{ + { "basename", { functionBasename, 1 } }, + { "dirname", { functionDirname, 1 } }, + { "exists", { functionExists, 1 } }, + { "remove", { functionRemove, 1 } }, +#if defined(HAVE_STAT) + { "stat", { functionStat, 1 } }, +#endif +}; + +const duk::Map<int> constants{ + { "SeekCur", SEEK_CUR }, + { "SeekEnd", SEEK_END }, + { "SeekSet", SEEK_SET }, +}; + +} // !namespace + +FileModule::FileModule() noexcept + : Module("Irccd.File") +{ +} + +void FileModule::load(Irccd &, JsPlugin &plugin) +{ + duk::StackAssert sa(plugin.context()); + + duk::getGlobal<void>(plugin.context(), "Irccd"); + duk::push(plugin.context(), duk::Function{constructor, 2}); + duk::push(plugin.context(), constants); + duk::push(plugin.context(), functions); + duk::push(plugin.context(), duk::Object{}); + duk::push(plugin.context(), methods); + duk::putProperty(plugin.context(), -2, "prototype"); + duk::putProperty(plugin.context(), -2, "File"); + duk::pop(plugin.context()); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-file.hpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,202 @@ +/* + * mod-file.hpp -- Irccd.File API + * + * Copyright (c) 2013-2016 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_MOD_FILE_HPP +#define IRCCD_MOD_FILE_HPP + +/** + * \file mod-file.hpp + * \brief Irccd.File JavaScript API. + */ + +#include <cassert> +#include <cerrno> +#include <cstdio> +#include <cstring> +#include <functional> +#include <stdexcept> +#include <string> + +#include "js.hpp" +#include "module.hpp" + +namespace irccd { + +class Irccd; + +/** + * \class File + * \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 m_path; + std::FILE *m_stream; + std::function<void (std::FILE *)> m_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) + : m_path(std::move(path)) + , m_destructor([] (std::FILE *fp) { std::fclose(fp); }) + { + if ((m_stream = std::fopen(m_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 + : m_stream(fp) + , m_destructor(std::move(destructor)) + { + assert(m_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 &path() const noexcept + { + return m_path; + } + + /** + * Get the handle. + * + * \return the handle or nullptr if the stream was closed + */ + inline std::FILE *handle() noexcept + { + return m_stream; + } + + /** + * Force close, can be safely called multiple times. + */ + inline void close() noexcept + { + if (m_stream) { + m_destructor(m_stream); + m_stream = nullptr; + } + } +}; + +namespace duk { + +/** + * \brief JavaScript binding for File. + */ +template <> +class TypeTraits<File> { +public: + /** + * Push the File prototype. + * + * \param ctx the context + */ + static inline void prototype(ContextPtr ctx) + { + getGlobal<void>(ctx, "Irccd"); + getGlobal<void>(ctx, "File"); + getProperty<void>(ctx, -1, "prototype"); + remove(ctx, -2); + remove(ctx, -2); + } + + /** + * Get the File signature. + * + * \return File + */ + static inline std::string name() + { + return "\xff""\xff""File"; + } + + /** + * Get the inheritance list. + * + * \return empty + */ + static inline std::vector<std::string> inherits() + { + return {}; + } +}; + +} // !duk + +/** + * \brief Irccd.File JavaScript API. + */ +class FileModule : public Module { +public: + /** + * Irccd.File. + */ + FileModule() noexcept; + + /** + * \copydoc Module::load + */ + void load(Irccd &irccd, JsPlugin &plugin) override; +}; + +} // !irccd + +#endif // !IRCCD_MOD_FILE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-irccd.cpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,94 @@ +/* + * js-irccd.cpp -- Irccd API + * + * Copyright (c) 2013-2016 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 "mod-irccd.hpp" +#include "plugin-js.hpp" +#include "sysconfig.hpp" + +namespace irccd { + +SystemError::SystemError() + : m_errno(errno) + , m_message(std::strerror(m_errno)) +{ +} + +SystemError::SystemError(int e, std::string message) + : m_errno(e) + , m_message(std::move(message)) +{ +} + +void SystemError::raise(duk::ContextPtr ctx) const +{ + duk::StackAssert sa(ctx, 1); + + duk::getGlobal<void>(ctx, "Irccd"); + duk::getProperty<void>(ctx, -1, "SystemError"); + duk::remove(ctx, -2); + duk::push(ctx, m_errno); + duk::push(ctx, m_message); + duk::create(ctx, 2); + duk::raise(ctx); +} + +duk::Ret constructor(duk::ContextPtr ctx) +{ + duk::push(ctx, duk::This{}); + duk::putProperty(ctx, -1, "errno", duk::require<int>(ctx, 0)); + duk::putProperty(ctx, -1, "message", duk::require<std::string>(ctx, 1)); + duk::putProperty(ctx, -1, "name", "SystemError"); + duk::pop(ctx); + + return 0; +} + +IrccdModule::IrccdModule() noexcept + : Module("Irccd") +{ +} + +void IrccdModule::load(Irccd &, JsPlugin &plugin) +{ + duk::StackAssert sa(plugin.context()); + + // Irccd. + duk::push(plugin.context(), duk::Object{}); + + // Version. + duk::push(plugin.context(), duk::Object{}); + duk::putProperty(plugin.context(), -1, "major", IRCCD_VERSION_MAJOR); + duk::putProperty(plugin.context(), -1, "minor", IRCCD_VERSION_MINOR); + duk::putProperty(plugin.context(), -1, "patch", IRCCD_VERSION_PATCH); + duk::putProperty(plugin.context(), -2, "version"); + + // Create the SystemError that inherits from Error. + duk::push(plugin.context(), duk::Function{constructor, 2}); + duk::push(plugin.context(), duk::Object{}); + duk::getGlobal<void>(plugin.context(), "Error"); + duk::getProperty<void>(plugin.context(), -1, "prototype"); + duk::remove(plugin.context(), -2); + duk::setPrototype(plugin.context(), -2); + duk::putProperty(plugin.context(), -2, "prototype"); + duk::putProperty(plugin.context(), -2, "SystemError"); + + // Set Irccd as global. + duk::putGlobal(plugin.context(), "Irccd"); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-irccd.hpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,86 @@ +/* + * mod-irccd.hpp -- Irccd API + * + * Copyright (c) 2013-2016 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_MOD_IRCCD_HPP +#define IRCCD_MOD_IRCCD_HPP + +/** + * \file mod-irccd.hpp + * \brief Irccd JavaScript API. + */ + +#include <cerrno> +#include <cstring> +#include <string> + +#include "js.hpp" +#include "module.hpp" + +namespace irccd { + +class Irccd; + +/** + * \brief Custom JavaScript exception for system error. + */ +class SystemError { +private: + int m_errno; + std::string m_message; + +public: + /** + * Create a system error from the current errno value. + */ + SystemError(); + + /** + * Create a system error with the given errno and message. + * + * \param e the errno number + * \param message the message + */ + SystemError(int e, std::string message); + + /** + * Raise the SystemError. + * + * \param ctx the context + */ + void raise(duk::ContextPtr ctx) const; +}; + +/** + * Irccd JavaScript API. + */ +class IrccdModule : public Module { +public: + /** + * Irccd. + */ + IrccdModule() noexcept; + + /** + * \copydoc Module::load + */ + void load(Irccd &irccd, JsPlugin &plugin) override; +}; + +} // !irccd + +#endif // !IRCCD_MOD_IRCCD_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-logger.cpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,105 @@ +/* + * mod-logger.cpp -- Irccd.Logger API + * + * Copyright (c) 2013-2016 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 "mod-logger.hpp" +#include "logger.hpp" +#include "plugin-js.hpp" + +namespace irccd { + +namespace { + +duk::Ret print(duk::ContextPtr ctx, std::ostream &out) +{ + /* + * Get the message before we start printing stuff to avoid + * empty lines. + */ + out << "plugin " << duk::getGlobal<std::string>(ctx, "\xff""\xff""name"); + out << ": " << duk::require<std::string>(ctx, 0) << std::endl; + + return 0; +} + +/* + * Function: Irccd.Logger.info(message) + * -------------------------------------------------------- + * + * Write a verbose message. + * + * Arguments: + * - message, the message. + */ +duk::Ret info(duk::ContextPtr ctx) +{ + return print(ctx, log::info()); +} + +/* + * Function: Irccd.Logger.warning(message) + * -------------------------------------------------------- + * + * Write a warning message. + * + * Arguments: + * - message, the warning. + */ +duk::Ret warning(duk::ContextPtr ctx) +{ + return print(ctx, log::warning()); +} + +/* + * Function: Logger.debug(message) + * -------------------------------------------------------- + * + * Write a debug message, only shown if irccd is compiled in debug. + * + * Arguments: + * - message, the message. + */ +duk::Ret debug(duk::ContextPtr ctx) +{ + return print(ctx, log::debug()); +} + +const duk::FunctionMap functions{ + { "info", { info, 1 } }, + { "warning", { warning, 1 } }, + { "debug", { debug, 1 } } +}; + +} // !namespace + +LoggerModule::LoggerModule() noexcept + : Module("Irccd.Logger") +{ +} + +void LoggerModule::load(Irccd &, JsPlugin &plugin) +{ + duk::StackAssert sa(plugin.context()); + + duk::getGlobal<void>(plugin.context(), "Irccd"); + duk::push(plugin.context(), duk::Object{}); + duk::push(plugin.context(), functions); + duk::putProperty(plugin.context(), -2, "Logger"); + duk::pop(plugin.context()); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-logger.hpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,49 @@ +/* + * mod-logger.hpp -- Irccd.Logger API + * + * Copyright (c) 2013-2016 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_MOD_LOGGER_HPP +#define IRCCD_MOD_LOGGER_HPP + +/** + * \file mod-logger.hpp + * \brief Irccd.Logger JavaScript API. + */ + +#include "module.hpp" + +namespace irccd { + +/** + * \brief Irccd.Logger JavaScript API. + */ +class LoggerModule : public Module { +public: + /** + * Irccd.Logger. + */ + LoggerModule() noexcept; + + /** + * \copydoc Module::load + */ + void load(Irccd &irccd, JsPlugin &plugin) override; +}; + +} // !irccd + +#endif // !IRCCD_MOD_LOGGER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-plugin.cpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,200 @@ +/* + * js-plugin.cpp -- Irccd.Plugin API + * + * Copyright (c) 2013-2016 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.hpp" +#include "plugin-js.hpp" +#include "service-plugin.hpp" +#include "mod-plugin.hpp" + +namespace irccd { + +namespace { + +/* + * Wrap function for these functions because they all takes the same arguments. + * + * - load, + * - reload, + * - unload. + */ +template <typename Func> +duk::Ret wrap(duk::ContextPtr ctx, int nret, Func &&func) +{ + std::string name = duk::require<std::string>(ctx, 0); + + try { + func(*duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd"), name); + } catch (const std::out_of_range &ex) { + duk::raise(ctx, duk::ReferenceError(ex.what())); + } catch (const std::exception &ex) { + duk::raise(ctx, duk::Error(ex.what())); + } + + return nret; +} + +/* + * 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. + */ +duk::Ret info(duk::ContextPtr ctx) +{ + Plugin *plugin = nullptr; + + if (duk::top(ctx) >= 1) { + plugin = duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->pluginService().get(duk::require<std::string>(ctx, 0)).get(); + } else { + plugin = duk::getGlobal<duk::RawPointer<Plugin>>(ctx, "\xff""\xff""plugin"); + } + + if (!plugin) { + return 0; + } + + duk::push(ctx, duk::Object{}); + duk::putProperty(ctx, -1, "name", plugin->name()); + duk::putProperty(ctx, -1, "author", plugin->author()); + duk::putProperty(ctx, -1, "license", plugin->license()); + duk::putProperty(ctx, -1, "summary", plugin->summary()); + duk::putProperty(ctx, -1, "version", plugin->version()); + + return 1; +} + +/* + * Function: Irccd.Plugin.list() + * ------------------------------------------------------------------ + * + * Get the list of plugins, the array returned contains all plugin names. + * + * Returns: + * The list of all plugin names. + */ +duk::Ret list(duk::ContextPtr ctx) +{ + duk::push(ctx, duk::Array{}); + + int i = 0; + for (const auto &plugin : duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->pluginService().plugins()) { + duk::putProperty(ctx, -1, i++, plugin->name()); + } + + return 1; +} + +/* + * Function: Irccd.Plugin.load(name) + * ------------------------------------------------------------------ + * + * Load a plugin by name. This function will search through the standard directories. + * + * Arguments: + * - name, the plugin identifier. + * Throws: + * - Error on errors, + * - ReferenceError if the plugin was not found. + */ +duk::Ret load(duk::ContextPtr ctx) +{ + return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) { + irccd.pluginService().load(name); + }); +} + +/* + * Function: Irccd.Plugin.reload(name) + * ------------------------------------------------------------------ + * + * Reload a plugin by name. + * + * Arguments: + * - name, the plugin identifier. + * Throws: + * - Error on errors, + * - ReferenceError if the plugin was not found. + */ +duk::Ret reload(duk::ContextPtr ctx) +{ + return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) { + irccd.pluginService().reload(name); + }); +} + +/* + * Function: Irccd.Plugin.unload(name) + * ------------------------------------------------------------------ + * + * Unload a plugin by name. + * + * Arguments: + * - name, the plugin identifier. + * Throws: + * - Error on errors, + * - ReferenceError if the plugin was not found. + */ +duk::Ret unload(duk::ContextPtr ctx) +{ + return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) { + irccd.pluginService().unload(name); + }); +} + +const duk::FunctionMap functions{ + { "info", { info, DUK_VARARGS } }, + { "list", { list, 0 } }, + { "load", { load, 1 } }, + { "reload", { reload, 1 } }, + { "unload", { unload, 1 } } +}; + +} // !namespace + +PluginModule::PluginModule() noexcept + : Module("Irccd.Plugin") +{ +} + +void PluginModule::load(Irccd &, JsPlugin &plugin) +{ + duk::StackAssert sa(plugin.context()); + + duk::getGlobal<void>(plugin.context(), "Irccd"); + duk::push(plugin.context(), duk::Object{}); + duk::push(plugin.context(), functions); + duk::push(plugin.context(), duk::Object{}); + duk::putProperty(plugin.context(), -2, "config"); + duk::putProperty(plugin.context(), -2, "Plugin"); + duk::pop(plugin.context()); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-plugin.hpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,49 @@ +/* + * mod-plugin.hpp -- Irccd.Plugin API + * + * Copyright (c) 2013-2016 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_MOD_PLUGIN_HPP +#define IRCCD_MOD_PLUGIN_HPP + +/** + * \file mod-plugin.hpp + * \brief Irccd.Plugin JavaScript API. + */ + +#include "module.hpp" + +namespace irccd { + +/** + * \brief Irccd.Plugin JavaScript API. + */ +class PluginModule : public Module { +public: + /** + * Irccd.Plugin. + */ + PluginModule() noexcept; + + /** + * \copydoc Module::load + */ + virtual void load(Irccd &irccd, JsPlugin &plugin); +}; + +} // !irccd + +#endif // !IRCCD_MOD_PLUGIN_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-server.cpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,546 @@ +/* + * js-server.cpp -- Irccd.Server API + * + * Copyright (c) 2013-2016 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 <sstream> +#include <unordered_map> + +#include "irccd.hpp" +#include "mod-server.hpp" +#include "plugin-js.hpp" +#include "server.hpp" +#include "service-server.hpp" + +namespace irccd { + +namespace { + +/* + * Method: Server.cmode(channel, mode) + * ------------------------------------------------------------------ + * + * Change a channel mode. + * + * Arguments: + * - channel, the channel, + * - mode, the mode. + */ +duk::Ret cmode(duk::ContextPtr ctx) +{ + duk::self<duk::Shared<Server>>(ctx)->cmode(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); + + return 0; +} + +/* + * Method: Server.cnotice(channel, message) + * ------------------------------------------------------------------ + * + * Send a channel notice. + * + * Arguments: + * - channel, the channel, + * - message, the message. + */ +duk::Ret cnotice(duk::ContextPtr ctx) +{ + duk::self<duk::Shared<Server>>(ctx)->cnotice(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); + + return 0; +} + +/* + * Method: Server.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 + */ +duk::Ret info(duk::ContextPtr ctx) +{ + auto server = duk::self<duk::Shared<Server>>(ctx); + + duk::push(ctx, duk::Object{}); + duk::putProperty(ctx, -1, "name", server->name()); + duk::putProperty(ctx, -1, "host", server->info().host); + duk::putProperty<int>(ctx, -1, "port", server->info().port); + duk::putProperty<bool>(ctx, -1, "ssl", server->info().flags & ServerInfo::Ssl); + duk::putProperty<bool>(ctx, -1, "sslVerify", server->info().flags & ServerInfo::SslVerify); + duk::putProperty(ctx, -1, "commandChar", server->settings().command); + duk::putProperty(ctx, -1, "realname", server->identity().realname); + duk::putProperty(ctx, -1, "nickname", server->identity().nickname); + duk::putProperty(ctx, -1, "username", server->identity().username); + + /* Channels */ + duk::push(ctx, duk::Array{}); + + int i = 0; + for (const auto &channel : server->settings().channels) { + duk::putProperty(ctx, -1, i++, channel.name); + } + + duk::putProperty(ctx, -2, "channels"); + + return 1; +} + +/* + * Method: Server.invite(target, channel) + * ------------------------------------------------------------------ + * + * Invite someone to a channel. + * + * Arguments: + * - target, the target to invite, + * - channel, the channel. + */ +duk::Ret invite(duk::ContextPtr ctx) +{ + duk::self<duk::Shared<Server>>(ctx)->invite(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); + + return 0; +} + +/* + * Method: Server.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. + */ +duk::Ret join(duk::ContextPtr ctx) +{ + duk::self<duk::Shared<Server>>(ctx)->join(duk::require<std::string>(ctx, 0), duk::optional<std::string>(ctx, 1, "")); + + return 0; +} + +/* + * Method: Server.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. + */ +duk::Ret kick(duk::ContextPtr ctx) +{ + duk::self<duk::Shared<Server>>(ctx)->kick( + duk::require<std::string>(ctx, 0), + duk::require<std::string>(ctx, 1), + duk::optional<std::string>(ctx, 2, "") + ); + + return 0; +} + +/* + * Method: Server.me(target, message) + * ------------------------------------------------------------------ + * + * Send a CTCP Action. + * + * Arguments: + * - target, the target or a channel, + * - message, the message. + */ +duk::Ret me(duk::ContextPtr ctx) +{ + duk::self<duk::Shared<Server>>(ctx)->me(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); + + return 0; +} + +/* + * Method: Server.message(target, message) + * ------------------------------------------------------------------ + * + * Send a message. + * + * Arguments: + * - target, the target or a channel, + * - message, the message. + */ +duk::Ret message(duk::ContextPtr ctx) +{ + duk::self<duk::Shared<Server>>(ctx)->message(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); + + return 0; +} + +/* + * Method: Server.mode(mode) + * ------------------------------------------------------------------ + * + * Change your mode. + * + * Arguments: + * - mode, the new mode. + */ +duk::Ret mode(duk::ContextPtr ctx) +{ + duk::self<duk::Shared<Server>>(ctx)->mode(duk::require<std::string>(ctx, 0)); + + return 0; +} + +/* + * Method: Server.names(channel) + * ------------------------------------------------------------------ + * + * Get the list of names from a channel. + * + * Arguments: + * - channel, the channel. + */ +duk::Ret names(duk::ContextPtr ctx) +{ + duk::self<duk::Shared<Server>>(ctx)->names(duk::require<std::string>(ctx, 0)); + + return 0; +} + +/* + * Method: Server.nick(nickname) + * ------------------------------------------------------------------ + * + * Change the nickname. + * + * Arguments: + * - nickname, the nickname. + */ +duk::Ret nick(duk::ContextPtr ctx) +{ + duk::self<duk::Shared<Server>>(ctx)->nick(duk::require<std::string>(ctx, 0)); + + return 0; +} + +/* + * Method: Server.notice(target, message) + * ------------------------------------------------------------------ + * + * Send a private notice. + * + * Arguments: + * - target, the target, + * - message, the notice message. + */ +duk::Ret notice(duk::ContextPtr ctx) +{ + duk::self<duk::Shared<Server>>(ctx)->notice(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); + + return 0; +} + +/* + * Method: Server.part(channel, reason = undefined) + * ------------------------------------------------------------------ + * + * Leave a channel. + * + * Arguments: + * - channel, the channel to leave, + * - reason, the optional reason, keep undefined for portability. + */ +duk::Ret part(duk::ContextPtr ctx) +{ + duk::self<duk::Shared<Server>>(ctx)->part(duk::require<std::string>(ctx, 0), duk::optional<std::string>(ctx, 1, "")); + + return 0; +} + +/* + * Method: Server.send(raw) + * ------------------------------------------------------------------ + * + * Send a raw message to the IRC server. + * + * Arguments: + * - raw, the raw message (without terminators). + */ +duk::Ret send(duk::ContextPtr ctx) +{ + duk::self<duk::Shared<Server>>(ctx)->send(duk::require<std::string>(ctx, 0)); + + return 0; +} + +/* + * Method: Server.topic(channel, topic) + * ------------------------------------------------------------------ + * + * Change a channel topic. + * + * Arguments: + * - channel, the channel, + * - topic, the new topic. + */ +duk::Ret topic(duk::ContextPtr ctx) +{ + duk::self<duk::Shared<Server>>(ctx)->topic(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); + + return 0; +} + +/* + * Method: Server.whois(target) + * ------------------------------------------------------------------ + * + * Get whois information. + * + * Arguments: + * - target, the target. + */ +duk::Ret whois(duk::ContextPtr ctx) +{ + duk::self<duk::Shared<Server>>(ctx)->whois(duk::require<std::string>(ctx, 0)); + + return 0; +} + +/* + * Method: Server.toString() + * ------------------------------------------------------------------ + * + * Convert the object to std::string, convenience for adding the object + * as property key. + * + * Returns: + * The server name (unique). + */ +duk::Ret toString(duk::ContextPtr ctx) +{ + duk::push(ctx, duk::self<duk::Shared<Server>>(ctx)->name()); + + return 1; +} + +/* + * Function: Irccd.Server(params) [constructor] + * ------------------------------------------------------------------ + * + * Construct a new server. + * + * Params must be filled with the following properties: + * + * name: the name, + * host: the host, + * 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) + * sslVerify: true to verify (Optional: default true) + * 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: "!") + */ +duk::Ret constructor(duk::ContextPtr ctx) +{ + if (!duk_is_constructor_call(ctx)) + return 0; + + std::string name; + ServerInfo info; + ServerIdentity identity; + ServerSettings settings; + + // Information part. + name = duk::getProperty<std::string>(ctx, 0, "name"); + info.host = duk::getProperty<std::string>(ctx, 0, "host"); + info.port = duk::optionalProperty<int>(ctx, 0, "port", (int)info.port); + info.password = duk::optionalProperty<std::string>(ctx, 0, "password", ""); + + if (duk::optionalProperty<bool>(ctx, 0, "ipv6", false)) { + info.flags |= ServerInfo::Ipv6; + } + + // Identity part. + identity.nickname = duk::optionalProperty<std::string>(ctx, 0, "nickname", identity.nickname); + identity.username = duk::optionalProperty<std::string>(ctx, 0, "username", identity.username); + identity.realname = duk::optionalProperty<std::string>(ctx, 0, "realname", identity.realname); + identity.ctcpversion = duk::optionalProperty<std::string>(ctx, 0, "version", identity.ctcpversion); + + // Settings part. + for (const auto &chan: duk::getProperty<std::vector<std::string>>(ctx, 0, "channels")) { + settings.channels.push_back(Server::splitChannel(chan)); + } + + settings.reconnectTries = duk::optionalProperty<int>(ctx, 0, "recoTries", (int)settings.reconnectTries); + settings.reconnectDelay = duk::optionalProperty<int>(ctx, 0, "recoTimeout", (int)settings.reconnectDelay); + + if (duk::optionalProperty<bool>(ctx, 0, "joinInvite", false)) { + settings.flags |= ServerSettings::JoinInvite; + } + if (duk::optionalProperty<bool>(ctx, 0, "autoRejoin", false)) { + settings.flags |= ServerSettings::AutoRejoin; + } + + try { + duk::construct(ctx, duk::Shared<Server>{std::make_shared<Server>(std::move(name), std::move(info), + std::move(identity), std::move(settings))}); + } catch (const std::exception &ex) { + duk::raise(ctx, duk::Error(ex.what())); + } + + return 0; +} + +/* + * Function: Irccd.Server.add(s) + * ------------------------------------------------------------------ + * + * Register a new server to the irccd instance. + * + * Arguments: + * - s, the server to add. + */ +duk::Ret add(duk::ContextPtr ctx) +{ + auto server = duk::get<duk::Shared<Server>>(ctx, 0); + + if (server) { + duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->serverService().add(server); + } + + return 0; +} + +/* + * Function: Irccd.Server.find(name) + * ------------------------------------------------------------------ + * + * Find a server by name. + * + * Arguments: + * - name, the server name + * Returns: + * The server object or undefined if not found. + */ +duk::Ret find(duk::ContextPtr ctx) +{ + const auto name = duk::require<std::string>(ctx, 0); + const auto irccd = duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd"); + + try { + duk::push(ctx, duk::Shared<Server>{irccd->serverService().require(name)}); + } catch (...) { + return 0; + } + + return 1; +} + +/* + * Function: Irccd.Server.list() + * ------------------------------------------------------------------ + * + * Get the map of all loaded servers. + * + * Returns: + * An object with string-to-servers pairs. + */ +duk::Ret list(duk::ContextPtr ctx) +{ + duk::push(ctx, duk::Object{}); + + for (const auto &server : duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->serverService().servers()) { + duk::putProperty(ctx, -1, server->name(), duk::Shared<Server>{server}); + } + + return 1; +} + +/* + * 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. + */ +duk::Ret remove(duk::ContextPtr ctx) +{ + duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->serverService().remove(duk::require<std::string>(ctx, 0)); + + return 0; +} + +const duk::FunctionMap methods{ + { "cmode", { cmode, 2 } }, + { "cnotice", { cnotice, 2 } }, + { "info", { info, 0 } }, + { "invite", { invite, 2 } }, + { "join", { join, DUK_VARARGS } }, + { "kick", { kick, DUK_VARARGS } }, + { "me", { me, 2 } }, + { "message", { message, 2 } }, + { "mode", { mode, 1 } }, + { "names", { names, 1 } }, + { "nick", { nick, 1 } }, + { "notice", { notice, 2 } }, + { "part", { part, DUK_VARARGS } }, + { "send", { send, 1 } }, + { "topic", { topic, 2 } }, + { "whois", { whois, 1 } }, + { "toString", { toString, 0 } } +}; + +const duk::FunctionMap functions{ + { "add", { add, 1 } }, + { "find", { find, 1 } }, + { "list", { list, 0 } }, + { "remove", { remove, 1 } } +}; + +} // !namespace + +ServerModule::ServerModule() noexcept + : Module("Irccd.Server") +{ +} + +void ServerModule::load(Irccd &, JsPlugin &plugin) +{ + duk::StackAssert sa(plugin.context()); + + duk::getGlobal<void>(plugin.context(), "Irccd"); + duk::push(plugin.context(), duk::Function{constructor, 1}); + duk::push(plugin.context(), functions); + duk::push(plugin.context(), duk::Object()); + duk::push(plugin.context(), methods); + duk::putProperty(plugin.context(), -2, "prototype"); + duk::putProperty(plugin.context(), -2, "Server"); + duk::pop(plugin.context()); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-server.hpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,96 @@ +/* + * mod-server.hpp -- Irccd.Server API + * + * Copyright (c) 2013-2016 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_MOD_SERVER_HPP +#define IRCCD_MOD_SERVER_HPP + +/** + * \file mod-server.hpp + * \brief Irccd.Server JavaScript API. + */ + +#include "js.hpp" +#include "module.hpp" +#include "server.hpp" + +namespace irccd { + +namespace duk { + +/** + * \brief JavaScript binding for Server. + */ +template <> +class TypeTraits<irccd::Server> { +public: + /** + * Push the Server prototype. + * + * \param ctx the context + */ + static inline void prototype(ContextPtr ctx) + { + getGlobal<void>(ctx, "Irccd"); + getProperty<void>(ctx, -1, "Server"); + getProperty<void>(ctx, -1, "prototype"); + remove(ctx, -2); + remove(ctx, -2); + } + + /** + * Get the Server signature. + * + * \return Server + */ + static inline std::string name() + { + return "\xff""\xff""Server"; + } + + /** + * Get the inheritance list. + * + * \return empty + */ + static inline std::vector<std::string> inherits() + { + return {}; + } +}; + +} // !duk + +/** + * \brief Irccd.Server JavaScript API. + */ +class ServerModule : public Module { +public: + /** + * Irccd.Server. + */ + ServerModule() noexcept; + + /** + * \copydoc Module::load + */ + void load(Irccd &irccd, JsPlugin &plugin) override; +}; + +} // !irccd + +#endif // !IRCCD_JS_SERVER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-system.cpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,243 @@ +/* + * js-system.cpp -- Irccd.System API + * + * Copyright (c) 2013-2016 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 <chrono> +#include <cstdlib> +#include <thread> + +#include "sysconfig.hpp" + +#if defined(HAVE_POPEN) +# include <cstdio> +#endif + +#include "mod-file.hpp" +#include "mod-irccd.hpp" +#include "mod-system.hpp" +#include "plugin-js.hpp" +#include "system.hpp" + +namespace irccd { + +namespace { + +/* + * Function: Irccd.System.env(key) + * ------------------------------------------------------------------ + * + * Get an environment system variable. + * + * Arguments: + * - key, the environment variable. + * Returns: + * The value. + */ +int env(duk::ContextPtr ctx) +{ + duk::push(ctx, sys::env(duk::get<std::string>(ctx, 0))); + + return 1; +} + +/* + * Function: Irccd.System.exec(cmd) + * ------------------------------------------------------------------ + * + * Execute a system command. + * + * Arguments: + * - cmd, the command to execute. + */ +int exec(duk::ContextPtr ctx) +{ + std::system(duk::get<const char *>(ctx, 0)); + + return 0; +} + +/* + * Function: Irccd.System.home() + * ------------------------------------------------------------------ + * + * Get the operating system user's home. + * + * Returns: + * The user home directory. + */ +int home(duk::ContextPtr ctx) +{ + duk::push(ctx, sys::home()); + + return 1; +} + +/* + * Function: Irccd.System.name() + * ------------------------------------------------------------------ + * + * Get the operating system name. + * + * Returns: + * The system name. + */ +int name(duk::ContextPtr ctx) +{ + duk::push(ctx, sys::name()); + + return 1; +} + +#if defined(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 failures. + */ +int popen(duk::ContextPtr ctx) +{ + auto fp = ::popen(duk::require<const char *>(ctx, 0), duk::require<const char *>(ctx, 1)); + + if (fp == nullptr) { + duk::raise(ctx, SystemError{}); + } + + duk::push(ctx, duk::Pointer<File>{new File(fp, [] (std::FILE *fp) { ::pclose(fp); })}); + + return 1; +} + +#endif // !HAVE_POPEN + +/* + * Function: Irccd.System.sleep(delay) + * ------------------------------------------------------------------ + * + * Sleep the main loop for the specific delay in seconds. + */ +int sleep(duk::ContextPtr ctx) +{ + std::this_thread::sleep_for(std::chrono::seconds(duk::get<int>(ctx, 0))); + + return 0; +} + +/* + * Function: Irccd.System.ticks() + * ------------------------------------------------------------------ + * + * Get the number of milliseconds since irccd was started. + * + * Returns: + * The number of milliseconds. + */ +int ticks(duk::ContextPtr ctx) +{ + duk::push(ctx, static_cast<int>(sys::ticks())); + + return 1; +} + +/* + * Function: Irccd.System.usleep(delay) + * ------------------------------------------------------------------ + * + * Sleep the main loop for the specific delay in microseconds. + */ +int usleep(duk::ContextPtr ctx) +{ + std::this_thread::sleep_for(std::chrono::microseconds(duk::get<int>(ctx, 0))); + + return 0; +} + +/* + * Function: Irccd.System.uptime() + * ------------------------------------------------------------------ + * + * Get the system uptime. + * + * Returns: + * The system uptime. + */ +int uptime(duk::ContextPtr ctx) +{ + duk::push<int>(ctx, sys::uptime()); + + return 0; +} + +/* + * Function: Irccd.System.version() + * ------------------------------------------------------------------ + * + * Get the operating system version. + * + * Returns: + * The system version. + */ +int version(duk::ContextPtr ctx) +{ + duk::push(ctx, sys::version()); + + return 1; +} + +const duk::FunctionMap functions{ + { "env", { env, 1 } }, + { "exec", { exec, 1 } }, + { "home", { home, 0 } }, + { "name", { name, 0 } }, +#if defined(HAVE_POPEN) + { "popen", { popen, 2 } }, +#endif + { "sleep", { sleep, 1 } }, + { "ticks", { ticks, 0 } }, + { "uptime", { uptime, 0 } }, + { "usleep", { usleep, 1 } }, + { "version", { version, 0 } } +}; + +} // !namespace + +SystemModule::SystemModule() noexcept + : Module("Irccd.System") +{ +} + +void SystemModule::load(Irccd &, JsPlugin &plugin) +{ + duk::StackAssert sa(plugin.context()); + + duk::getGlobal<void>(plugin.context(), "Irccd"); + duk::push(plugin.context(), duk::Object{}); + duk::push(plugin.context(), functions); + duk::putProperty(plugin.context(), -2, "System"); + duk::pop(plugin.context()); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-system.hpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,49 @@ +/* + * mod-system.hpp -- Irccd.System API + * + * Copyright (c) 2013-2016 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_MOD_SYSTEM_HPP +#define IRCCD_MOD_SYSTEM_HPP + +/** + * \file mod-system.hpp + * \brief Irccd.System JavaScript API. + */ + +#include "module.hpp" + +namespace irccd { + +/** + * \brief Irccd.System JavaScript API. + */ +class SystemModule : public Module { +public: + /** + * Irccd.System. + */ + SystemModule() noexcept; + + /** + * \copydoc Module::load + */ + void load(Irccd &irccd, JsPlugin &plugin) override; +}; + +} // !irccd + +#endif // !IRCCD_MOD_SYSTEM_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-timer.cpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,149 @@ +/* + * js-timer.cpp -- Irccd.Timer API + * + * Copyright (c) 2013-2016 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 <cstdint> + +#include "mod-timer.hpp" +#include "plugin-js.hpp" +#include "timer.hpp" + +namespace irccd { + +namespace duk { + +template <> +class TypeTraits<Timer> { +public: + static std::string name() + { + return "\xff""\xff""Timer"; + } + + static std::vector<std::string> inherits() + { + return {}; + } +}; + +} // !duk + +namespace { + +/* + * Method: Timer.start() + * -------------------------------------------------------- + * + * Start the timer. If the timer is already started the method + * is a no-op. + */ +duk::Ret start(duk::ContextPtr ctx) +{ + auto timer = duk::self<duk::Shared<Timer>>(ctx); + + if (!timer->isRunning()) { + timer->start(); + } + + return 0; +} + +/* + * Method: Timer.stop() + * -------------------------------------------------------- + * + * Stop the timer. + */ +duk::Ret stop(duk::ContextPtr ctx) +{ + auto timer = duk::self<duk::Shared<Timer>>(ctx); + + if (timer->isRunning()) { + timer->stop(); + } + + return 0; +} + +const duk::FunctionMap methods{ + { "start", { start, 0 } }, + { "stop", { stop, 0 } } +}; + +/* + * 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. + */ +duk::Ret constructor(duk::ContextPtr ctx) +{ + int type = duk::require<int>(ctx, 0); + int delay = duk::require<int>(ctx, 1); + + if (!duk::is<duk::Function>(ctx, 2)) { + duk::raise(ctx, duk::TypeError("missing callback function")); + } + + auto timer = std::make_shared<Timer>(static_cast<TimerType>(type), delay); + + /* Add this timer to the underlying plugin */ + duk::getGlobal<duk::RawPointer<JsPlugin>>(ctx, "\xff""\xff""plugin")->addTimer(timer); + + /* Construct object */ + duk::construct(ctx, duk::Shared<Timer>{timer}); + + /* Now store the JavaScript function to call */ + duk::dup(ctx, 2); + duk::putGlobal(ctx, "\xff""\xff""timer-" + std::to_string(reinterpret_cast<std::intptr_t>(timer.get()))); + + return 0; +} + +const duk::Map<int> constants{ + { "Single", static_cast<int>(TimerType::Single) }, + { "Repeat", static_cast<int>(TimerType::Repeat) }, +}; + +} // !namespace + +TimerModule::TimerModule() noexcept + : Module("Irccd.Timer") +{ +} + +void TimerModule::load(Irccd &, JsPlugin &plugin) +{ + duk::StackAssert sa(plugin.context()); + + duk::getGlobal<void>(plugin.context(), "Irccd"); + duk::push(plugin.context(), duk::Function{constructor, 3}); + duk::push(plugin.context(), constants); + duk::push(plugin.context(), duk::Object{}); + duk::push(plugin.context(), methods); + duk::putProperty(plugin.context(), -2, "prototype"); + duk::putProperty(plugin.context(), -2, "Timer"); + duk::pop(plugin.context()); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-timer.hpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,49 @@ +/* + * mod-timer.hpp -- Irccd.Timer API + * + * Copyright (c) 2013-2016 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_MOD_TIMER_HPP +#define IRCCD_MOD_TIMER_HPP + +/** + * \file mod-timer.hpp + * \brief Irccd.Timer JavaScript API. + */ + +#include "module.hpp" + +namespace irccd { + +/** + * \brief Irccd.Timer JavaScript API. + */ +class TimerModule : public Module { +public: + /** + * Irccd.Timer. + */ + TimerModule() noexcept; + + /** + * \copydoc Module::load + */ + void load(Irccd &irccd, JsPlugin &plugin) override; +}; + +} // !irccd + +#endif // !IRCCD_MOD_TIMER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-unicode.cpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,151 @@ +/* + * js-unicode.cpp -- Irccd.Unicode API + * + * Copyright (c) 2013-2016 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.hpp" +#include "mod-unicode.hpp" +#include "plugin-js.hpp" +#include "unicode.hpp" + +namespace irccd { + +namespace { + +/* + * Function: Irccd.Unicode.isDigit(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is in the digit category. + */ +duk::Ret isDigit(duk::ContextPtr ctx) +{ + duk::push(ctx, unicode::isdigit(duk::get<int>(ctx, 0))); + + return 1; +} + +/* + * Function: Irccd.Unicode.isLetter(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is in the letter category. + */ +duk::Ret isLetter(duk::ContextPtr ctx) +{ + duk::push(ctx, unicode::isalpha(duk::get<int>(ctx, 0))); + + return 1; +} + +/* + * Function: Irccd.Unicode.isLower(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is lower case. + */ +duk::Ret isLower(duk::ContextPtr ctx) +{ + duk::push(ctx, unicode::islower(duk::get<int>(ctx, 0))); + + return 1; +} + +/* + * Function: Irccd.Unicode.isSpace(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is in the space category. + */ +duk::Ret isSpace(duk::ContextPtr ctx) +{ + duk::push(ctx, unicode::isspace(duk::get<int>(ctx, 0))); + + return 1; +} + +/* + * Function: Irccd.Unicode.isTitle(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is title case. + */ +duk::Ret isTitle(duk::ContextPtr ctx) +{ + duk::push(ctx, unicode::istitle(duk::get<int>(ctx, 0))); + + return 1; +} + +/* + * Function: Irccd.Unicode.isUpper(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is upper case. + */ +duk::Ret isUpper(duk::ContextPtr ctx) +{ + duk::push(ctx, unicode::isupper(duk::get<int>(ctx, 0))); + + return 1; +} + +const duk::FunctionMap functions{ + { "isDigit", { isDigit, 1 } }, + { "isLetter", { isLetter, 1 } }, + { "isLower", { isLower, 1 } }, + { "isSpace", { isSpace, 1 } }, + { "isTitle", { isTitle, 1 } }, + { "isUpper", { isUpper, 1 } }, +}; + +} // !namespace + +UnicodeModule::UnicodeModule() noexcept + : Module("Irccd.Unicode") +{ +} + +void UnicodeModule::load(Irccd &, JsPlugin &plugin) +{ + duk::StackAssert sa(plugin.context()); + + duk::getGlobal<void>(plugin.context(), "Irccd"); + duk::push(plugin.context(), duk::Object{}); + duk::push(plugin.context(), functions); + duk::putProperty(plugin.context(), -2, "Unicode"); + duk::pop(plugin.context()); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-unicode.hpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,49 @@ +/* + * mod-unicode.cpp -- Irccd.Unicode API + * + * Copyright (c) 2013-2016 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_MOD_UNICODE_HPP +#define IRCCD_MOD_UNICODE_HPP + +/** + * \file mod-unicode.hpp + * \brief Irccd.Unicode JavaScript API. + */ + +#include "module.hpp" + +namespace irccd { + +/** + * \brief Irccd.Unicode JavaScript API. + */ +class UnicodeModule : public Module { +public: + /** + * Irccd.Unicode. + */ + UnicodeModule() noexcept; + + /** + * \copydoc Module::load + */ + void load(Irccd &irccd, JsPlugin &plugin) override; +}; + +} // !irccd + +#endif // !IRCCD_MOD_UNICODE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-util.cpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,158 @@ +/* + * js-util.cpp -- Irccd.Util API + * + * Copyright (c) 2013-2016 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 <libircclient.h> + +#include "mod-util.hpp" +#include "plugin-js.hpp" +#include "util.hpp" + +namespace irccd { + +namespace duk { + +/** + * Read parameters for Irccd.Util.format function, the object is defined as follow: + * + * { + * 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: ... + * } + */ +template <> +class TypeTraits<util::Substitution> { +public: + static util::Substitution get(ContextPtr ctx, int index) + { + util::Substitution params; + + if (!duk::is<Object>(ctx, index)) { + return params; + } + + duk::enumerate(ctx, index, 0, true, [&] (ContextPtr) { + if (duk::get<std::string>(ctx, -2) == "date") { + params.time = static_cast<time_t>(duk::get<double>(ctx, -1) / 1000); + } else { + params.keywords.insert({duk::get<std::string>(ctx, -2), duk::get<std::string>(ctx, -1)}); + } + }); + + return params; + } +}; + +} // !duk + +namespace { + +/* + * Function: Irccd.Util.format(text, parameters) + * -------------------------------------------------------- + * + * Format a string with templates. + * + * Arguments: + * - input, the text to update, + * - params, the parameters. + * Returns: + * The converted text. + */ +duk::Ret format(duk::ContextPtr ctx) +{ + try { + duk::push(ctx, util::format(duk::get<std::string>(ctx, 0), duk::get<util::Substitution>(ctx, 1))); + } catch (const std::exception &ex) { + duk::raise(ctx, duk::SyntaxError(ex.what())); + } + + return 1; +} + +/* + * Function: Irccd.Util.splituser(ident) + * -------------------------------------------------------- + * + * Return the nickname part from a full username. + * + * Arguments: + * - ident, the full identity. + * Returns: + * The nickname. + */ +duk::Ret splituser(duk::ContextPtr ctx) +{ + const char *target = duk::require<const char *>(ctx, 0); + char nick[32] = {0}; + + irc_target_get_nick(target, nick, sizeof (nick) -1); + duk::push(ctx, std::string(nick)); + + return 1; +} + +/* + * Function: Irccd.Util.splithost(ident) + * -------------------------------------------------------- + * + * Return the hostname part from a full username. + * + * Arguments: + * - ident, the full identity. + * Returns: + * The hostname. + */ +duk::Ret splithost(duk::ContextPtr ctx) +{ + const char *target = duk::require<const char *>(ctx, 0); + char host[32] = {0}; + + irc_target_get_host(target, host, sizeof (host) -1); + duk::push(ctx, std::string(host)); + + return 1; +} + +const duk::FunctionMap functions{ + { "format", { format, DUK_VARARGS } }, + { "splituser", { splituser, 1 } }, + { "splithost", { splithost, 1 } } +}; + +} // !namespace + +UtilModule::UtilModule() noexcept + : Module("Irccd.Util") +{ +} + +void UtilModule::load(Irccd &, JsPlugin &plugin) +{ + duk::StackAssert sa(plugin.context()); + + duk::getGlobal<void>(plugin.context(), "Irccd"); + duk::push(plugin.context(), duk::Object{}); + duk::push(plugin.context(), functions); + duk::putProperty(plugin.context(), -2, "Util"); + duk::pop(plugin.context()); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/mod-util.hpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,49 @@ +/* + * mod-util.hpp -- Irccd.Util API + * + * Copyright (c) 2013-2016 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_MOD_UTIL_HPP +#define IRCCD_MOD_UTIL_HPP + +/** + * \file mod-util.hpp + * \brief Irccd.Util JavaScript API. + */ + +#include "module.hpp" + +namespace irccd { + +/** + * \brief Irccd.Util JavaScript API. + */ +class UtilModule : public Module { +public: + /** + * Irccd.Util. + */ + UtilModule() noexcept; + + /** + * \copydoc Module::load + */ + void load(Irccd &irccd, JsPlugin &plugin) override; +}; + +} // !irccd + +#endif // !IRCCD_MOD_UTIL_HPP
--- a/lib/irccd/module.hpp Fri May 13 12:06:06 2016 +0200 +++ b/lib/irccd/module.hpp Sun May 15 21:36:04 2016 +0200 @@ -1,24 +1,71 @@ +/* + * module.hpp -- JavaScript API module + * + * Copyright (c) 2013-2016 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_MODULE_HPP #define IRCCD_MODULE_HPP -#include <memory> +/** + * \file module.hpp + * \brief JavaScript API module. + */ + +#include <cassert> + +#include "util.hpp" namespace irccd { class Irccd; class JsPlugin; +/** + * \brief JavaScript API module. + */ class Module { +private: + std::string m_name; + public: /** * Default constructor. + * + * \pre !name.empty() */ - Module() = default; + inline Module(std::string name) noexcept + : m_name(std::move(name)) + { + assert(!m_name.empty()); + } /** * Virtual destructor defaulted. */ - virtual ~Module() = default + virtual ~Module() = default; + + /** + * Get the module name. + * + * \return the name + */ + inline const std::string &name() const noexcept + { + return m_name; + } /** * Load the module into the JavaScript plugin. @@ -26,7 +73,10 @@ * \param irccd the irccd instance * \param plugin the plugin */ - void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin); + virtual void load(Irccd &irccd, JsPlugin &plugin) + { + util::unused(irccd, plugin); + } /** * Unload the module from the JavaScript plugin. @@ -34,9 +84,12 @@ * \param irccd the irccd instance * \param plugin the plugin */ - void unload(Irccd &irccd, const std::shared_ptr<Plugin> &plugin); + virtual void unload(Irccd &irccd, JsPlugin &plugin) + { + util::unused(irccd, plugin); + } }; } // !irccd -#endif // !IRCCD_MODULE_HPP \ No newline at end of file +#endif // !IRCCD_MODULE_HPP
--- a/lib/irccd/plugin-js.cpp Fri May 13 12:06:06 2016 +0200 +++ b/lib/irccd/plugin-js.cpp Sun May 15 21:36:04 2016 +0200 @@ -25,19 +25,11 @@ #endif #include "fs.hpp" -#include "js-directory.hpp" -#include "js-elapsed-timer.hpp" -#include "js-file.hpp" -#include "js-irccd.hpp" -#include "js-logger.hpp" -#include "js-plugin.hpp" -#include "js-server.hpp" -#include "js-system.hpp" -#include "js-timer.hpp" -#include "js-unicode.hpp" -#include "js-util.hpp" +#include "irccd.hpp" #include "logger.hpp" +#include "mod-server.hpp" #include "plugin-js.hpp" +#include "service-module.hpp" #include "timer.hpp" namespace irccd { @@ -69,7 +61,6 @@ { duk::StackAssert sa(m_context); - /* Save a reference to this */ duk::putGlobal(m_context, "\xff""\xff""plugin", duk::RawPointer<JsPlugin>{this}); duk::putGlobal(m_context, "\xff""\xff""name", name()); duk::putGlobal(m_context, "\xff""\xff""path", path()); @@ -82,10 +73,8 @@ bool found = true; std::string foundpath; - /* - * Use the first existing directory available. - */ - for (const std::string &p : path::list(type)) { + // Use the first existing directory available. + for (const auto &p : path::list(type)) { foundpath = path::clean(p + append); if (fs::exists(foundpath)) { @@ -94,7 +83,7 @@ } } - /* Use the system as default */ + // Use the system as default. if (!found) { foundpath = path::clean(path::get(type, path::OwnerSystem) + append); } @@ -105,19 +94,6 @@ duk::pop(m_context, 2); } -void JsPlugin::putPaths() -{ - duk::StackAssert sa(m_context); - - /* - * dataPath: DATA + plugin/name (e.g ~/.local/share/irccd/plugins/<name>/) - * configPath: CONFIG + plugin/name (e.g ~/.config/irccd/plugin/<name>/) - */ - putPath("dataPath", "plugin/" + name(), path::PathData); - putPath("configPath", "plugin/" + name(), path::PathConfig); - putPath("cachePath", "plugin/" + name(), path::PathCache); -} - void JsPlugin::putConfig(const PluginConfig &config) { duk::StackAssert sa(m_context); @@ -145,64 +121,6 @@ JsPlugin::JsPlugin(std::string name, std::string path, const PluginConfig &config) : Plugin(name, path, config) { - duk::StackAssert sa(m_context); - - /* - * Duktape currently emit useless warnings when a file do - * not exists so we do a homemade access. - */ -#if defined(HAVE_STAT) - struct stat st; - - if (::stat(path.c_str(), &st) < 0) { - throw std::runtime_error(std::strerror(errno)); - } -#endif - - // TODO: change with future modules - // Load standard irccd API. - loadJsIrccd(m_context); - loadJsDirectory(m_context); - loadJsElapsedTimer(m_context); - loadJsFile(m_context); - loadJsLogger(m_context); - loadJsPlugin(m_context); - loadJsServer(m_context); - loadJsSystem(m_context); - loadJsUnicode(m_context); - loadJsTimer(m_context); - loadJsUtil(m_context); - - putVars(); - putPaths(); - - /* Try to load the file (does not call onLoad yet) */ - if (duk::pevalFile(m_context, path) != 0) { - throw duk::error(m_context, -1); - } - - duk::pop(m_context); - - /* Initialize user defined options after loading to allow the plugin to define default values */ - putConfig(config); - - /* Read metadata */ - duk::getGlobal<void>(m_context, "info"); - - if (duk::type(m_context, -1) == DUK_TYPE_OBJECT) { - setAuthor(duk::optionalProperty<std::string>(m_context, -1, "author", author())); - setLicense(duk::optionalProperty<std::string>(m_context, -1, "license", license())); - setSummary(duk::optionalProperty<std::string>(m_context, -1, "summary", summary())); - setVersion(duk::optionalProperty<std::string>(m_context, -1, "version", version())); - } - - duk::pop(m_context); - - log::debug() << "plugin " << name << ": " << std::endl; - log::debug() << " author: " << author() << std::endl; - log::debug() << " license: " << license() << std::endl; - log::debug() << " summary: " << summary() << std::endl; - log::debug() << " version: " << version() << std::endl; } JsPlugin::~JsPlugin() @@ -342,10 +260,60 @@ call("onKick", 5); } -void JsPlugin::onLoad(Irccd &) +void JsPlugin::putModules(Irccd &irccd) +{ + for (const auto &module : irccd.moduleService().modules()) { + module->load(irccd, *this); + } +} + +void JsPlugin::onLoad(Irccd &irccd) { duk::StackAssert sa(m_context); + /* + * Duktape currently emit useless warnings when a file do + * not exists so we do a homemade access. + */ +#if defined(HAVE_STAT) + struct stat st; + + if (::stat(path().c_str(), &st) < 0) { + throw std::runtime_error(std::strerror(errno)); + } +#endif + + /* + * dataPath: DATA + plugin/name (e.g ~/.local/share/irccd/plugins/<name>/) + * configPath: CONFIG + plugin/name (e.g ~/.config/irccd/plugin/<name>/) + */ + putModules(irccd); + putVars(); + putPath("dataPath", "plugin/" + name(), path::PathData); + putPath("configPath", "plugin/" + name(), path::PathConfig); + putPath("cachePath", "plugin/" + name(), path::PathCache); + + /* Try to load the file (does not call onLoad yet) */ + if (duk::pevalFile(m_context, path()) != 0) { + throw duk::error(m_context, -1); + } + + duk::pop(m_context); + + putConfig(config()); + + /* Read metadata */ + duk::getGlobal<void>(m_context, "info"); + + if (duk::type(m_context, -1) == DUK_TYPE_OBJECT) { + setAuthor(duk::optionalProperty<std::string>(m_context, -1, "author", author())); + setLicense(duk::optionalProperty<std::string>(m_context, -1, "license", license())); + setSummary(duk::optionalProperty<std::string>(m_context, -1, "summary", summary())); + setVersion(duk::optionalProperty<std::string>(m_context, -1, "version", version())); + } + + duk::pop(m_context); + call("onLoad", 0); }
--- a/lib/irccd/plugin-js.hpp Fri May 13 12:06:06 2016 +0200 +++ b/lib/irccd/plugin-js.hpp Sun May 15 21:36:04 2016 +0200 @@ -77,11 +77,14 @@ // Plugin info and its timers PluginTimers m_timers; + // Store loaded modules. + std::vector<std::shared_ptr<Module>> m_modules; + // Private helpers void call(const std::string &name, unsigned nargs = 0); + void putModules(Irccd &irccd); void putVars(); void putPath(const std::string &varname, const std::string &append, path::Path type); - void putPaths(); void putConfig(const PluginConfig &config); public:
--- a/lib/irccd/plugin.hpp Fri May 13 12:06:06 2016 +0200 +++ b/lib/irccd/plugin.hpp Sun May 15 21:36:04 2016 +0200 @@ -185,6 +185,16 @@ m_version = std::move(version); } + inline const PluginConfig &config() const noexcept + { + return m_config; + } + + inline PluginConfig &config() noexcept + { + return m_config; + } + /** * On channel message. This event will call onMessage or * onCommand if the messages starts with the command character
--- a/lib/irccd/server-state-disconnected.cpp Fri May 13 12:06:06 2016 +0200 +++ b/lib/irccd/server-state-disconnected.cpp Sun May 15 21:36:04 2016 +0200 @@ -27,7 +27,6 @@ void Disconnected::prepare(Server &server, fd_set &, fd_set &, net::Handle &) { - const ServerInfo &info = server.info(); ServerSettings &settings = server.settings(); ServerCache &cache = server.cache();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/service-module.cpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,77 @@ +/* + * service-module.cpp -- store and manage JavaScript modules + * + * Copyright (c) 2013-2016 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 "mod-elapsed-timer.hpp" +#include "mod-directory.hpp" +#include "mod-file.hpp" +#include "mod-irccd.hpp" +#include "mod-logger.hpp" +#include "mod-plugin.hpp" +#include "mod-server.hpp" +#include "mod-system.hpp" +#include "mod-timer.hpp" +#include "mod-unicode.hpp" +#include "mod-util.hpp" +#include "service-module.hpp" + +namespace irccd { + +namespace { + +auto find(const std::vector<std::shared_ptr<Module>> &modules, const std::string &name) noexcept +{ + return std::find_if(modules.begin(), modules.end(), [&] (const auto &module) { + return module->name() == name; + }); +} + +} // !namespace + +ModuleService::ModuleService() +{ + // Load Irccd global first. + m_modules.push_back(std::make_shared<IrccdModule>()); + + // Additional modules. + m_modules.push_back(std::make_shared<ElapsedTimerModule>()); + m_modules.push_back(std::make_shared<DirectoryModule>()); + m_modules.push_back(std::make_shared<FileModule>()); + m_modules.push_back(std::make_shared<LoggerModule>()); + m_modules.push_back(std::make_shared<PluginModule>()); + m_modules.push_back(std::make_shared<ServerModule>()); + m_modules.push_back(std::make_shared<SystemModule>()); + m_modules.push_back(std::make_shared<TimerModule>()); + m_modules.push_back(std::make_shared<UnicodeModule>()); + m_modules.push_back(std::make_shared<UtilModule>()); +} + +bool ModuleService::contains(const std::string &name) const +{ + return find(m_modules, name) != m_modules.end(); +} + +void ModuleService::add(std::shared_ptr<Module> module) +{ + assert(!contains(module->name())); + + m_modules.push_back(std::move(module)); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/service-module.hpp Sun May 15 21:36:04 2016 +0200 @@ -0,0 +1,76 @@ +/* + * service-module.hpp -- store and manage JavaScript modules + * + * Copyright (c) 2013-2016 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_SERVICE_MODULE_HPP +#define IRCCD_SERVICE_MODULE_HPP + +/** + * \file service-module.hpp + * \brief Store and manage JavaScript modules. + */ + +#include <memory> +#include <vector> + +namespace irccd { + +class Module; + +/** + * \brief Store and manage JavaScript modules. + */ +class ModuleService { +private: + std::vector<std::shared_ptr<Module>> m_modules; + +public: + /** + * Construct the service and predefined irccd API. + */ + ModuleService(); + + /** + * Get all modules. + * + * \return the modules + */ + inline const std::vector<std::shared_ptr<Module>> &modules() const noexcept + { + return m_modules; + } + + /** + * Tells if a module exist. + * + * \param name the name + */ + bool contains(const std::string &name) const; + + /** + * Add a JavaScript API module. + * + * \pre module != nullptr + * \pre !contains(module) + * \param module the module + */ + void add(std::shared_ptr<Module> module); +}; + +} // !irccd + +#endif // !IRCCD_SERVICE_MODULE_HPP
--- a/lib/irccd/service-plugin.cpp Fri May 13 12:06:06 2016 +0200 +++ b/lib/irccd/service-plugin.cpp Sun May 15 21:36:04 2016 +0200 @@ -39,13 +39,6 @@ { } -void PluginService::addModule(std::shared_ptr<Module> module) -{ - assert(module); - - m_modules.push_back(std::move(module)); -} - bool PluginService::has(const std::string &name) const noexcept { return std::count_if(m_plugins.cbegin(), m_plugins.cend(), [&] (const auto &plugin) { @@ -93,19 +86,12 @@ // Store reference to irccd. duk::putGlobal(jsp->context(), "\xff""\xff""irccd", duk::RawPointer<Irccd>{&m_irccd}); } - - // Initial load now. - // TODO: not responsible of this. - try { - plugin->onLoad(m_irccd); - m_plugins.push_back(std::move(plugin)); - } catch (const std::exception &ex) { - log::warning("plugin {}: {}"_format(plugin->name(), ex.what())); - } } -std::shared_ptr<Plugin> PluginService::find(std::string name, PluginConfig config) +std::shared_ptr<Plugin> PluginService::find(std::string name) { + PluginConfig config = m_config[name]; + for (const auto &path : path::list(path::PathPlugins)) { std::string fullpath = path + name + ".js"; @@ -121,6 +107,22 @@ throw std::runtime_error("no suitable plugin found"); } +void PluginService::configure(const std::string &name, PluginConfig config) +{ + m_config.emplace(name, std::move(config)); +} + +PluginConfig PluginService::config(const std::string &name) const +{ + auto it = m_config.find(name); + + if (it != m_config.end()) { + return it->second; + } + + return PluginConfig(); +} + void PluginService::load(std::string name, std::string path) { auto it = std::find_if(m_plugins.begin(), m_plugins.end(), [&] (const auto &plugin) { @@ -128,19 +130,21 @@ }); if (it != m_plugins.end()) { - throw std::invalid_argument("plugin already loaded"); + return; } - // TODO: LOAD CONFIG - std::shared_ptr<Plugin> plugin; + try { + std::shared_ptr<Plugin> plugin; - try { if (path.empty()) { - plugin = find(std::move(name), PluginConfig()); + plugin = find(std::move(name)); } else { // TODO: DYNLIB BASED PLUGINS plugin = std::make_shared<JsPlugin>(std::move(name), std::move(path)); } + + plugin->onLoad(m_irccd); + add(std::move(plugin)); } catch (const std::exception &ex) { log::warning("plugin {}: {}"_format(ex.what())); }
--- a/lib/irccd/service-plugin.hpp Fri May 13 12:06:06 2016 +0200 +++ b/lib/irccd/service-plugin.hpp Sun May 15 21:36:04 2016 +0200 @@ -24,6 +24,7 @@ * \brief Manage plugins. */ +#include <unordered_map> #include <memory> #include <vector> @@ -39,8 +40,8 @@ class PluginService { private: Irccd &m_irccd; - std::vector<std::shared_ptr<Module>> m_modules; std::vector<std::shared_ptr<Plugin>> m_plugins; + std::unordered_map<std::string, PluginConfig> m_config; // TODO: get rid of this with future JavaScript modules. void handleTimerSignal(std::weak_ptr<JsPlugin>, std::shared_ptr<Timer>); @@ -65,14 +66,6 @@ } /** - * Add an API module for JavaScript plugins. - * - * \pre module is not null - * \param module the module - */ - void addModule(std::shared_ptr<Module> module); - - /** * Check if a plugin is loaded. * * \param name the plugin id @@ -97,10 +90,50 @@ */ std::shared_ptr<Plugin> require(const std::string &name) const; + /** + * Add the specified plugin to the registry. + * + * \pre plugin != nullptr + * \param plugin the plugin + * \note the plugin is only added to the list, no action is performed on it + */ void add(std::shared_ptr<Plugin> plugin); - std::shared_ptr<Plugin> find(std::string name, PluginConfig config = PluginConfig()); + /** + * Search a plugin through the predefined directories. + * + * \param name the plugin name (without any extension) + * \return the plugin if found + * \throw std::exception on any errors + */ + std::shared_ptr<Plugin> find(std::string name); + /** + * Configure a plugin. + * + * If the plugin is already loaded, its configuration is updated. + * + * \param name the plugin name + * \param config the new configuration + */ + void configure(const std::string &name, PluginConfig config); + + /** + * Get a configuration for a plugin. + * + * \param name the plugin name + * \return the configuration or default one if not found + */ + PluginConfig config(const std::string &name) const; + + /** + * Convenient wrapper that loads a plugin, call onLoad and add it to the registry. + * + * Any errors are printed using logger. + * + * \param name the name + * \param path the optional path (searched if empty) + */ void load(std::string name, std::string path = ""); /**
--- a/tests/js-elapsedtimer/main.cpp Fri May 13 12:06:06 2016 +0200 +++ b/tests/js-elapsedtimer/main.cpp Sun May 15 21:36:04 2016 +0200 @@ -21,7 +21,7 @@ #include <thread> #include <irccd/js-irccd.hpp> -#include <irccd/js-elapsed-timer.hpp> +#include <irccd/mod-elapsed-timer.hpp> using namespace irccd; using namespace std::chrono_literals;