Mercurial > irccd
changeset 75:f8160d515a76
Irccd: rework a lot the JavaScript library
line wrap: on
line diff
--- a/cmake/IrccdOptions.cmake Sat Mar 26 14:41:53 2016 +0100 +++ b/cmake/IrccdOptions.cmake Wed Mar 30 13:52:47 2016 +0200 @@ -109,6 +109,7 @@ set(WITH_BINDIR "bin" CACHE STRING "Binary directory") set(WITH_MANDIR "share/man" CACHE STRING "Man directory") set(WITH_CONFDIR "etc" CACHE STRING "Configuration directory") +set(WITH_CMAKEDIR "lib/cmake" CACHE STRING "Directory for CMake modules") set(WITH_PKGCONFIGDIR "lib/pkgconfig" CACHE STRING "Directory for pkg-config file") #
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cmake/internal/Irccd2Config.cmake.in Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/Irccd2Targets.cmake")
--- a/lib/CMakeLists.txt Sat Mar 26 14:41:53 2016 +0100 +++ b/lib/CMakeLists.txt Wed Mar 30 13:52:47 2016 +0200 @@ -25,9 +25,12 @@ target_include_directories( libirccd PUBLIC - ${irccd_BINARY_DIR} + $<BUILD_INTERFACE:${duktape_SOURCE_DIR}> + $<BUILD_INTERFACE:${extern-libircclient_SOURCE_DIR}/include> + $<BUILD_INTERFACE:${irccd_BINARY_DIR}> + $<BUILD_INTERFACE:${lib_SOURCE_DIR}> + $<INSTALL_INTERFACE:include> ${OPENSSL_INCLUDE_DIR} - ${lib_SOURCE_DIR} ) if (IRCCD_SYSTEM_WINDOWS) @@ -36,7 +39,8 @@ list(APPEND LIBRARIES resolv) endif () -target_link_libraries(libirccd extern-duktape extern-ircclient extern-jansson ${LIBRARIES}) +target_link_libraries(libirccd PRIVATE extern-duktape extern-ircclient extern-jansson) +target_link_libraries(libirccd ${LIBRARIES}) set_target_properties( libirccd @@ -49,11 +53,50 @@ install( TARGETS libirccd + EXPORT Irccd2Targets RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib ) +# CMake target exports. +export( + EXPORT Irccd2Targets + FILE "${CMAKE_BINARY_DIR}/Irccd2Targets.cmake" + NAMESPACE Irccd2:: +) + +# CMake version file. +include(CMakePackageConfigHelpers) + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/Irccd2ConfigVersion.cmake" + VERSION ${IRCCD_VERSION} + COMPATIBILITY AnyNewerVersion +) + +# Irccd main module file. +configure_file( + "${CMAKE_SOURCE_DIR}/cmake/internal/Irccd2Config.cmake.in" + "${CMAKE_BINARY_DIR}/Irccd2Config.cmake" + COPYONLY +) + +# Install all CMake stuff. +install( + EXPORT Irccd2Targets + FILE Irccd2Targets.cmake + NAMESPACE Irccd2:: + DESTINATION ${WITH_CMAKEDIR}/Irccd2 +) + +install( + FILES + "${CMAKE_BINARY_DIR}/Irccd2Config.cmake" + "${CMAKE_BINARY_DIR}/Irccd2ConfigVersion.cmake" + DESTINATION ${WITH_CMAKEDIR}/Irccd2 +) + # pkg-config if (WITH_PKGCONFIG) # Configure the pkg-config file.
--- a/lib/irccd/CMakeSources.cmake Sat Mar 26 14:41:53 2016 +0100 +++ b/lib/irccd/CMakeSources.cmake Wed Mar 30 13:52:47 2016 +0200 @@ -1,7 +1,3 @@ -include(${CMAKE_CURRENT_LIST_DIR}/command/CMakeSources.cmake) -include(${CMAKE_CURRENT_LIST_DIR}/js/CMakeSources.cmake) -include(${CMAKE_CURRENT_LIST_DIR}/private/CMakeSources.cmake) - set( HEADERS ${COMMAND_HEADERS} @@ -9,10 +5,52 @@ ${PRIVATE_HEADERS} ${CMAKE_CURRENT_LIST_DIR}/alias.h ${CMAKE_CURRENT_LIST_DIR}/application.h + ${CMAKE_CURRENT_LIST_DIR}/connection.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-help.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-info.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-list.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-load.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-reload.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-unload.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-cmode.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-cnotice.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-connect.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-disconnect.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-info.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-invite.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-join.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-kick.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-list.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-me.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-message.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-mode.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-nick.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-notice.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-part.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-reconnect.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-topic.h + ${CMAKE_CURRENT_LIST_DIR}/cmd-watch.h + ${CMAKE_CURRENT_LIST_DIR}/command.h ${CMAKE_CURRENT_LIST_DIR}/config.h + ${CMAKE_CURRENT_LIST_DIR}/directory.h + ${CMAKE_CURRENT_LIST_DIR}/elapsed-timer.h + ${CMAKE_CURRENT_LIST_DIR}/filesystem.h + ${CMAKE_CURRENT_LIST_DIR}/ini.h ${CMAKE_CURRENT_LIST_DIR}/irccd.h ${CMAKE_CURRENT_LIST_DIR}/irccdctl.h + ${CMAKE_CURRENT_LIST_DIR}/js.h ${CMAKE_CURRENT_LIST_DIR}/json.h + ${CMAKE_CURRENT_LIST_DIR}/js-directory.h + ${CMAKE_CURRENT_LIST_DIR}/js-elapsed-timer.h + ${CMAKE_CURRENT_LIST_DIR}/js-file.h + ${CMAKE_CURRENT_LIST_DIR}/js-irccd.h + ${CMAKE_CURRENT_LIST_DIR}/js-logger.h + ${CMAKE_CURRENT_LIST_DIR}/js-plugin.h + ${CMAKE_CURRENT_LIST_DIR}/js-server.h + ${CMAKE_CURRENT_LIST_DIR}/js-system.h + ${CMAKE_CURRENT_LIST_DIR}/js-timer.h + ${CMAKE_CURRENT_LIST_DIR}/js-unicode.h + ${CMAKE_CURRENT_LIST_DIR}/js-util.h ${CMAKE_CURRENT_LIST_DIR}/logger.h ${CMAKE_CURRENT_LIST_DIR}/options.h ${CMAKE_CURRENT_LIST_DIR}/path.h @@ -20,6 +58,7 @@ ${CMAKE_CURRENT_LIST_DIR}/rule.h ${CMAKE_CURRENT_LIST_DIR}/server.h ${CMAKE_CURRENT_LIST_DIR}/server-state.h + ${CMAKE_CURRENT_LIST_DIR}/sockets.h ${CMAKE_CURRENT_LIST_DIR}/system.h ${CMAKE_CURRENT_LIST_DIR}/timer.h ${CMAKE_CURRENT_LIST_DIR}/transport-client.h @@ -30,15 +69,53 @@ set( SOURCES - ${COMMAND_SOURCES} - ${JS_SOURCES} - ${PRIVATE_SOURCES} ${CMAKE_CURRENT_LIST_DIR}/alias.cpp ${CMAKE_CURRENT_LIST_DIR}/application.cpp + ${CMAKE_CURRENT_LIST_DIR}/connection.cpp ${CMAKE_CURRENT_LIST_DIR}/config.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-help.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-info.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-list.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-load.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-reload.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-unload.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-cmode.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-cnotice.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-connect.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-disconnect.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-info.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-invite.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-join.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-kick.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-list.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-me.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-message.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-mode.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-nick.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-notice.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-part.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-reconnect.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-server-topic.cpp + ${CMAKE_CURRENT_LIST_DIR}/cmd-watch.cpp + ${CMAKE_CURRENT_LIST_DIR}/command.cpp + ${CMAKE_CURRENT_LIST_DIR}/directory.cpp + ${CMAKE_CURRENT_LIST_DIR}/elapsed-timer.cpp + ${CMAKE_CURRENT_LIST_DIR}/filesystem.cpp + ${CMAKE_CURRENT_LIST_DIR}/ini.cpp ${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}/logger.cpp ${CMAKE_CURRENT_LIST_DIR}/options.cpp ${CMAKE_CURRENT_LIST_DIR}/path.cpp @@ -46,6 +123,7 @@ ${CMAKE_CURRENT_LIST_DIR}/rule.cpp ${CMAKE_CURRENT_LIST_DIR}/server.cpp ${CMAKE_CURRENT_LIST_DIR}/server-state.cpp + ${CMAKE_CURRENT_LIST_DIR}/sockets.cpp ${CMAKE_CURRENT_LIST_DIR}/system.cpp ${CMAKE_CURRENT_LIST_DIR}/timer.cpp ${CMAKE_CURRENT_LIST_DIR}/transport-client.cpp @@ -53,3 +131,8 @@ ${CMAKE_CURRENT_LIST_DIR}/unicode.cpp ${CMAKE_CURRENT_LIST_DIR}/util.cpp ) + +if (NOT IRCCD_SYSTEM_WINDOWS) + list(APPEND HEADERS ${CMAKE_CURRENT_LIST_DIR}/xdg.h) + list(APPEND SOURCES ${CMAKE_CURRENT_LIST_DIR}/xdg.cpp) +endif ()
--- a/lib/irccd/application.cpp Sat Mar 26 14:41:53 2016 +0100 +++ b/lib/irccd/application.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -17,31 +17,30 @@ */ #include "application.h" - -#include "command/help.h" -#include "command/plugin-info.h" -#include "command/plugin-list.h" -#include "command/plugin-load.h" -#include "command/plugin-reload.h" -#include "command/plugin-unload.h" -#include "command/server-cmode.h" -#include "command/server-cnotice.h" -#include "command/server-connect.h" -#include "command/server-disconnect.h" -#include "command/server-info.h" -#include "command/server-invite.h" -#include "command/server-join.h" -#include "command/server-kick.h" -#include "command/server-list.h" -#include "command/server-me.h" -#include "command/server-message.h" -#include "command/server-mode.h" -#include "command/server-nick.h" -#include "command/server-notice.h" -#include "command/server-part.h" -#include "command/server-reconnect.h" -#include "command/server-topic.h" -#include "command/watch.h" +#include "cmd-help.h" +#include "cmd-plugin-info.h" +#include "cmd-plugin-list.h" +#include "cmd-plugin-load.h" +#include "cmd-plugin-reload.h" +#include "cmd-plugin-unload.h" +#include "cmd-server-cmode.h" +#include "cmd-server-cnotice.h" +#include "cmd-server-connect.h" +#include "cmd-server-disconnect.h" +#include "cmd-server-info.h" +#include "cmd-server-invite.h" +#include "cmd-server-join.h" +#include "cmd-server-kick.h" +#include "cmd-server-list.h" +#include "cmd-server-me.h" +#include "cmd-server-message.h" +#include "cmd-server-mode.h" +#include "cmd-server-nick.h" +#include "cmd-server-notice.h" +#include "cmd-server-part.h" +#include "cmd-server-reconnect.h" +#include "cmd-server-topic.h" +#include "cmd-watch.h" namespace irccd {
--- a/lib/irccd/application.h Sat Mar 26 14:41:53 2016 +0100 +++ b/lib/irccd/application.h Wed Mar 30 13:52:47 2016 +0200 @@ -23,7 +23,7 @@ #include <memory> #include <unordered_map> -#include "command/command.h" +#include "command.h" namespace irccd {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-help.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,57 @@ +/* + * command-help.cpp -- implementation of irccdctl help + * + * 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/irccdctl.h> +#include <irccd/logger.h> + +#include "cmd-help.h" + +namespace irccd { + +namespace command { + +Help::Help() + : RemoteCommand("help", "General") +{ +} + +std::vector<RemoteCommand::Arg> Help::args() const +{ + return {{ "command", true }}; +} + +std::string Help::help() const +{ + return "Get help about a command."; +} + +json::Value Help::request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const +{ + auto it = irccdctl.commands().find(args.arg(0U)); + + if (it == irccdctl.commands().end()) + log::warning() << "there is no command named: " << args.arg(0U) << std::endl; + else + log::warning() << it->second->usage() << std::flush; + + return nullptr; +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-help.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,58 @@ +/* + * command-help.h -- implementation of irccdctl help + * + * 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 _IRCCDCTL_COMMAND_HELP_H_ +#define _IRCCDCTL_COMMAND_HELP_H_ + +/** + * @file command-help.h + * @brief Implementation of irccdctl help. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class Help + * @brief Implementation of irccdctl help. + */ +class Help : public RemoteCommand { +public: + Help(); + + std::vector<Arg> args() const override; + + /** + * @copydoc RemoteCommand::help + */ + std::string help() const override; + + /** + * @copydoc RemoteCommand::request + */ + json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCDCTL_COMMAND_HELP_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-plugin-info.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,86 @@ +/* + * plugin-info.cpp -- implementation of plugin-info command + * + * 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 <iostream> + +#include <irccd-config.h> + +#include "cmd-plugin-info.h" +#include "irccd.h" + +namespace irccd { + +namespace command { + +PluginInfo::PluginInfo() + : RemoteCommand("plugin-info", "Plugins") +{ +} + +std::string PluginInfo::help() const +{ + return "Get plugin information."; +} + +std::vector<RemoteCommand::Arg> PluginInfo::args() const +{ + return {{ "plugin", true }}; +} + +json::Value PluginInfo::request(Irccdctl &, const RemoteCommandRequest &args) const +{ + return json::object({{ "plugin", args.arg(0) }}); +} + +json::Value PluginInfo::exec(Irccd &irccd, const json::Value &request) const +{ +#if defined(WITH_JS) + auto plugin = irccd.requirePlugin(request.at("plugin").toString()); + + return json::object({ + { "author", plugin->info().author }, + { "license", plugin->info().license }, + { "summary", plugin->info().summary }, + { "version", plugin->info().version } + }); +#else + (void)irccd; + (void)object; + + throw std::runtime_error("JavaScript disabled"); +#endif +} + +void PluginInfo::result(Irccdctl &irccdctl, const json::Value &result) const +{ + RemoteCommand::result(irccdctl, result); + + /* Plugin information */ + if (result.valueOr("status", false).toBool()) { + std::cout << std::boolalpha; + std::cout << "Author : " << result.valueOr("author", "").toString(true) << std::endl; + std::cout << "License : " << result.valueOr("license", "").toString(true) << std::endl; + std::cout << "Summary : " << result.valueOr("summary", "").toString(true) << std::endl; + std::cout << "Version : " << result.valueOr("version", "").toString(true) << std::endl; + } +} + +} // !command + +} // !irccd +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-plugin-info.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,74 @@ +/* + * plugin-info.h -- implementation of plugin-info command + * + * 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_PLUGIN_INFO_H_ +#define _IRCCD_PLUGIN_INFO_H_ + +/** + * @file plugin-info.h + * @brief Implementation of plugin-info transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class PluginInfo + * @brief Implementation of plugin-info transport command. + */ +class PluginInfo : public RemoteCommand { +public: + /** + * Constructor. + */ + PluginInfo(); + + /** + * @copydoc RemoteCommand::help + */ + std::string help() const override; + + /** + * @copydoc RemoteCommand::args + */ + std::vector<Arg> args() const override; + + /** + * @copydoc RemoteCommand::request + */ + json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; + + /** + * @copydoc RemoteCommand::exec + */ + json::Value exec(Irccd &irccd, const json::Value &request) const override; + + /** + * @copydoc RemoteCommand::result + */ + void result(Irccdctl &irccdctl, const json::Value &object) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_PLUGIN_INFO_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-plugin-list.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,70 @@ +/* + * command-plugin-list.cpp -- implementation of plugin-list transport command + * + * 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 <iostream> + +#include <irccd-config.h> + +#include "cmd-plugin-list.h" +#include "irccd.h" + +namespace irccd { + +namespace command { + +PluginList::PluginList() + : RemoteCommand("plugin-list", "Plugins") +{ +} + +std::string PluginList::help() const +{ + return "Get the list of loaded plugins."; +} + +json::Value PluginList::exec(Irccd &irccd, const json::Value &request) const +{ +#if defined(WITH_JS) + json::Value response = RemoteCommand::exec(irccd, request); + json::Value list = json::array({}); + + for (const auto &plugin : irccd.plugins()) + list.append(plugin.first); + + response.insert("list", std::move(list)); + + return response; +#else + (void)irccd; + (void)tc; + + throw std::runtime_error("JavaScript disabled"); +#endif +} + +void PluginList::result(Irccdctl &irccdctl, const json::Value &object) const +{ + RemoteCommand::result(irccdctl, object); + + for (const auto &n : object.valueOr("list", json::Type::Array, json::array({}))) + std::cout << n.toString() << std::endl; +} + +} // !command + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-plugin-list.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,64 @@ +/* + * command-plugin-list.h -- implementation of plugin-list transport command + * + * 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_COMMAND_PLUGIN_LIST_H_ +#define _IRCCD_COMMAND_PLUGIN_LIST_H_ + +/** + * @file command-plugin-list.h + * @brief Implementation of plugin-list transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class PluginList + * @brief Implementation of plugin-list transport command. + */ +class PluginList : public RemoteCommand { +public: + /** + * Constructor. + */ + PluginList(); + + /** + * @copydoc RemoteCommand::help + */ + std::string help() const override; + + /** + * @copydoc RemoteCommand::exec + */ + json::Value exec(Irccd &irccd, const json::Value &request) const override; + + /** + * @copydoc RemoteCommand::result + */ + void result(Irccdctl &irccdctl, const json::Value &object) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_COMMAND_PLUGIN_LIST_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-plugin-load.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,62 @@ +/* + * command-plugin-load.cpp -- implementation of plugin-load transport command + * + * 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-config.h> + +#include "cmd-plugin-load.h" +#include "irccd.h" + +namespace irccd { + +namespace command { + +PluginLoad::PluginLoad() + : RemoteCommand("plugin-load", "Plugins") +{ +} + +std::string PluginLoad::help() const +{ + return "Load a plugin."; +} + +std::vector<RemoteCommand::Arg> PluginLoad::args() const +{ + return {{ "plugin", true }}; +} + +json::Value PluginLoad::exec(Irccd &irccd, const json::Value &request) const +{ +#if defined(WITH_JS) + auto name = request.at("plugin").toString(); + + irccd.loadPlugin(name, name, true); + + return RemoteCommand::exec(irccd, request); +#else + (void)irccd; + (void)tc; + (void)object; + + throw std::runtime_error("JavaScript disabled"); +#endif +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-plugin-load.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,64 @@ +/* + * command-plugin-load.h -- implementation of plugin-load transport command + * + * 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_COMMAND_PLUGIN_LOAD_H_ +#define _IRCCD_COMMAND_PLUGIN_LOAD_H_ + +/** + * @file plugin-load.h + * @brief Implementation of plugin-load transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class PluginLoad + * @brief Implementation of plugin-load transport command. + */ +class PluginLoad : public RemoteCommand { +public: + /** + * Constructor. + */ + PluginLoad(); + + /** + * @copydoc TransportCommand::help + */ + std::string help() const override; + + /** + * @copydoc TransportCommand::args + */ + std::vector<Arg> args() const override; + + /** + * @copydoc TransportCommand::exec + */ + json::Value exec(Irccd &irccd, const json::Value &object) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_COMMAND_PLUGIN_LOAD_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-plugin-reload.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,60 @@ +/* + * command-plugin-reload.cpp -- implementation of plugin-reload transport command + * + * 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-config.h> + +#include "cmd-plugin-reload.h" +#include "irccd.h" + +namespace irccd { + +namespace command { + +PluginReload::PluginReload() + : RemoteCommand("plugin-reload", "Plugins") +{ +} + +std::string PluginReload::help() const +{ + return "Reload a plugin."; +} + +std::vector<RemoteCommand::Arg> PluginReload::args() const +{ + return {{ "plugin", true }}; +} + +json::Value PluginReload::exec(Irccd &irccd, const json::Value &request) const +{ +#if defined(WITH_JS) + irccd.requirePlugin(request.at("plugin").toString())->onReload(); + + return RemoteCommand::exec(irccd, request); +#else + (void)irccd; + (void)tc; + (void)object; + + throw std::runtime_error("JavaScript disabled"); +#endif +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-plugin-reload.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,64 @@ +/* + * plugin-reload.h -- implementation of plugin-reload transport command + * + * 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_COMMAND_PLUGIN_RELOAD_H_ +#define _IRCCD_COMMAND_PLUGIN_RELOAD_H_ + +/** + * @file command-plugin-load.h + * @brief Implementation of plugin-reload transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class PluginReload + * @brief Implementation of plugin-reload transport command. + */ +class PluginReload : public RemoteCommand { +public: + /** + * Constructor. + */ + PluginReload(); + + /** + * @copydoc TransportCommand::help + */ + std::string help() const override; + + /** + * @copydoc TransportCommand::args + */ + std::vector<Arg> args() const override; + + /** + * @copydoc TransportCommand::exec + */ + json::Value exec(Irccd &irccd, const json::Value &object) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_COMMAND_PLUGIN_RELOAD_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-plugin-unload.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,60 @@ +/* + * plugin-unload.cpp -- implementation of plugin-unload transport command + * + * 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-config.h> + +#include "cmd-plugin-unload.h" +#include "irccd.h" + +namespace irccd { + +namespace command { + +PluginUnload::PluginUnload() + : RemoteCommand("plugin-unload", "Plugins") +{ +} + +std::string PluginUnload::help() const +{ + return "Unload a plugin."; +} + +std::vector<RemoteCommand::Arg> PluginUnload::args() const +{ + return {{ "plugin", true }}; +} + +json::Value PluginUnload::exec(Irccd &irccd, const json::Value &request) const +{ +#if defined(WITH_JS) + irccd.unloadPlugin(request.at("plugin").toString()); + + return RemoteCommand::exec(irccd, request); +#else + (void)irccd; + (void)tc; + (void)object; + + throw std::runtime_error("JavaScript disabled"); +#endif +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-plugin-unload.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,64 @@ +/* + * plugin-unload.h -- implementation of plugin-unload transport command + * + * 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_COMMAND_PLUGIN_UNLOAD_H_ +#define _IRCCD_COMMAND_PLUGIN_UNLOAD_H_ + +/** + * @file plugin-unload.h + * @brief Implementation of plugin-unload transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class PluginUnload + * @brief Implementation of plugin-unload transport command. + */ +class PluginUnload : public RemoteCommand { +public: + /** + * Constructor. + */ + PluginUnload(); + + /** + * @copydoc TransportCommand::help + */ + std::string help() const override; + + /** + * @copydoc TransportCommand::args + */ + std::vector<Arg> args() const override; + + /** + * @copydoc RemoteCommand::exec + */ + json::Value exec(Irccd &irccd, const json::Value &object) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_COMMAND_PLUGIN_UNLOAD_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-cmode.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,58 @@ +/* + * server-cmode.cpp -- implementation of server-cmode transport command + * + * 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 "cmd-server-cmode.h" +#include "irccd.h" + +namespace irccd { + +namespace command { + +ServerChannelMode::ServerChannelMode() + : RemoteCommand("server-cmode", "Server") +{ +} + +std::string ServerChannelMode::help() const +{ + return ""; +} + +std::vector<RemoteCommand::Arg> ServerChannelMode::args() const +{ + return { + { "server", true }, + { "channel", true }, + { "mode", true } + }; +} + +json::Value ServerChannelMode::exec(Irccd &irccd, const json::Value &request) const +{ + irccd.requireServer(request.at("server").toString())->cmode( + request.at("channel").toString(), + request.at("mode").toString() + ); + + return RemoteCommand::exec(irccd, request); +} + +} // !command + +} // !irccd +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-cmode.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,64 @@ +/* + * server-cmode.h -- implementation of server-cmode transport command + * + * 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_COMMAND_SERVER_CHANNEL_MODE_H_ +#define _IRCCD_COMMAND_SERVER_CHANNEL_MODE_H_ + +/** + * @file server-cmode.h + * @brief Implementation of server-cmode transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class ServerChannelMode + * @brief Implementation of server-cmode transport command. + */ +class ServerChannelMode : public RemoteCommand { +public: + /** + * Constructor. + */ + ServerChannelMode(); + + /** + * @copydoc RemoteCommand::help + */ + std::string help() const override; + + /** + * @copydoc TransportCommand::args + */ + std::vector<Arg> args() const override; + + /** + * @copydoc TransportCommand::exec + */ + json::Value exec(Irccd &irccd, const json::Value &object) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_COMMAND_SERVER_CHANNEL_MODE_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-cnotice.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,57 @@ +/* + * server-cnotice.cpp -- implementation of server-cnotice transport command + * + * 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 "cmd-server-cnotice.h" +#include "irccd.h" + +namespace irccd { + +namespace command { + +ServerChannelNotice::ServerChannelNotice() + : RemoteCommand("server-cnotice", "Server") +{ +} + +std::string ServerChannelNotice::help() const +{ + return ""; +} + +std::vector<RemoteCommand::Arg> ServerChannelNotice::args() const +{ + return { + { "server", true }, + { "channel", true }, + { "message", true } + }; +} + +json::Value ServerChannelNotice::exec(Irccd &irccd, const json::Value &request) const +{ + irccd.requireServer(request.at("server").toString())->cnotice( + request.at("channel").toString(), + request.at("message").toString() + ); + + return RemoteCommand::exec(irccd, request); +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-cnotice.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,73 @@ +/* + * server-cnotice.h -- implementation of server-cnotice transport command + * + * 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_COMMAND_SERVER_CNOTICE_H_ +#define _IRCCD_COMMAND_SERVER_CNOTICE_H_ + +/** + * @file server-cnotice.h + * @brief Implementation of server-cnotice transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class ServerChannelNotice + * @brief Implementation of server-cnotice transport command. + * + * Send a channel notice to the specified channel. + * + * { + * "command": "server-cnotice", + * "server": "the server name", + * "channel": "name", + * "message": "the message" + * } + */ +class ServerChannelNotice : public RemoteCommand { +public: + /** + * Constructor. + */ + ServerChannelNotice(); + + /** + * @copydoc RemoteCommand::help + */ + std::string help() const override; + + /** + * @copydoc TransportCommand::args + */ + std::vector<Arg> args() const override; + + /** + * @copydoc TransportCommand::exec + */ + json::Value exec(Irccd &irccd, const json::Value &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_COMMAND_SERVER_CNOTICE_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-connect.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,162 @@ +/* + * command-server-connect.cpp -- implementation of server-connect transport command + * + * 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 <limits> + +#include "cmd-server-connect.h" +#include "irccd.h" +#include "server.h" +#include "util.h" + +namespace irccd { + +namespace command { + +namespace { + +std::string readInfoName(const json::Value &object) +{ + auto it = object.find("name"); + + if (it == object.end()) + throw std::invalid_argument("missing 'name' property"); + if (!it->isString() || !util::isIdentifierValid(it->toString())) + throw std::invalid_argument("invalid server name"); + + return it->toString(); +} + +std::string readInfoHost(const json::Value &object) +{ + auto it = object.find("host"); + + if (it == object.end()) + throw std::invalid_argument("missing 'host' property"); + if (!it->isString()) + throw std::invalid_argument("invalid host"); + + return it->toString(); +} + +std::uint16_t readInfoPort(const json::Value &object) +{ + auto it = object.find("port"); + uint16_t port = 6667; + + if (it != object.end()) + if (it->isInt() && it->toInt() >= 0 && it->toInt() <= std::numeric_limits<std::uint16_t>::max()) + port = static_cast<std::uint16_t>(it->toInt()); + + return port; +} + +ServerInfo readInfo(const json::Value &object) +{ + ServerInfo info; + + /* Mandatory */ + info.name = readInfoName(object); + info.host = readInfoHost(object); + + /* Optional */ + info.port = readInfoPort(object); + + if (object.valueOr("ssl", json::Type::Boolean, false).toBool()) +#if defined(WITH_SSL) + info.flags |= ServerInfo::Ssl; +#else + throw std::invalid_argument("ssl is disabled"); +#endif + + if (object.valueOr("sslVerify", json::Type::Boolean, false).toBool()) + info.flags |= ServerInfo::SslVerify; + + return info; +} + +ServerIdentity readIdentity(const json::Value &object) +{ + ServerIdentity identity; + + identity.nickname = object.valueOr("nickname", json::Type::String, identity.nickname).toString(); + identity.realname = object.valueOr("realname", json::Type::String, identity.realname).toString(); + identity.username = object.valueOr("username", json::Type::String, identity.username).toString(); + identity.ctcpversion = object.valueOr("ctcpVersion", json::Type::String, identity.ctcpversion).toString(); + + return identity; +} + +ServerSettings readSettings(const json::Value &object) +{ + ServerSettings settings; + + settings.command = object.valueOr("commandChar", json::Type::String, settings.command).toString(); + settings.recotries = object.valueOr("reconnectTries", json::Type::Int, settings.recotries).toInt(); + settings.recotimeout = object.valueOr("reconnectTimeout", json::Type::Int, settings.recotimeout).toInt(); + + return settings; +} + +} // !namespace + +ServerConnect::ServerConnect() + : RemoteCommand("server-connect", "Server") +{ +} + +std::string ServerConnect::help() const +{ + return "Connect to a server."; +} + +std::vector<RemoteCommand::Option> ServerConnect::options() const +{ + return { + { "command", "c", "command", "char", "command character to use" }, + { "nickname", "n", "nickname", "nickname", "nickname to use" }, + { "realname", "r", "realname", "realname", "realname to use" }, + { "sslverify", "S", "ssl-verify", "", "verify SSL" }, + { "ssl", "s", "ssl", "", "connect with SSL" }, + { "username", "u", "username", "", "username to use" }, + }; +} + +std::vector<RemoteCommand::Arg> ServerConnect::args() const +{ + return { + { "id", true }, + { "host", true }, + { "port", false } + }; +} + +json::Value ServerConnect::exec(Irccd &irccd, const json::Value &request) const +{ + auto server = std::make_shared<Server>(readInfo(request), readIdentity(request), readSettings(request)); + + if (irccd.hasServer(server->info().name)) + throw std::invalid_argument("server '" + server->info().name + "' already exists"); + + irccd.addServer(std::move(server)); + + return RemoteCommand::exec(irccd, request); +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-connect.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,69 @@ +/* + * server-connect.h -- implementation of server-connect transport command + * + * 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_COMMAND_SERVER_CONNECT_H_ +#define _IRCCD_COMMAND_SERVER_CONNECT_H_ + +/** + * @file server-connect.h + * @brief Implementation of server-connect transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class ServerConnect + * @brief Implementation of server-connect transport command. + */ +class ServerConnect : public RemoteCommand { +public: + /** + * Constructor. + */ + ServerConnect(); + + /** + * @copydoc RemoteCommand::help + */ + std::string help() const override; + + /** + * @copydoc TransportCommand::options + */ + std::vector<Option> options() const override; + + /** + * @copydoc TransportCommand::args + */ + std::vector<Arg> args() const override; + + /** + * @copydoc TransportCommand::exec + */ + json::Value exec(Irccd &irccd, const json::Value &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_COMMAND_SERVER_CONNECT_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-disconnect.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,55 @@ +/* + * command-server-disconnect.cpp -- implementation of server-disconnect transport command + * + * 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 "cmd-server-disconnect.h" +#include "irccd.h" + +namespace irccd { + +namespace command { + +ServerDisconnect::ServerDisconnect() + : RemoteCommand("server-disconnect", "Server") +{ +} + +std::string ServerDisconnect::help() const +{ + return ""; +} + +std::vector<RemoteCommand::Arg> ServerDisconnect::args() const +{ + return {{ "server", false }}; +} + +json::Value ServerDisconnect::exec(Irccd &irccd, const json::Value &request) const +{ + auto it = request.find("server"); + + if (it == request.end()) + irccd.clearServers(); + else + irccd.removeServer(it->toString()); + + return RemoteCommand::exec(irccd, request); +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-disconnect.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,61 @@ +/* + * server-disconnect.h -- implementation of server-disconnect transport command + * + * 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_COMMAND_SERVER_DISCONNECT_H_ +#define _IRCCD_COMMAND_SERVER_DISCONNECT_H_ + +/** + * @file server-disconnect.h + * @brief Implementation of server-disconnect transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class ServerDisconnect + * @brief Implementation of server-disconnect transport command. + */ +class ServerDisconnect : public RemoteCommand { +public: + /** + * Constructor. + */ + ServerDisconnect(); + + /** + * @copydoc RemoteCommand::help + */ + std::string help() const override; + + std::vector<Arg> args() const override; + + /** + * @copydoc TransportCommand::exec + */ + json::Value exec(Irccd &irccd, const json::Value &object) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_COMMAND_SERVER_DISCONNECT_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-info.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,113 @@ +/* + * command-server-info.cpp -- implementation of server-info transport command + * + * 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 <iostream> + +#include "cmd-server-info.h" +#include "irccd.h" + +namespace irccd { + +namespace command { + +ServerInfo::ServerInfo() + : RemoteCommand("server-info", "Server") +{ +} + +std::string ServerInfo::help() const +{ + return ""; +} + +std::vector<RemoteCommand::Arg> ServerInfo::args() const +{ + return {{ "server", true }}; +} + +json::Value ServerInfo::request(Irccdctl &, const RemoteCommandRequest &args) const +{ + return json::object({ + { "server", args.args()[0] }, + { "target", args.args()[1] }, + { "channel", args.args()[2] } + }); +} + +json::Value ServerInfo::exec(Irccd &irccd, const json::Value &request) const +{ + auto server = irccd.requireServer(request.at("server").toString()); + auto response = RemoteCommand::exec(irccd, request); + + /* General stuff */ + response.insert("name", server->info().name); + response.insert("host", server->info().host); + response.insert("port", server->info().port); + response.insert("nickname", server->identity().nickname); + response.insert("username", server->identity().username); + response.insert("realname", server->identity().realname); + + /* Optional stuff */ + if (server->info().flags & irccd::ServerInfo::Ipv6) + response.insert("ipv6", true); + if (server->info().flags & irccd::ServerInfo::Ssl) + response.insert("ssl", true); + if (server->info().flags & irccd::ServerInfo::SslVerify) + response.insert("sslVerify", true); + + /* Channel list */ + auto channels = json::array({}); + + for (const auto &c : server->settings().channels) + channels.append(c.name); + + response.insert("channels", std::move(channels)); + + return response; +} + +void ServerInfo::result(Irccdctl &irccdctl, const json::Value &response) const +{ + RemoteCommand::result(irccdctl, response); + + /* Server information */ + std::cout << std::boolalpha; + std::cout << "Name : " << response.valueOr("name", "").toString(true) << std::endl; + std::cout << "Host : " << response.valueOr("host", "").toString(true) << std::endl; + std::cout << "Port : " << response.valueOr("port", "").toString(true) << std::endl; + std::cout << "Ipv6 : " << response.valueOr("ipv6", "").toString(true) << std::endl; + std::cout << "SSL : " << response.valueOr("ssl", "").toString(true) << std::endl; + std::cout << "SSL verified : " << response.valueOr("sslVerify", "").toString(true) << std::endl; + + /* Channels */ + std::cout << "Channels : "; + + for (const json::Value &v : response.valueOr("channels", json::Type::Array, json::array({}))) + std::cout << v.toString() << " "; + + std::cout << std::endl; + + /* Identity */ + std::cout << "Nickname : " << response.valueOr("nickname", "").toString(true) << std::endl; + std::cout << "User name : " << response.valueOr("username", "").toString(true) << std::endl; + std::cout << "Real name : " << response.valueOr("realname", "").toString(true) << std::endl; +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-info.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,65 @@ +/* + * command-server-info.h -- implementation of server-info transport command + * + * 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_COMMAND_SERVER_INFO_H_ +#define _IRCCD_COMMAND_SERVER_INFO_H_ + +/** + * @file command-server-info.h + * @brief Implementation of server-info transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class ServerInfo + * @brief Implementation of server-info transport command. + */ +class ServerInfo : public RemoteCommand { +public: + ServerInfo(); + + /** + * @copydoc RemoteCommand::help + */ + std::string help() const override; + + /** + * @copydoc TransportCommand::args + */ + std::vector<Arg> args() const override; + + json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; + + /** + * @copydoc TransportCommand::exec + */ + json::Value exec(Irccd &irccd, const json::Value &object) const override; + + void result(Irccdctl &irccdctl, const json::Value &response) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_COMMAND_SERVER_INFO_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-invite.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,68 @@ +/* + * command-server-invite.cpp -- implementation of server-invite transport command + * + * 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 "cmd-server-invite.h" +#include "irccd.h" + +namespace irccd { + +namespace command { + +ServerInvite::ServerInvite() + : RemoteCommand("server-invite", "Server") +{ +} + +std::string ServerInvite::help() const +{ + return ""; +} + +std::vector<RemoteCommand::Arg> ServerInvite::args() const +{ + return { + { "server", true }, + { "nickname", true }, + { "channel", true } + }; +} + +json::Value ServerInvite::request(Irccdctl &, const RemoteCommandRequest &args) const +{ + return json::object({ + { "server", args.args()[0] }, + { "target", args.args()[1] }, + { "channel", args.args()[2] } + }); +} + +json::Value ServerInvite::exec(Irccd &irccd, const json::Value &request) const +{ + irccd.requireServer( + request.at("server").toString())->invite( + request.at("target").toString(), + request.at("channel").toString() + ); + + return RemoteCommand::exec(irccd, request); +} + +} // !command + +} // !irccd +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-invite.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,68 @@ +/* + * server-invite.h -- implementation of server-invite transport command + * + * 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_COMMAND_SERVER_INVITE_H_ +#define _IRCCD_COMMAND_SERVER_INVITE_H_ + +/** + * @file server-invite.h + * @brief Implementation of server-invite transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class ServerInvite + * @brief Implementation of server-invite transport command. + */ +class ServerInvite : public RemoteCommand { +public: + ServerInvite(); + + /** + * @copydoc RemoteCommand::help + */ + std::string help() const override; + + /** + * @copydoc RemoteCommand::args + */ + std::vector<Arg> args() const override; + + /** + * @copydoc RemoteCommand::request + */ + json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; + + /** + * @copydoc TransportCommand::exec + */ + json::Value exec(Irccd &irccd, const json::Value &request) const override; + + +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_COMMAND_SERVER_INVITE_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-join.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,71 @@ +/* + * server-join.cpp -- implementation of server-join transport command + * + * 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 "cmd-server-join.h" +#include "irccd.h" + +namespace irccd { + +namespace command { + +ServerJoin::ServerJoin() + : RemoteCommand("server-join", "Server") +{ +} + +std::string ServerJoin::help() const +{ + return ""; +} + +std::vector<RemoteCommand::Arg> ServerJoin::args() const +{ + return { + { "server", true }, + { "channel", true }, + { "password", false } + }; +} + +json::Value ServerJoin::request(Irccdctl &, const RemoteCommandRequest &args) const +{ + auto req = json::object({ + { "server", args.args()[0] }, + { "channel", args.args()[1] } + }); + + if (args.length() == 3) + req.insert("password", args.args()[2]); + + return req; +} + +json::Value ServerJoin::exec(Irccd &irccd, const json::Value &request) const +{ + irccd.requireServer( + request.at("server").toString())->join( + request.at("channel").toString(), + request.valueOr("password", json::Type::String, "").toString() + ); + + return RemoteCommand::exec(irccd, request); +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-join.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,66 @@ +/* + * server-join.h -- implementation of server-join transport command + * + * 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_COMMAND_SERVER_JOIN_H_ +#define _IRCCD_COMMAND_SERVER_JOIN_H_ + +/** + * @file server-join.h + * @brief Implementation of server-join transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class ServerJoin + * @brief Implementation of server-join transport command. + */ +class ServerJoin : public RemoteCommand { +public: + ServerJoin(); + + /** + * @copydoc RemoteCommand::help + */ + std::string help() const override; + + /** + * @copydoc RemoteCommand::args + */ + std::vector<Arg> args() const override; + + /** + * @copydoc RemoteCommand::request + */ + json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; + + /** + * @copydoc RemoteCommand::exec + */ + json::Value exec(Irccd &irccd, const json::Value &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_COMMAND_SERVER_JOIN_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-kick.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,73 @@ +/* + * command-server-kick.cpp -- implementation of server-kick transport command + * + * 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 "cmd-server-kick.h" +#include "irccd.h" + +namespace irccd { + +namespace command { + +ServerKick::ServerKick() + : RemoteCommand("server-kick", "Server") +{ +} + +std::string ServerKick::help() const +{ + return ""; +} + +std::vector<RemoteCommand::Arg> ServerKick::args() const +{ + return { + { "server", true }, + { "target", true }, + { "channel", true }, + { "reason", false } + }; +} + +json::Value ServerKick::request(Irccdctl &, const RemoteCommandRequest &args) const +{ + auto req = json::object({ + { "server", args.arg(0) }, + { "target", args.arg(1) }, + { "channel", args.arg(2) } + }); + + if (args.length() == 4) + req.insert("reason", args.arg(3)); + + return req; +} + +json::Value ServerKick::exec(Irccd &irccd, const json::Value &request) const +{ + irccd.requireServer(request.at("server").toString())->kick( + request.at("target").toString(), + request.at("channel").toString(), + request.valueOr("reason", json::Type::String, "").toString() + ); + + return RemoteCommand::exec(irccd, request); +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-kick.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,66 @@ +/* + * server-kick.h -- implementation of server-kick transport command + * + * 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_COMMAND_SERVER_KICK_H_ +#define _IRCCD_COMMAND_SERVER_KICK_H_ + +/** + * @file server-kick.h + * @brief Implementation of server-kick transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class ServerKick + * @brief Implementation of server-kick transport command. + */ +class ServerKick : public RemoteCommand { +public: + ServerKick(); + + /** + * @copydoc RemoteCommand::help + */ + std::string help() const override; + + /** + * @copydoc RemoteCommand::args + */ + std::vector<Arg> args() const override; + + /** + * @copydoc RemoteCommand::request + */ + json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; + + /** + * @copydoc RemoteCommand::exec + */ + json::Value exec(Irccd &irccd, const json::Value &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_COMMAND_SERVER_KICK_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-list.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,59 @@ +/* + * server-list.cpp -- implementation of server-list transport command + * + * 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 <iostream> + +#include "cmd-server-list.h" +#include "irccd.h" + +namespace irccd { + +namespace command { + +ServerList::ServerList() + : RemoteCommand("server-list", "Server") +{ +} + +std::string ServerList::help() const +{ + return ""; +} + +json::Value ServerList::exec(Irccd &irccd, const json::Value &) const +{ + auto json = json::object({}); + auto list = json::array({}); + + for (const auto &pair : irccd.servers()) + list.append(pair.first); + + json.insert("list", std::move(list)); + + return json; +} + +void ServerList::result(Irccdctl &, const json::Value &response) const +{ + for (const auto &n : response.valueOr("list", json::Type::Array, json::array({}))) + std::cout << n.toString() << std::endl; +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-list.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,55 @@ +/* + * server-list.h -- implementation of server-list transport command + * + * 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_COMMAND_SERVER_LIST_H_ +#define _IRCCD_COMMAND_SERVER_LIST_H_ + +/** + * @file command-server-list.h + * @brief Implementation of server-list transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class ServerList + * @brief Implementation of server-list transport command. + */ +class ServerList : public RemoteCommand { +public: + ServerList(); + + /** + * @copydoc RemoteCommand::help + */ + std::string help() const override; + + json::Value exec(Irccd &irccd, const json::Value &request) const override; + + void result(Irccdctl &irccdctl, const json::Value &response) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_COMMAND_SERVER_LIST_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-me.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,66 @@ +/* + * server-me.cpp -- implementation of server-me transport command + * + * 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 "cmd-server-me.h" +#include "irccd.h" + +namespace irccd { + +namespace command { + +ServerMe::ServerMe() + : RemoteCommand("server-me", "Server") +{ +} + +std::string ServerMe::help() const +{ + return ""; +} + +std::vector<RemoteCommand::Arg> ServerMe::args() const +{ + return { + { "server", true }, + { "target", true }, + { "message", true } + }; +} + +json::Value ServerMe::request(Irccdctl &, const RemoteCommandRequest &args) const +{ + return json::object({ + { "server", args.arg(0) }, + { "target", args.arg(1) }, + { "message", args.arg(2) } + }); +} + +json::Value ServerMe::exec(Irccd &irccd, const json::Value &request) const +{ + irccd.requireServer(request.at("server").toString())->me( + request.at("target").toString(), + request.at("message").toString() + ); + + return nullptr; +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-me.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,66 @@ +/* + * server-me.h -- implementation of server-me transport command + * + * 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_COMMAND_SERVER_ME_H_ +#define _IRCCD_COMMAND_SERVER_ME_H_ + +/** + * @file command-.h + * @brief Implementation of transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class ServerMe + * @brief Implementation of server-me transport command. + */ +class ServerMe : public RemoteCommand { +public: + ServerMe(); + + /** + * @copydoc RemoteCommand::help + */ + std::string help() const override; + + /** + * @copydoc RemoteCommand::args + */ + std::vector<Arg> args() const override; + + /** + * @copydoc RemoteCommand::request + */ + json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; + + /** + * @copydoc RemoteCommand::exec + */ + json::Value exec(Irccd &irccd, const json::Value &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_COMMAND_SERVER_ME_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-message.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,66 @@ +/* + * server-message.cpp -- implementation of server-message transport command + * + * 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 "cmd-server-message.h" +#include "irccd.h" + +namespace irccd { + +namespace command { + +ServerMessage::ServerMessage() + : RemoteCommand("server-message", "Server") +{ +} + +std::string ServerMessage::help() const +{ + return ""; +} + +std::vector<RemoteCommand::Arg> ServerMessage::args() const +{ + return { + { "server", true }, + { "target", true }, + { "message", true } + }; +} + +json::Value ServerMessage::request(Irccdctl &, const RemoteCommandRequest &args) const +{ + return json::object({ + { "server", args.arg(0) }, + { "target", args.arg(1) }, + { "message", args.arg(2) } + }); +} + +json::Value ServerMessage::exec(Irccd &irccd, const json::Value &request) const +{ + irccd.requireServer(request.at("server").toString())->me( + request.at("target").toString(), + request.at("message").toString() + ); + + return nullptr; +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-message.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,66 @@ +/* + * server-message.h -- implementation of server-message transport command + * + * 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_COMMAND_SERVER_MESSAGE_H_ +#define _IRCCD_COMMAND_SERVER_MESSAGE_H_ + +/** + * @file command-server-message.h + * @brief Implementation of server-message transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class ServerMessage + * @brief Implementation of server-message transport command. + */ +class ServerMessage : public RemoteCommand { +public: + ServerMessage(); + + /** + * @copydoc RemoteCommand::help + */ + std::string help() const override; + + /** + * @copydoc RemoteCommand::args + */ + std::vector<Arg> args() const override; + + /** + * @copydoc RemoteCommand::request + */ + json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; + + /** + * @copydoc RemoteCommand::exec + */ + json::Value exec(Irccd &irccd, const json::Value &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_COMMAND_SERVER_MESSAGE_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-mode.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,61 @@ +/* + * command-server-mode.cpp -- implementation of server-mode transport command + * + * 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 "cmd-server-mode.h" +#include "irccd.h" + +namespace irccd { + +namespace command { + +ServerMode::ServerMode() + : RemoteCommand("server-mode", "Server") +{ +} + +std::string ServerMode::help() const +{ + return ""; +} + +std::vector<RemoteCommand::Arg> ServerMode::args() const +{ + return { + { "server", true }, + { "mode", true } + }; +} + +json::Value ServerMode::request(Irccdctl &, const RemoteCommandRequest &args) const +{ + return json::object({ + { "server", args.arg(0) }, + { "mode", args.arg(1) } + }); +} + +json::Value ServerMode::exec(Irccd &irccd, const json::Value &request) const +{ + irccd.requireServer(request.at("server").toString())->mode(request.at("mode").toString()); + + return nullptr; +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-mode.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,66 @@ +/* + * server-mode.h -- implementation of server-mode transport command + * + * 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_COMMAND_SERVER_MODE_H_ +#define _IRCCD_COMMAND_SERVER_MODE_H_ + +/** + * @file server-mode.h + * @brief Implementation of server-mode transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class ServerMode + * @brief Implementation of server-mode transport command. + */ +class ServerMode : public RemoteCommand { +public: + ServerMode(); + + /** + * @copydoc RemoteCommand::help + */ + std::string help() const override; + + /** + * @copydoc RemoteCommand::args + */ + std::vector<Arg> args() const override; + + /** + * @copydoc RemoteCommand::request + */ + json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; + + /** + * @copydoc RemoteCommand::exec + */ + json::Value exec(Irccd &irccd, const json::Value &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_COMMAND_SERVER_MODE_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-nick.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,61 @@ +/* + * server-nick.cpp -- implementation of server-nick transport command + * + * 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 "cmd-server-nick.h" +#include "irccd.h" + +namespace irccd { + +namespace command { + +ServerNick::ServerNick() + : RemoteCommand("server-nick", "Server") +{ +} + +std::string ServerNick::help() const +{ + return ""; +} + +std::vector<RemoteCommand::Arg> ServerNick::args() const +{ + return { + { "server", true }, + { "nickname", true } + }; +} + +json::Value ServerNick::request(Irccdctl &, const RemoteCommandRequest &args) const +{ + return json::object({ + { "server", args.arg(0) }, + { "nickname", args.arg(1) } + }); +} + +json::Value ServerNick::exec(Irccd &irccd, const json::Value &object) const +{ + irccd.requireServer(object.at("server").toString())->nick(object.at("nickname").toString()); + + return nullptr; +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-nick.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,66 @@ +/* + * server-nick.h -- implementation of server-nick transport command + * + * 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_COMMAND_SERVER_NICK_H_ +#define _IRCCD_COMMAND_SERVER_NICK_H_ + +/** + * @file command-server-nick.h + * @brief Implementation of server-nick transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class ServerNick + * @brief Implementation of server-nick transport command. + */ +class ServerNick : public RemoteCommand { +public: + ServerNick(); + + /** + * @copydoc RemoteCommand::help + */ + std::string help() const override; + + /** + * @copydoc RemoteCommand::args + */ + std::vector<Arg> args() const override; + + /** + * @copydoc RemoteCommand::request + */ + json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; + + /** + * @copydoc RemoteCommand::exec + */ + json::Value exec(Irccd &irccd, const json::Value &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_COMMAND_SERVER_NICK_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-notice.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,67 @@ +/* + * server-notice.cpp -- implementation of server-notice transport command + * + * 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 "cmd-server-notice.h" +#include "irccd.h" + +namespace irccd { + +namespace command { + +ServerNotice::ServerNotice() + : RemoteCommand("server-notice", "Server") +{ +} + +std::string ServerNotice::help() const +{ + return ""; +} + +std::vector<RemoteCommand::Arg> ServerNotice::args() const +{ + return { + { "server", true }, + { "target", true }, + { "message", true } + }; +} + +json::Value ServerNotice::request(Irccdctl &, const RemoteCommandRequest &args) const +{ + return json::object({ + { "server", args.arg(0) }, + { "target", args.arg(1) }, + { "message", args.arg(2) } + }); +} + +json::Value ServerNotice::exec(Irccd &irccd, const json::Value &request) const +{ + irccd.requireServer(request.at("server").toString())->notice( + request.at("target").toString(), + request.at("message").toString() + ); + + return nullptr; +} + +} // !command + +} // !irccd +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-notice.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,66 @@ +/* + * server-notice.h -- implementation of server-notice transport command + * + * 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_COMMAND_SERVER_NOTICE_H_ +#define _IRCCD_COMMAND_SERVER_NOTICE_H_ + +/** + * @file server-notice.h + * @brief Implementation of server-notice transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class ServerNotice + * @brief Implementation of server-notice transport command. + */ +class ServerNotice : public RemoteCommand { +public: + ServerNotice(); + + /** + * @copydoc RemoteCommand::help + */ + std::string help() const override; + + /** + * @copydoc RemoteCommand::args + */ + std::vector<Arg> args() const override; + + /** + * @copydoc RemoteCommand::request + */ + json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; + + /** + * @copydoc RemoteCommand::exec + */ + json::Value exec(Irccd &irccd, const json::Value &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_COMMAND_SERVER_NOTICE_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-part.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,70 @@ +/* + * server-part.cpp -- implementation of server-part transport command + * + * 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 "cmd-server-part.h" +#include "irccd.h" + +namespace irccd { + +namespace command { + +ServerPart::ServerPart() + : RemoteCommand("server-part", "Server") +{ +} + +std::string ServerPart::help() const +{ + return ""; +} + +std::vector<RemoteCommand::Arg> ServerPart::args() const +{ + return { + { "server", true }, + { "channel", true }, + { "reason", false } + }; +} + +json::Value ServerPart::request(Irccdctl &, const RemoteCommandRequest &args) const +{ + auto req = json::object({ + { "server", args.arg(0) }, + { "channel", args.arg(1) } + }); + + if (args.length() == 3) + req.insert("reason", args.arg(2)); + + return req; +} + +json::Value ServerPart::exec(Irccd &irccd, const json::Value &request) const +{ + irccd.requireServer(request.at("server").toString())->part( + request.at("channel").toString(), + request.valueOr("reason", "").toString() + ); + + return nullptr; +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-part.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,66 @@ +/* + * server-part.h -- implementation of server-part transport command + * + * 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_COMMAND_SERVER_PART_H_ +#define _IRCCD_COMMAND_SERVER_PART_H_ + +/** + * @file server-part.h + * @brief Implementation of server-part transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class ServerPart + * @brief Implementation of server-part transport command. + */ +class ServerPart : public RemoteCommand { +public: + ServerPart(); + + /** + * @copydoc RemoteCommand::help + */ + std::string help() const override; + + /** + * @copydoc RemoteCommand::args + */ + std::vector<Arg> args() const override; + + /** + * @copydoc RemoteCommand::request + */ + json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; + + /** + * @copydoc RemoteCommand::exec + */ + json::Value exec(Irccd &irccd, const json::Value &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_COMMAND_SERVER_PART_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-reconnect.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,61 @@ +/* + * command-server-reconnect.cpp -- implementation of server-reconnect transport command + * + * 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 "cmd-server-reconnect.h" +#include "irccd.h" + +namespace irccd { + +namespace command { + +ServerReconnect::ServerReconnect() + : RemoteCommand("server-reconnect", "Server") +{ +} + +std::string ServerReconnect::help() const +{ + return ""; +} + +std::vector<RemoteCommand::Arg> ServerReconnect::args() const +{ + return {{ "server", false }}; +} + +json::Value ServerReconnect::request(Irccdctl &, const RemoteCommandRequest &args) const +{ + return args.length() == 0 ? nullptr : json::object({ { "server", args.arg(0) } }); +} + +json::Value ServerReconnect::exec(Irccd &irccd, const json::Value &request) const +{ + auto server = request.find("server"); + + if (server != request.end() && server->isString()) + irccd.requireServer(server->toString())->reconnect(); + else + for (auto &pair : irccd.servers()) + pair.second->reconnect(); + + return nullptr; +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-reconnect.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,66 @@ +/* + * server-reconnect.h -- implementation of server-reconnect transport command + * + * 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_COMMAND_SERVER_RECONNECT_H_ +#define _IRCCD_COMMAND_SERVER_RECONNECT_H_ + +/** + * @file server-reconnect.h + * @brief Implementation of server-reconnect transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class ServerReconnect + * @brief Implementation of server-reconnect transport command. + */ +class ServerReconnect : public RemoteCommand { +public: + ServerReconnect(); + + /** + * @copydoc RemoteCommand::help + */ + std::string help() const override; + + /** + * @copydoc RemoteCommand::args + */ + std::vector<Arg> args() const override; + + /** + * @copydoc RemoteCommand::request + */ + json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; + + /** + * @copydoc RemoteCommand::exec + */ + json::Value exec(Irccd &irccd, const json::Value &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_COMMAND_SERVER_RECONNECT_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-topic.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,66 @@ +/* + * command-server-topic.cpp -- implementation of server-topic transport command + * + * 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 "cmd-server-topic.h" +#include "irccd.h" + +namespace irccd { + +namespace command { + +ServerTopic::ServerTopic() + : RemoteCommand("server-topic", "Server") +{ +} + +std::string ServerTopic::help() const +{ + return ""; +} + +std::vector<RemoteCommand::Arg> ServerTopic::args() const +{ + return { + { "server", true }, + { "channel", true }, + { "topic", true } + }; +} + +json::Value ServerTopic::request(Irccdctl &, const RemoteCommandRequest &args) const +{ + return json::object({ + { "server", args.arg(0) }, + { "channel", args.arg(1) }, + { "topic", args.arg(2) } + }); +} + +json::Value ServerTopic::exec(Irccd &irccd, const json::Value &request) const +{ + irccd.requireServer(request.at("server").toString())->topic( + request.at("channel").toString(), + request.at("topic").toString() + ); + + return nullptr; +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-server-topic.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,66 @@ +/* + * server-topic.h -- implementation of server-topic transport command + * + * 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_COMMAND_SERVER_TOPIC_H_ +#define _IRCCD_COMMAND_SERVER_TOPIC_H_ + +/** + * @file server-topic.h + * @brief Implementation of server-topic transport command. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class ServerTopic + * @brief Implementation of server-topic transport command. + */ +class ServerTopic : public RemoteCommand { +public: + ServerTopic(); + + /** + * @copydoc RemoteCommand::help + */ + std::string help() const override; + + /** + * @copydoc RemoteCommand::args + */ + std::vector<Arg> args() const override; + + /** + * @copydoc RemoteCommand::request + */ + json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; + + /** + * @copydoc RemoteCommand::exec + */ + json::Value exec(Irccd &irccd, const json::Value &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCD_COMMAND_SERVER_TOPIC_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-watch.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,244 @@ +/* + * command-watch.cpp -- implementation of irccdctl watch + * + * 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 <functional> +#include <iostream> +#include <sstream> +#include <unordered_map> + +#include "cmd-watch.h" +#include "irccdctl.h" + +namespace irccd { + +namespace command { + +namespace { + +void onChannelMode(const json::Value &v) +{ + std::cout << "event: onChannelMode\n"; + std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; + std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; + std::cout << "mode: " << v.valueOr("mode", "").toString() << "\n"; + std::cout << "argument: " << v.valueOr("argument", "").toString() << "\n"; +} + +void onChannelNotice(const json::Value &v) +{ + std::cout << "event: onChannelNotice\n"; + std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; + std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; + std::cout << "channel: " << v.valueOr("channel", "").toString() << "\n"; + std::cout << "message: " << v.valueOr("message", "").toString() << "\n"; +} + +void onConnect(const json::Value &v) +{ + std::cout << "event: onConnect\n"; + std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; +} + +void onInvite(const json::Value &v) +{ + std::cout << "event: onInvite\n"; + std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; + std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; + std::cout << "channel: " << v.valueOr("channel", "").toString() << "\n"; +} + +void onJoin(const json::Value &v) +{ + std::cout << "event: onJoin\n"; + std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; + std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; + std::cout << "channel: " << v.valueOr("channel", "").toString() << "\n"; +} + +void onKick(const json::Value &v) +{ + std::cout << "event: onKick\n"; + std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; + std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; + std::cout << "channel: " << v.valueOr("channel", "").toString() << "\n"; + std::cout << "target: " << v.valueOr("target", "").toString() << "\n"; + std::cout << "reason: " << v.valueOr("reason", "").toString() << "\n"; +} + +void onMessage(const json::Value &v) +{ + std::cout << "event: onMessage\n"; + std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; + std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; + std::cout << "channel: " << v.valueOr("channel", "").toString() << "\n"; + std::cout << "message: " << v.valueOr("message", "").toString() << "\n"; +} + +void onMe(const json::Value &v) +{ + std::cout << "event: onMe\n"; + std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; + std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; + std::cout << "target: " << v.valueOr("target", "").toString() << "\n"; + std::cout << "message: " << v.valueOr("message", "").toString() << "\n"; +} + +void onMode(const json::Value &v) +{ + std::cout << "event: onMode\n"; + std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; + std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; + std::cout << "mode: " << v.valueOr("mode", "").toString() << "\n"; +} + +void onNames(const json::Value &v) +{ + std::cout << "event: onNames\n"; + std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; + std::cout << "channel: " << v.valueOr("channel", "").toString() << "\n"; + std::cout << "names: " << v.valueOr("names", "").toJson(0) << "\n"; +} + +void onNick(const json::Value &v) +{ + std::cout << "event: onNick\n"; + std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; + std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; + std::cout << "nickname: " << v.valueOr("nickname", "").toString() << "\n"; +} + +void onNotice(const json::Value &v) +{ + std::cout << "event: onNotice\n"; + std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; + std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; + std::cout << "message: " << v.valueOr("message", "").toString() << "\n"; +} + +void onPart(const json::Value &v) +{ + std::cout << "event: onPart\n"; + std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; + std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; + std::cout << "channel: " << v.valueOr("channel", "").toString() << "\n"; + std::cout << "reason: " << v.valueOr("reason", "").toString() << "\n"; +} + +void onQuery(const json::Value &v) +{ + std::cout << "event: onQuery\n"; + std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; + std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; + std::cout << "message: " << v.valueOr("message", "").toString() << "\n"; +} + +void onTopic(const json::Value &v) +{ + std::cout << "event: onTopic\n"; + std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; + std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; + std::cout << "channel: " << v.valueOr("channel", "").toString() << "\n"; + std::cout << "topic: " << v.valueOr("topic", "").toString() << "\n"; +} + +void onWhois(const json::Value &v) +{ + std::cout << "event: onWhois\n"; + std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; + std::cout << "nickname: " << v.valueOr("nickname", "").toString() << "\n"; + std::cout << "username: " << v.valueOr("username", "").toString() << "\n"; + std::cout << "host: " << v.valueOr("host", "").toString() << "\n"; + std::cout << "realname: " << v.valueOr("realname", "").toString() << "\n"; +} + +const std::unordered_map<std::string, std::function<void (const json::Value &)>> events{ + { "onChannelMode", onChannelMode }, + { "onChannelNotice", onChannelNotice }, + { "onConnect", onConnect }, + { "onInvite", onInvite }, + { "onJoin", onJoin }, + { "onKick", onKick }, + { "onMessage", onMessage }, + { "onMe", onMe }, + { "onMode", onMode }, + { "onNames", onNames }, + { "onNick", onNick }, + { "onNotice", onNotice }, + { "onPart", onPart }, + { "onQuery", onQuery }, + { "onTopic", onTopic }, + { "onWhois", onWhois } +}; + +} // !namespace + +Watch::Watch() + : RemoteCommand("watch", "General") +{ +} + +std::vector<RemoteCommand::Option> Watch::options() const +{ + return {{ "format", "f", "format", "format", "output format" }}; +} + +std::string Watch::help() const +{ + std::ostringstream oss; + + oss << "Start watching irccd events.\n\n"; + oss << "You can use different output formats, native which is a human readable\n"; + oss << "format or json, pretty formatted json."; + + return oss.str(); +} + +json::Value Watch::request(Irccdctl &ctl, const RemoteCommandRequest &request) const +{ + std::string format = request.optionOr("format", "native"); + + if (format != "native" && format != "json") + throw std::invalid_argument("invalid format given: " + format); + + while (ctl.connection().isConnected()) { + try { + auto object = ctl.connection().next(-1); + auto it = events.find(object.valueOr("event", "").toString()); + + /* Silently ignore to avoid breaking user output */ + if (it == events.end()) + continue; + + if (format == "json") { + std::cout << object.toJson() << std::endl; + } else { + it->second(object); + std::cout << std::endl; + } + } catch (...) { + } + } + + throw std::runtime_error("connection lost"); + + return nullptr; +} + +} // !command + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/cmd-watch.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,58 @@ +/* + * command-watch.h -- implementation of irccdctl watch + * + * 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 _IRCCDCTL_COMMAND_WATCH_H_ +#define _IRCCDCTL_COMMAND_WATCH_H_ + +/** + * @file command-watch.h + * @brief Implementation of irccdctl watch. + */ + +#include "command.h" + +namespace irccd { + +namespace command { + +/** + * @class Watch + * @brief Implementation of irccdctl watch. + */ +class Watch : public RemoteCommand { +public: + Watch(); + + std::vector<Option> options() const override; + + /** + * @copydoc RemoteCommand::help + */ + std::string help() const override; + + /** + * @copydoc RemoteCommand::request + */ + json::Value request(Irccdctl &ctl, const RemoteCommandRequest &request) const override; +}; + +} // !command + +} // !irccd + +#endif // !_IRCCDCTL_COMMAND_WATCH_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/command.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,107 @@ +/* + * command.cpp -- remote command + * + * 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 <iomanip> +#include <sstream> + +#include <irccd/logger.h> +#include <irccd/system.h> + +#include "command.h" + +using namespace std::string_literals; + +namespace irccd { + +std::string RemoteCommand::usage() const +{ + std::ostringstream oss; + + oss << "usage: " << sys::programName() << " " << m_name; + + /* Options summary */ + if (options().size() > 0) + oss << " [options...]"; + + /* Arguments summary */ + if (args().size() > 0) { + oss << " "; + + for (const auto &arg : args()) + oss << (arg.required() ? "" : "[") << arg.name() << (arg.required() ? "" : "]") << " "; + } + + /* Description */ + oss << "\n\n" << help() << "\n\n"; + + /* Options */ + if (options().size() > 0) { + oss << "Options:\n"; + + for (const auto &opt : options()) { + std::ostringstream optoss; + + /* Construct the line for the option in a single string to pad it correctly */ + optoss << " "; + optoss << (!opt.simpleKey().empty() ? ("-"s + opt.simpleKey() + " ") : " "); + optoss << (!opt.longKey().empty() ? ("--"s + opt.longKey() + " "s) : ""); + optoss << opt.arg(); + + /* Add it padded with spaces */ + oss << std::left << std::setw(28) << optoss.str(); + oss << opt.description() << "\n"; + } + } + + return oss.str(); +} + +unsigned RemoteCommand::min() const noexcept +{ + auto list = args(); + + return std::accumulate(list.begin(), list.end(), 0U, [] (unsigned i, const auto &arg) noexcept -> unsigned { + return i + (arg.required() ? 1 : 0); + }); +} + +unsigned RemoteCommand::max() const noexcept +{ + return (unsigned)args().size(); +} + +json::Value RemoteCommand::request(Irccdctl &, const RemoteCommandRequest &) const +{ + return json::object({}); +} + +json::Value RemoteCommand::exec(Irccd &, const json::Value &) const +{ + return json::object({}); +} + +void RemoteCommand::result(Irccdctl &, const json::Value &response) const +{ + auto it = response.find("error"); + + if (it != response.end() && it->isString()) + log::warning() << "irccdctl: " << it->toString() << std::endl; +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/command.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,469 @@ +/* + * command.h -- remote command + * + * 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_COMMAND_H +#define IRCCD_COMMAND_H + +#include <cassert> +#include <map> +#include <vector> + +#include <irccd/json.h> + +namespace irccd { + +class Irccd; +class Irccdctl; + +/** + * @brief Command line arguments to irccdctl. + * + * This class contains the resolved arguments from command line that can apply to the command. + */ +class RemoteCommandRequest { +public: + using Options = std::multimap<std::string, std::string>; + using Args = std::vector<std::string>; + +private: + Options m_options; + Args m_args; + +public: + /** + * Construct the request. + * + * @param options the options + * @param args the arguments + */ + inline RemoteCommandRequest(Options options, Args args) noexcept + : m_options(std::move(options)) + , m_args(std::move(args)) + { + } + + /** + * Get the arguments. + * + * @return the arguments + */ + inline const Args &args() const noexcept + { + return m_args; + } + + /** + * Get the options. + * + * @return the options + */ + inline const Options &options() const noexcept + { + return m_options; + } + + /** + * Get the number of arguments. + * + * @return the number of arguments + */ + inline unsigned length() const noexcept + { + return (unsigned)m_args.size(); + } + + /** + * Check if the request has the given option id. + * + * @param option the option id + * @return true if the option is available + */ + inline bool has(const std::string &option) const noexcept + { + return m_options.count(option) != 0; + } + + /** + * Get the argument at the specified index. + * + * @pre index < length() + * @param index the argument index + * @return the argument + */ + inline const std::string &arg(unsigned index) const noexcept + { + assert(index < m_args.size()); + + return m_args[index]; + } + + /** + * Get the argument or default value if not available. + * + * @param index the index + * @param defaultValue the value if index is out of range + * @return the argument + */ + inline std::string argOr(unsigned index, std::string defaultValue) const noexcept + { + return index < m_args.size() ? m_args[index] : defaultValue; + } + + /** + * Get the given option by its id. + * + * @pre has(key) + * @param key the option id + * @return the option + */ + inline const std::string &option(const std::string &key) const noexcept + { + assert(m_options.count(key) != 0); + + return m_options.find(key)->second; + } + + /** + * Get the given option by its id or defaultValue if not found. + * + * @param key the option id + * @return the option + */ + inline std::string optionOr(const std::string &key, std::string defaultValue) const noexcept + { + auto it = m_options.find(key); + + if (it == m_options.end()) + return defaultValue; + + return it->second; + } +}; + +/** + * @brief Invokable command. + * + * A remote command is a invokable command in the irccd daemon. You can register dynamically any remote command you + * like using Application::addCommand. + * + * The remote command will be usable directly from irccdctl without any other code. + * + * A remote command can have options and arguments. Options always come first, before arguments. + * + * The command workflow is defined as follow: + * + * 1. User wants to invoke a command, request() is called and return a JSON object containaing the request, it it send + * to the daemon. + * + * 2. The daemon receive the request and execute it using exec(). It returns a JSON object containint the request result + * or error if any. + * + * 3. Finally, the command receives the result in result() function and user can manipulate it. For convenience, the + * default implementation shows the error if any. + */ +class RemoteCommand { +public: + /** + * @brief Defines available options for this command. + */ + class Option; + + /** + * @brief Defines available arguments for this command. + */ + class Arg; + +private: + std::string m_name; + std::string m_category; + bool m_visible; + +public: + inline RemoteCommand(std::string name, std::string category, bool visible = true) noexcept + : m_name(std::move(name)) + , m_category(std::move(category)) + , m_visible(visible) + { + assert(!m_name.empty()); + assert(!m_category.empty()); + } + + /** + * Default destructor virtual. + */ + virtual ~RemoteCommand() = default; + + /** + * Return the command name, must not have spaces. + * + * @return the command name + */ + inline const std::string &name() const noexcept + { + return m_name; + } + + /** + * Get the command category. + * + * Irccdctl will sort commands by categories. + * + * @return the category + */ + inline const std::string &category() const noexcept + { + return m_category; + } + + /** + * Hide the command in non-verbose mode. + * + * @return true if the command should be visible in non-verbose mode + */ + inline bool visible() const noexcept + { + return m_visible; + } + + /** + * Return the command documentation usage. + * + * @return the usage + */ + std::string usage() const; + + /** + * Return the help message. + * + * @return the help message + */ + virtual std::string help() const = 0; + + /** + * Get the supported irccdctl options. + * + * @return the options + */ + virtual std::vector<Option> options() const + { + return {}; + } + + /** + * Get the supported arguments. + * + * @return the arguments + */ + virtual std::vector<Arg> args() const + { + return {}; + } + + /** + * Get the minimum number of arguments required. + * + * @return the minimum + */ + unsigned min() const noexcept; + + /** + * Get the maximum number of arguments required. + * + * @return the maximum + */ + unsigned max() const noexcept; + + /** + * Prepare a JSON request to the daemon. + * + * If the command is local and does not need to send anything to irccd's instance, return a null JSON value. + * + * The default implementation just send the command name with no arguments. + * + * @param irccdctl the irccdctl instance + * @param args the command line arguments and options + * @return the JSON object to send to the daemon + * @post the returned JSON value must be an object + */ + virtual json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const; + + /** + * Execute the command in the daemon. + * + * The user can return an object with any properties to forward to the client. Irccd will automatically + * add the command name and the appropriate status code. + * + * The default return an empty object which indicates success. + * + * If any exception is thrown from this function, it is forwarded to the client as error status. + * + * @param irccd the instance + * @param request the JSON request + * @return the response + */ + virtual json::Value exec(Irccd &irccd, const json::Value &request) const; + + /** + * What to do when receiving the response from irccd. + * + * This default implementation just check for an error string and shows it if any. + * + * @param irccdctl the irccdctl instane + * @param object the result + */ + virtual void result(Irccdctl &irccdctl, const json::Value &response) const; +}; + +/** + * @brief Option description for a command. + */ +class RemoteCommand::Option { +private: + std::string m_id; + std::string m_simple; + std::string m_long; + std::string m_arg; + std::string m_description; + +public: + /** + * Constructor an option description. + * + * Simple and long keys must not start with '-' or '--', they will be added automatically. + * + * If arg is not empty, the option takes an argument. + * + * @pre id must not be empty + * @pre at least simpleKey or longKey must not be empty + * @pre description must not be empty + * @param key the key the option key + * @param description the description + * @param flags the optional flags + */ + inline Option(std::string id, + std::string simpleKey, + std::string longKey, + std::string arg, + std::string description) noexcept + : m_id(std::move(id)) + , m_simple(std::move(simpleKey)) + , m_long(std::move(longKey)) + , m_arg(std::move(arg)) + , m_description(std::move(description)) + { + assert(!m_id.empty()); + assert(!m_simple.empty() || !m_long.empty()); + assert(!m_description.empty()); + } + + /** + * Get the id. + * + * @return the id + */ + inline const std::string &id() const noexcept + { + return m_id; + } + + /** + * Get the option key. + * + * @return the key + */ + inline const std::string &simpleKey() const noexcept + { + return m_simple; + } + + /** + * Get the long option. + * + * @return the long option + */ + inline const std::string &longKey() const noexcept + { + return m_long; + } + + /** + * Get the option description. + * + * @return the description + */ + inline const std::string &description() const noexcept + { + return m_description; + } + + /** + * Get the option argument name. + * + * @return the argument name if any + */ + inline const std::string &arg() const noexcept + { + return m_arg; + } +}; + +/** + * @brief Argument description for command. + */ +class RemoteCommand::Arg { +private: + std::string m_name; + bool m_required; + +public: + /** + * Construct an argument. + * + * @param name the name + * @param required true if the argument is required + */ + inline Arg(std::string name, bool required) noexcept + : m_name(std::move(name)) + , m_required(required) + { + } + + /** + * Get the argument name. + * + * @return the name + */ + inline const std::string &name() const noexcept + { + return m_name; + } + + /** + * Tells if the argument is required. + * + * @return true if required + */ + inline bool required() const noexcept + { + return m_required; + } +}; + +} // !irccd + +#endif // !IRCCD_COMMAND_H
--- a/lib/irccd/command/CMakeSources.cmake Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -set( - COMMAND_HEADERS - ${CMAKE_CURRENT_LIST_DIR}/command.h - ${CMAKE_CURRENT_LIST_DIR}/help.h - ${CMAKE_CURRENT_LIST_DIR}/plugin-info.h - ${CMAKE_CURRENT_LIST_DIR}/plugin-list.h - ${CMAKE_CURRENT_LIST_DIR}/plugin-load.h - ${CMAKE_CURRENT_LIST_DIR}/plugin-reload.h - ${CMAKE_CURRENT_LIST_DIR}/plugin-unload.h - ${CMAKE_CURRENT_LIST_DIR}/server-cmode.h - ${CMAKE_CURRENT_LIST_DIR}/server-cnotice.h - ${CMAKE_CURRENT_LIST_DIR}/server-connect.h - ${CMAKE_CURRENT_LIST_DIR}/server-disconnect.h - ${CMAKE_CURRENT_LIST_DIR}/server-info.h - ${CMAKE_CURRENT_LIST_DIR}/server-invite.h - ${CMAKE_CURRENT_LIST_DIR}/server-join.h - ${CMAKE_CURRENT_LIST_DIR}/server-kick.h - ${CMAKE_CURRENT_LIST_DIR}/server-list.h - ${CMAKE_CURRENT_LIST_DIR}/server-me.h - ${CMAKE_CURRENT_LIST_DIR}/server-message.h - ${CMAKE_CURRENT_LIST_DIR}/server-mode.h - ${CMAKE_CURRENT_LIST_DIR}/server-nick.h - ${CMAKE_CURRENT_LIST_DIR}/server-notice.h - ${CMAKE_CURRENT_LIST_DIR}/server-part.h - ${CMAKE_CURRENT_LIST_DIR}/server-reconnect.h - ${CMAKE_CURRENT_LIST_DIR}/server-topic.h - ${CMAKE_CURRENT_LIST_DIR}/watch.h -) - -set( - COMMAND_SOURCES - ${CMAKE_CURRENT_LIST_DIR}/command.cpp - ${CMAKE_CURRENT_LIST_DIR}/help.cpp - ${CMAKE_CURRENT_LIST_DIR}/plugin-info.cpp - ${CMAKE_CURRENT_LIST_DIR}/plugin-list.cpp - ${CMAKE_CURRENT_LIST_DIR}/plugin-load.cpp - ${CMAKE_CURRENT_LIST_DIR}/plugin-reload.cpp - ${CMAKE_CURRENT_LIST_DIR}/plugin-unload.cpp - ${CMAKE_CURRENT_LIST_DIR}/server-cmode.cpp - ${CMAKE_CURRENT_LIST_DIR}/server-cnotice.cpp - ${CMAKE_CURRENT_LIST_DIR}/server-connect.cpp - ${CMAKE_CURRENT_LIST_DIR}/server-disconnect.cpp - ${CMAKE_CURRENT_LIST_DIR}/server-info.cpp - ${CMAKE_CURRENT_LIST_DIR}/server-invite.cpp - ${CMAKE_CURRENT_LIST_DIR}/server-join.cpp - ${CMAKE_CURRENT_LIST_DIR}/server-kick.cpp - ${CMAKE_CURRENT_LIST_DIR}/server-list.cpp - ${CMAKE_CURRENT_LIST_DIR}/server-me.cpp - ${CMAKE_CURRENT_LIST_DIR}/server-message.cpp - ${CMAKE_CURRENT_LIST_DIR}/server-mode.cpp - ${CMAKE_CURRENT_LIST_DIR}/server-nick.cpp - ${CMAKE_CURRENT_LIST_DIR}/server-notice.cpp - ${CMAKE_CURRENT_LIST_DIR}/server-part.cpp - ${CMAKE_CURRENT_LIST_DIR}/server-reconnect.cpp - ${CMAKE_CURRENT_LIST_DIR}/server-topic.cpp - ${CMAKE_CURRENT_LIST_DIR}/watch.cpp -)
--- a/lib/irccd/command/command.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,107 +0,0 @@ -/* - * command.cpp -- remote command - * - * 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 <iomanip> -#include <sstream> - -#include <irccd/logger.h> -#include <irccd/system.h> - -#include "command.h" - -using namespace std::string_literals; - -namespace irccd { - -std::string RemoteCommand::usage() const -{ - std::ostringstream oss; - - oss << "usage: " << sys::programName() << " " << m_name; - - /* Options summary */ - if (options().size() > 0) - oss << " [options...]"; - - /* Arguments summary */ - if (args().size() > 0) { - oss << " "; - - for (const auto &arg : args()) - oss << (arg.required() ? "" : "[") << arg.name() << (arg.required() ? "" : "]") << " "; - } - - /* Description */ - oss << "\n\n" << help() << "\n\n"; - - /* Options */ - if (options().size() > 0) { - oss << "Options:\n"; - - for (const auto &opt : options()) { - std::ostringstream optoss; - - /* Construct the line for the option in a single string to pad it correctly */ - optoss << " "; - optoss << (!opt.simpleKey().empty() ? ("-"s + opt.simpleKey() + " ") : " "); - optoss << (!opt.longKey().empty() ? ("--"s + opt.longKey() + " "s) : ""); - optoss << opt.arg(); - - /* Add it padded with spaces */ - oss << std::left << std::setw(28) << optoss.str(); - oss << opt.description() << "\n"; - } - } - - return oss.str(); -} - -unsigned RemoteCommand::min() const noexcept -{ - auto list = args(); - - return std::accumulate(list.begin(), list.end(), 0U, [] (unsigned i, const auto &arg) noexcept -> unsigned { - return i + (arg.required() ? 1 : 0); - }); -} - -unsigned RemoteCommand::max() const noexcept -{ - return (unsigned)args().size(); -} - -json::Value RemoteCommand::request(Irccdctl &, const RemoteCommandRequest &) const -{ - return json::object({}); -} - -json::Value RemoteCommand::exec(Irccd &, const json::Value &) const -{ - return json::object({}); -} - -void RemoteCommand::result(Irccdctl &, const json::Value &response) const -{ - auto it = response.find("error"); - - if (it != response.end() && it->isString()) - log::warning() << "irccdctl: " << it->toString() << std::endl; -} - -} // !irccd
--- a/lib/irccd/command/command.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,469 +0,0 @@ -/* - * command.h -- remote command - * - * 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_COMMAND_H -#define IRCCD_COMMAND_H - -#include <cassert> -#include <map> -#include <vector> - -#include <irccd/json.h> - -namespace irccd { - -class Irccd; -class Irccdctl; - -/** - * @brief Command line arguments to irccdctl. - * - * This class contains the resolved arguments from command line that can apply to the command. - */ -class RemoteCommandRequest { -public: - using Options = std::multimap<std::string, std::string>; - using Args = std::vector<std::string>; - -private: - Options m_options; - Args m_args; - -public: - /** - * Construct the request. - * - * @param options the options - * @param args the arguments - */ - inline RemoteCommandRequest(Options options, Args args) noexcept - : m_options(std::move(options)) - , m_args(std::move(args)) - { - } - - /** - * Get the arguments. - * - * @return the arguments - */ - inline const Args &args() const noexcept - { - return m_args; - } - - /** - * Get the options. - * - * @return the options - */ - inline const Options &options() const noexcept - { - return m_options; - } - - /** - * Get the number of arguments. - * - * @return the number of arguments - */ - inline unsigned length() const noexcept - { - return (unsigned)m_args.size(); - } - - /** - * Check if the request has the given option id. - * - * @param option the option id - * @return true if the option is available - */ - inline bool has(const std::string &option) const noexcept - { - return m_options.count(option) != 0; - } - - /** - * Get the argument at the specified index. - * - * @pre index < length() - * @param index the argument index - * @return the argument - */ - inline const std::string &arg(unsigned index) const noexcept - { - assert(index < m_args.size()); - - return m_args[index]; - } - - /** - * Get the argument or default value if not available. - * - * @param index the index - * @param defaultValue the value if index is out of range - * @return the argument - */ - inline std::string argOr(unsigned index, std::string defaultValue) const noexcept - { - return index < m_args.size() ? m_args[index] : defaultValue; - } - - /** - * Get the given option by its id. - * - * @pre has(key) - * @param key the option id - * @return the option - */ - inline const std::string &option(const std::string &key) const noexcept - { - assert(m_options.count(key) != 0); - - return m_options.find(key)->second; - } - - /** - * Get the given option by its id or defaultValue if not found. - * - * @param key the option id - * @return the option - */ - inline std::string optionOr(const std::string &key, std::string defaultValue) const noexcept - { - auto it = m_options.find(key); - - if (it == m_options.end()) - return defaultValue; - - return it->second; - } -}; - -/** - * @brief Invokable command. - * - * A remote command is a invokable command in the irccd daemon. You can register dynamically any remote command you - * like using Application::addCommand. - * - * The remote command will be usable directly from irccdctl without any other code. - * - * A remote command can have options and arguments. Options always come first, before arguments. - * - * The command workflow is defined as follow: - * - * 1. User wants to invoke a command, request() is called and return a JSON object containaing the request, it it send - * to the daemon. - * - * 2. The daemon receive the request and execute it using exec(). It returns a JSON object containint the request result - * or error if any. - * - * 3. Finally, the command receives the result in result() function and user can manipulate it. For convenience, the - * default implementation shows the error if any. - */ -class RemoteCommand { -public: - /** - * @brief Defines available options for this command. - */ - class Option; - - /** - * @brief Defines available arguments for this command. - */ - class Arg; - -private: - std::string m_name; - std::string m_category; - bool m_visible; - -public: - inline RemoteCommand(std::string name, std::string category, bool visible = true) noexcept - : m_name(std::move(name)) - , m_category(std::move(category)) - , m_visible(visible) - { - assert(!m_name.empty()); - assert(!m_category.empty()); - } - - /** - * Default destructor virtual. - */ - virtual ~RemoteCommand() = default; - - /** - * Return the command name, must not have spaces. - * - * @return the command name - */ - inline const std::string &name() const noexcept - { - return m_name; - } - - /** - * Get the command category. - * - * Irccdctl will sort commands by categories. - * - * @return the category - */ - inline const std::string &category() const noexcept - { - return m_category; - } - - /** - * Hide the command in non-verbose mode. - * - * @return true if the command should be visible in non-verbose mode - */ - inline bool visible() const noexcept - { - return m_visible; - } - - /** - * Return the command documentation usage. - * - * @return the usage - */ - std::string usage() const; - - /** - * Return the help message. - * - * @return the help message - */ - virtual std::string help() const = 0; - - /** - * Get the supported irccdctl options. - * - * @return the options - */ - virtual std::vector<Option> options() const - { - return {}; - } - - /** - * Get the supported arguments. - * - * @return the arguments - */ - virtual std::vector<Arg> args() const - { - return {}; - } - - /** - * Get the minimum number of arguments required. - * - * @return the minimum - */ - unsigned min() const noexcept; - - /** - * Get the maximum number of arguments required. - * - * @return the maximum - */ - unsigned max() const noexcept; - - /** - * Prepare a JSON request to the daemon. - * - * If the command is local and does not need to send anything to irccd's instance, return a null JSON value. - * - * The default implementation just send the command name with no arguments. - * - * @param irccdctl the irccdctl instance - * @param args the command line arguments and options - * @return the JSON object to send to the daemon - * @post the returned JSON value must be an object - */ - virtual json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const; - - /** - * Execute the command in the daemon. - * - * The user can return an object with any properties to forward to the client. Irccd will automatically - * add the command name and the appropriate status code. - * - * The default return an empty object which indicates success. - * - * If any exception is thrown from this function, it is forwarded to the client as error status. - * - * @param irccd the instance - * @param request the JSON request - * @return the response - */ - virtual json::Value exec(Irccd &irccd, const json::Value &request) const; - - /** - * What to do when receiving the response from irccd. - * - * This default implementation just check for an error string and shows it if any. - * - * @param irccdctl the irccdctl instane - * @param object the result - */ - virtual void result(Irccdctl &irccdctl, const json::Value &response) const; -}; - -/** - * @brief Option description for a command. - */ -class RemoteCommand::Option { -private: - std::string m_id; - std::string m_simple; - std::string m_long; - std::string m_arg; - std::string m_description; - -public: - /** - * Constructor an option description. - * - * Simple and long keys must not start with '-' or '--', they will be added automatically. - * - * If arg is not empty, the option takes an argument. - * - * @pre id must not be empty - * @pre at least simpleKey or longKey must not be empty - * @pre description must not be empty - * @param key the key the option key - * @param description the description - * @param flags the optional flags - */ - inline Option(std::string id, - std::string simpleKey, - std::string longKey, - std::string arg, - std::string description) noexcept - : m_id(std::move(id)) - , m_simple(std::move(simpleKey)) - , m_long(std::move(longKey)) - , m_arg(std::move(arg)) - , m_description(std::move(description)) - { - assert(!m_id.empty()); - assert(!m_simple.empty() || !m_long.empty()); - assert(!m_description.empty()); - } - - /** - * Get the id. - * - * @return the id - */ - inline const std::string &id() const noexcept - { - return m_id; - } - - /** - * Get the option key. - * - * @return the key - */ - inline const std::string &simpleKey() const noexcept - { - return m_simple; - } - - /** - * Get the long option. - * - * @return the long option - */ - inline const std::string &longKey() const noexcept - { - return m_long; - } - - /** - * Get the option description. - * - * @return the description - */ - inline const std::string &description() const noexcept - { - return m_description; - } - - /** - * Get the option argument name. - * - * @return the argument name if any - */ - inline const std::string &arg() const noexcept - { - return m_arg; - } -}; - -/** - * @brief Argument description for command. - */ -class RemoteCommand::Arg { -private: - std::string m_name; - bool m_required; - -public: - /** - * Construct an argument. - * - * @param name the name - * @param required true if the argument is required - */ - inline Arg(std::string name, bool required) noexcept - : m_name(std::move(name)) - , m_required(required) - { - } - - /** - * Get the argument name. - * - * @return the name - */ - inline const std::string &name() const noexcept - { - return m_name; - } - - /** - * Tells if the argument is required. - * - * @return true if required - */ - inline bool required() const noexcept - { - return m_required; - } -}; - -} // !irccd - -#endif // !IRCCD_COMMAND_H
--- a/lib/irccd/command/help.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/* - * command-help.cpp -- implementation of irccdctl help - * - * 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/irccdctl.h> -#include <irccd/logger.h> - -#include "help.h" - -namespace irccd { - -namespace command { - -Help::Help() - : RemoteCommand("help", "General") -{ -} - -std::vector<RemoteCommand::Arg> Help::args() const -{ - return {{ "command", true }}; -} - -std::string Help::help() const -{ - return "Get help about a command."; -} - -json::Value Help::request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const -{ - auto it = irccdctl.commands().find(args.arg(0U)); - - if (it == irccdctl.commands().end()) - log::warning() << "there is no command named: " << args.arg(0U) << std::endl; - else - log::warning() << it->second->usage() << std::flush; - - return nullptr; -} - -} // !command - -} // !irccd
--- a/lib/irccd/command/help.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -/* - * command-help.h -- implementation of irccdctl help - * - * 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 _IRCCDCTL_COMMAND_HELP_H_ -#define _IRCCDCTL_COMMAND_HELP_H_ - -/** - * @file command-help.h - * @brief Implementation of irccdctl help. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class Help - * @brief Implementation of irccdctl help. - */ -class Help : public RemoteCommand { -public: - Help(); - - std::vector<Arg> args() const override; - - /** - * @copydoc RemoteCommand::help - */ - std::string help() const override; - - /** - * @copydoc RemoteCommand::request - */ - json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCDCTL_COMMAND_HELP_H_
--- a/lib/irccd/command/plugin-info.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,86 +0,0 @@ -/* - * plugin-info.cpp -- implementation of plugin-info command - * - * 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 <iostream> - -#include <irccd-config.h> - -#include <irccd/irccd.h> -#include <irccd/command/plugin-info.h> - -namespace irccd { - -namespace command { - -PluginInfo::PluginInfo() - : RemoteCommand("plugin-info", "Plugins") -{ -} - -std::string PluginInfo::help() const -{ - return "Get plugin information."; -} - -std::vector<RemoteCommand::Arg> PluginInfo::args() const -{ - return {{ "plugin", true }}; -} - -json::Value PluginInfo::request(Irccdctl &, const RemoteCommandRequest &args) const -{ - return json::object({{ "plugin", args.arg(0) }}); -} - -json::Value PluginInfo::exec(Irccd &irccd, const json::Value &request) const -{ -#if defined(WITH_JS) - auto plugin = irccd.requirePlugin(request.at("plugin").toString()); - - return json::object({ - { "author", plugin->info().author }, - { "license", plugin->info().license }, - { "summary", plugin->info().summary }, - { "version", plugin->info().version } - }); -#else - (void)irccd; - (void)object; - - throw std::runtime_error("JavaScript disabled"); -#endif -} - -void PluginInfo::result(Irccdctl &irccdctl, const json::Value &result) const -{ - RemoteCommand::result(irccdctl, result); - - /* Plugin information */ - if (result.valueOr("status", false).toBool()) { - std::cout << std::boolalpha; - std::cout << "Author : " << result.valueOr("author", "").toString(true) << std::endl; - std::cout << "License : " << result.valueOr("license", "").toString(true) << std::endl; - std::cout << "Summary : " << result.valueOr("summary", "").toString(true) << std::endl; - std::cout << "Version : " << result.valueOr("version", "").toString(true) << std::endl; - } -} - -} // !command - -} // !irccd -
--- a/lib/irccd/command/plugin-info.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -/* - * plugin-info.h -- implementation of plugin-info command - * - * 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_PLUGIN_INFO_H_ -#define _IRCCD_PLUGIN_INFO_H_ - -/** - * @file plugin-info.h - * @brief Implementation of plugin-info transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class PluginInfo - * @brief Implementation of plugin-info transport command. - */ -class PluginInfo : public RemoteCommand { -public: - /** - * Constructor. - */ - PluginInfo(); - - /** - * @copydoc RemoteCommand::help - */ - std::string help() const override; - - /** - * @copydoc RemoteCommand::args - */ - std::vector<Arg> args() const override; - - /** - * @copydoc RemoteCommand::request - */ - json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; - - /** - * @copydoc RemoteCommand::exec - */ - json::Value exec(Irccd &irccd, const json::Value &request) const override; - - /** - * @copydoc RemoteCommand::result - */ - void result(Irccdctl &irccdctl, const json::Value &object) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_PLUGIN_INFO_H_
--- a/lib/irccd/command/plugin-list.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -/* - * command-plugin-list.cpp -- implementation of plugin-list transport command - * - * 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 <iostream> - -#include <irccd-config.h> - -#include <irccd/irccd.h> -#include <irccd/command/plugin-list.h> - -namespace irccd { - -namespace command { - -PluginList::PluginList() - : RemoteCommand("plugin-list", "Plugins") -{ -} - -std::string PluginList::help() const -{ - return "Get the list of loaded plugins."; -} - -json::Value PluginList::exec(Irccd &irccd, const json::Value &request) const -{ -#if defined(WITH_JS) - json::Value response = RemoteCommand::exec(irccd, request); - json::Value list = json::array({}); - - for (const auto &plugin : irccd.plugins()) - list.append(plugin.first); - - response.insert("list", std::move(list)); - - return response; -#else - (void)irccd; - (void)tc; - - throw std::runtime_error("JavaScript disabled"); -#endif -} - -void PluginList::result(Irccdctl &irccdctl, const json::Value &object) const -{ - RemoteCommand::result(irccdctl, object); - - for (const auto &n : object.valueOr("list", json::Type::Array, json::array({}))) - std::cout << n.toString() << std::endl; -} - -} // !command - -}
--- a/lib/irccd/command/plugin-list.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -/* - * command-plugin-list.h -- implementation of plugin-list transport command - * - * 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_COMMAND_PLUGIN_LIST_H_ -#define _IRCCD_COMMAND_PLUGIN_LIST_H_ - -/** - * @file command-plugin-list.h - * @brief Implementation of plugin-list transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class PluginList - * @brief Implementation of plugin-list transport command. - */ -class PluginList : public RemoteCommand { -public: - /** - * Constructor. - */ - PluginList(); - - /** - * @copydoc RemoteCommand::help - */ - std::string help() const override; - - /** - * @copydoc RemoteCommand::exec - */ - json::Value exec(Irccd &irccd, const json::Value &request) const override; - - /** - * @copydoc RemoteCommand::result - */ - void result(Irccdctl &irccdctl, const json::Value &object) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_COMMAND_PLUGIN_LIST_H_
--- a/lib/irccd/command/plugin-load.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -/* - * command-plugin-load.cpp -- implementation of plugin-load transport command - * - * 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-config.h> -#include <irccd/irccd.h> -#include <irccd/command/plugin-load.h> - -namespace irccd { - -namespace command { - -PluginLoad::PluginLoad() - : RemoteCommand("plugin-load", "Plugins") -{ -} - -std::string PluginLoad::help() const -{ - return "Load a plugin."; -} - -std::vector<RemoteCommand::Arg> PluginLoad::args() const -{ - return {{ "plugin", true }}; -} - -json::Value PluginLoad::exec(Irccd &irccd, const json::Value &request) const -{ -#if defined(WITH_JS) - auto name = request.at("plugin").toString(); - - irccd.loadPlugin(name, name, true); - - return RemoteCommand::exec(irccd, request); -#else - (void)irccd; - (void)tc; - (void)object; - - throw std::runtime_error("JavaScript disabled"); -#endif -} - -} // !command - -} // !irccd
--- a/lib/irccd/command/plugin-load.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -/* - * command-plugin-load.h -- implementation of plugin-load transport command - * - * 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_COMMAND_PLUGIN_LOAD_H_ -#define _IRCCD_COMMAND_PLUGIN_LOAD_H_ - -/** - * @file plugin-load.h - * @brief Implementation of plugin-load transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class PluginLoad - * @brief Implementation of plugin-load transport command. - */ -class PluginLoad : public RemoteCommand { -public: - /** - * Constructor. - */ - PluginLoad(); - - /** - * @copydoc TransportCommand::help - */ - std::string help() const override; - - /** - * @copydoc TransportCommand::args - */ - std::vector<Arg> args() const override; - - /** - * @copydoc TransportCommand::exec - */ - json::Value exec(Irccd &irccd, const json::Value &object) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_COMMAND_PLUGIN_LOAD_H_
--- a/lib/irccd/command/plugin-reload.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/* - * command-plugin-reload.cpp -- implementation of plugin-reload transport command - * - * 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-config.h> -#include <irccd/irccd.h> -#include <irccd/command/plugin-reload.h> - -namespace irccd { - -namespace command { - -PluginReload::PluginReload() - : RemoteCommand("plugin-reload", "Plugins") -{ -} - -std::string PluginReload::help() const -{ - return "Reload a plugin."; -} - -std::vector<RemoteCommand::Arg> PluginReload::args() const -{ - return {{ "plugin", true }}; -} - -json::Value PluginReload::exec(Irccd &irccd, const json::Value &request) const -{ -#if defined(WITH_JS) - irccd.requirePlugin(request.at("plugin").toString())->onReload(); - - return RemoteCommand::exec(irccd, request); -#else - (void)irccd; - (void)tc; - (void)object; - - throw std::runtime_error("JavaScript disabled"); -#endif -} - -} // !command - -} // !irccd
--- a/lib/irccd/command/plugin-reload.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -/* - * plugin-reload.h -- implementation of plugin-reload transport command - * - * 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_COMMAND_PLUGIN_RELOAD_H_ -#define _IRCCD_COMMAND_PLUGIN_RELOAD_H_ - -/** - * @file command-plugin-load.h - * @brief Implementation of plugin-reload transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class PluginReload - * @brief Implementation of plugin-reload transport command. - */ -class PluginReload : public RemoteCommand { -public: - /** - * Constructor. - */ - PluginReload(); - - /** - * @copydoc TransportCommand::help - */ - std::string help() const override; - - /** - * @copydoc TransportCommand::args - */ - std::vector<Arg> args() const override; - - /** - * @copydoc TransportCommand::exec - */ - json::Value exec(Irccd &irccd, const json::Value &object) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_COMMAND_PLUGIN_RELOAD_H_
--- a/lib/irccd/command/plugin-unload.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/* - * plugin-unload.cpp -- implementation of plugin-unload transport command - * - * 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-config.h> -#include <irccd/irccd.h> -#include <irccd/command/plugin-unload.h> - -namespace irccd { - -namespace command { - -PluginUnload::PluginUnload() - : RemoteCommand("plugin-unload", "Plugins") -{ -} - -std::string PluginUnload::help() const -{ - return "Unload a plugin."; -} - -std::vector<RemoteCommand::Arg> PluginUnload::args() const -{ - return {{ "plugin", true }}; -} - -json::Value PluginUnload::exec(Irccd &irccd, const json::Value &request) const -{ -#if defined(WITH_JS) - irccd.unloadPlugin(request.at("plugin").toString()); - - return RemoteCommand::exec(irccd, request); -#else - (void)irccd; - (void)tc; - (void)object; - - throw std::runtime_error("JavaScript disabled"); -#endif -} - -} // !command - -} // !irccd
--- a/lib/irccd/command/plugin-unload.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -/* - * plugin-unload.h -- implementation of plugin-unload transport command - * - * 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_COMMAND_PLUGIN_UNLOAD_H_ -#define _IRCCD_COMMAND_PLUGIN_UNLOAD_H_ - -/** - * @file plugin-unload.h - * @brief Implementation of plugin-unload transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class PluginUnload - * @brief Implementation of plugin-unload transport command. - */ -class PluginUnload : public RemoteCommand { -public: - /** - * Constructor. - */ - PluginUnload(); - - /** - * @copydoc TransportCommand::help - */ - std::string help() const override; - - /** - * @copydoc TransportCommand::args - */ - std::vector<Arg> args() const override; - - /** - * @copydoc RemoteCommand::exec - */ - json::Value exec(Irccd &irccd, const json::Value &object) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_COMMAND_PLUGIN_UNLOAD_H_
--- a/lib/irccd/command/server-cmode.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -/* - * server-cmode.cpp -- implementation of server-cmode transport command - * - * 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/irccd.h> -#include <irccd/command/server-cmode.h> - -namespace irccd { - -namespace command { - -ServerChannelMode::ServerChannelMode() - : RemoteCommand("server-cmode", "Server") -{ -} - -std::string ServerChannelMode::help() const -{ - return ""; -} - -std::vector<RemoteCommand::Arg> ServerChannelMode::args() const -{ - return { - { "server", true }, - { "channel", true }, - { "mode", true } - }; -} - -json::Value ServerChannelMode::exec(Irccd &irccd, const json::Value &request) const -{ - irccd.requireServer(request.at("server").toString())->cmode( - request.at("channel").toString(), - request.at("mode").toString() - ); - - return RemoteCommand::exec(irccd, request); -} - -} // !command - -} // !irccd -
--- a/lib/irccd/command/server-cmode.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -/* - * server-cmode.h -- implementation of server-cmode transport command - * - * 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_COMMAND_SERVER_CHANNEL_MODE_H_ -#define _IRCCD_COMMAND_SERVER_CHANNEL_MODE_H_ - -/** - * @file server-cmode.h - * @brief Implementation of server-cmode transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class ServerChannelMode - * @brief Implementation of server-cmode transport command. - */ -class ServerChannelMode : public RemoteCommand { -public: - /** - * Constructor. - */ - ServerChannelMode(); - - /** - * @copydoc RemoteCommand::help - */ - std::string help() const override; - - /** - * @copydoc TransportCommand::args - */ - std::vector<Arg> args() const override; - - /** - * @copydoc TransportCommand::exec - */ - json::Value exec(Irccd &irccd, const json::Value &object) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_COMMAND_SERVER_CHANNEL_MODE_H_
--- a/lib/irccd/command/server-cnotice.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -/* - * server-cnotice.cpp -- implementation of server-cnotice transport command - * - * 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/irccd.h> - -#include "server-cnotice.h" - -namespace irccd { - -namespace command { - -ServerChannelNotice::ServerChannelNotice() - : RemoteCommand("server-cnotice", "Server") -{ -} - -std::string ServerChannelNotice::help() const -{ - return ""; -} - -std::vector<RemoteCommand::Arg> ServerChannelNotice::args() const -{ - return { - { "server", true }, - { "channel", true }, - { "message", true } - }; -} - -json::Value ServerChannelNotice::exec(Irccd &irccd, const json::Value &request) const -{ - irccd.requireServer(request.at("server").toString())->cnotice( - request.at("channel").toString(), - request.at("message").toString() - ); - - return RemoteCommand::exec(irccd, request); -} - -} // !command - -} // !irccd
--- a/lib/irccd/command/server-cnotice.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -/* - * server-cnotice.h -- implementation of server-cnotice transport command - * - * 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_COMMAND_SERVER_CNOTICE_H_ -#define _IRCCD_COMMAND_SERVER_CNOTICE_H_ - -/** - * @file server-cnotice.h - * @brief Implementation of server-cnotice transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class ServerChannelNotice - * @brief Implementation of server-cnotice transport command. - * - * Send a channel notice to the specified channel. - * - * { - * "command": "server-cnotice", - * "server": "the server name", - * "channel": "name", - * "message": "the message" - * } - */ -class ServerChannelNotice : public RemoteCommand { -public: - /** - * Constructor. - */ - ServerChannelNotice(); - - /** - * @copydoc RemoteCommand::help - */ - std::string help() const override; - - /** - * @copydoc TransportCommand::args - */ - std::vector<Arg> args() const override; - - /** - * @copydoc TransportCommand::exec - */ - json::Value exec(Irccd &irccd, const json::Value &request) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_COMMAND_SERVER_CNOTICE_H_
--- a/lib/irccd/command/server-connect.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,163 +0,0 @@ -/* - * command-server-connect.cpp -- implementation of server-connect transport command - * - * 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 <limits> - -#include <irccd/irccd.h> -#include <irccd/server.h> -#include <irccd/util.h> - -#include "server-connect.h" - -namespace irccd { - -namespace command { - -namespace { - -std::string readInfoName(const json::Value &object) -{ - auto it = object.find("name"); - - if (it == object.end()) - throw std::invalid_argument("missing 'name' property"); - if (!it->isString() || !util::isIdentifierValid(it->toString())) - throw std::invalid_argument("invalid server name"); - - return it->toString(); -} - -std::string readInfoHost(const json::Value &object) -{ - auto it = object.find("host"); - - if (it == object.end()) - throw std::invalid_argument("missing 'host' property"); - if (!it->isString()) - throw std::invalid_argument("invalid host"); - - return it->toString(); -} - -std::uint16_t readInfoPort(const json::Value &object) -{ - auto it = object.find("port"); - uint16_t port = 6667; - - if (it != object.end()) - if (it->isInt() && it->toInt() >= 0 && it->toInt() <= std::numeric_limits<std::uint16_t>::max()) - port = static_cast<std::uint16_t>(it->toInt()); - - return port; -} - -ServerInfo readInfo(const json::Value &object) -{ - ServerInfo info; - - /* Mandatory */ - info.name = readInfoName(object); - info.host = readInfoHost(object); - - /* Optional */ - info.port = readInfoPort(object); - - if (object.valueOr("ssl", json::Type::Boolean, false).toBool()) -#if defined(WITH_SSL) - info.flags |= ServerInfo::Ssl; -#else - throw std::invalid_argument("ssl is disabled"); -#endif - - if (object.valueOr("sslVerify", json::Type::Boolean, false).toBool()) - info.flags |= ServerInfo::SslVerify; - - return info; -} - -ServerIdentity readIdentity(const json::Value &object) -{ - ServerIdentity identity; - - identity.nickname = object.valueOr("nickname", json::Type::String, identity.nickname).toString(); - identity.realname = object.valueOr("realname", json::Type::String, identity.realname).toString(); - identity.username = object.valueOr("username", json::Type::String, identity.username).toString(); - identity.ctcpversion = object.valueOr("ctcpVersion", json::Type::String, identity.ctcpversion).toString(); - - return identity; -} - -ServerSettings readSettings(const json::Value &object) -{ - ServerSettings settings; - - settings.command = object.valueOr("commandChar", json::Type::String, settings.command).toString(); - settings.recotries = object.valueOr("reconnectTries", json::Type::Int, settings.recotries).toInt(); - settings.recotimeout = object.valueOr("reconnectTimeout", json::Type::Int, settings.recotimeout).toInt(); - - return settings; -} - -} // !namespace - -ServerConnect::ServerConnect() - : RemoteCommand("server-connect", "Server") -{ -} - -std::string ServerConnect::help() const -{ - return "Connect to a server."; -} - -std::vector<RemoteCommand::Option> ServerConnect::options() const -{ - return { - { "command", "c", "command", "char", "command character to use" }, - { "nickname", "n", "nickname", "nickname", "nickname to use" }, - { "realname", "r", "realname", "realname", "realname to use" }, - { "sslverify", "S", "ssl-verify", "", "verify SSL" }, - { "ssl", "s", "ssl", "", "connect with SSL" }, - { "username", "u", "username", "", "username to use" }, - }; -} - -std::vector<RemoteCommand::Arg> ServerConnect::args() const -{ - return { - { "id", true }, - { "host", true }, - { "port", false } - }; -} - -json::Value ServerConnect::exec(Irccd &irccd, const json::Value &request) const -{ - auto server = std::make_shared<Server>(readInfo(request), readIdentity(request), readSettings(request)); - - if (irccd.hasServer(server->info().name)) - throw std::invalid_argument("server '" + server->info().name + "' already exists"); - - irccd.addServer(std::move(server)); - - return RemoteCommand::exec(irccd, request); -} - -} // !command - -} // !irccd
--- a/lib/irccd/command/server-connect.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -/* - * server-connect.h -- implementation of server-connect transport command - * - * 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_COMMAND_SERVER_CONNECT_H_ -#define _IRCCD_COMMAND_SERVER_CONNECT_H_ - -/** - * @file server-connect.h - * @brief Implementation of server-connect transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class ServerConnect - * @brief Implementation of server-connect transport command. - */ -class ServerConnect : public RemoteCommand { -public: - /** - * Constructor. - */ - ServerConnect(); - - /** - * @copydoc RemoteCommand::help - */ - std::string help() const override; - - /** - * @copydoc TransportCommand::options - */ - std::vector<Option> options() const override; - - /** - * @copydoc TransportCommand::args - */ - std::vector<Arg> args() const override; - - /** - * @copydoc TransportCommand::exec - */ - json::Value exec(Irccd &irccd, const json::Value &request) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_COMMAND_SERVER_CONNECT_H_
--- a/lib/irccd/command/server-disconnect.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -/* - * command-server-disconnect.cpp -- implementation of server-disconnect transport command - * - * 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/irccd.h> - -#include "server-disconnect.h" - -namespace irccd { - -namespace command { - -ServerDisconnect::ServerDisconnect() - : RemoteCommand("server-disconnect", "Server") -{ -} - -std::string ServerDisconnect::help() const -{ - return ""; -} - -std::vector<RemoteCommand::Arg> ServerDisconnect::args() const -{ - return {{ "server", false }}; -} - -json::Value ServerDisconnect::exec(Irccd &irccd, const json::Value &request) const -{ - auto it = request.find("server"); - - if (it == request.end()) - irccd.clearServers(); - else - irccd.removeServer(it->toString()); - - return RemoteCommand::exec(irccd, request); -} - -} // !command - -} // !irccd
--- a/lib/irccd/command/server-disconnect.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -/* - * server-disconnect.h -- implementation of server-disconnect transport command - * - * 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_COMMAND_SERVER_DISCONNECT_H_ -#define _IRCCD_COMMAND_SERVER_DISCONNECT_H_ - -/** - * @file server-disconnect.h - * @brief Implementation of server-disconnect transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class ServerDisconnect - * @brief Implementation of server-disconnect transport command. - */ -class ServerDisconnect : public RemoteCommand { -public: - /** - * Constructor. - */ - ServerDisconnect(); - - /** - * @copydoc RemoteCommand::help - */ - std::string help() const override; - - std::vector<Arg> args() const override; - - /** - * @copydoc TransportCommand::exec - */ - json::Value exec(Irccd &irccd, const json::Value &object) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_COMMAND_SERVER_DISCONNECT_H_
--- a/lib/irccd/command/server-info.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,114 +0,0 @@ -/* - * command-server-info.cpp -- implementation of server-info transport command - * - * 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 <iostream> - -#include <irccd/irccd.h> - -#include "server-info.h" - -namespace irccd { - -namespace command { - -ServerInfo::ServerInfo() - : RemoteCommand("server-info", "Server") -{ -} - -std::string ServerInfo::help() const -{ - return ""; -} - -std::vector<RemoteCommand::Arg> ServerInfo::args() const -{ - return {{ "server", true }}; -} - -json::Value ServerInfo::request(Irccdctl &, const RemoteCommandRequest &args) const -{ - return json::object({ - { "server", args.args()[0] }, - { "target", args.args()[1] }, - { "channel", args.args()[2] } - }); -} - -json::Value ServerInfo::exec(Irccd &irccd, const json::Value &request) const -{ - auto server = irccd.requireServer(request.at("server").toString()); - auto response = RemoteCommand::exec(irccd, request); - - /* General stuff */ - response.insert("name", server->info().name); - response.insert("host", server->info().host); - response.insert("port", server->info().port); - response.insert("nickname", server->identity().nickname); - response.insert("username", server->identity().username); - response.insert("realname", server->identity().realname); - - /* Optional stuff */ - if (server->info().flags & irccd::ServerInfo::Ipv6) - response.insert("ipv6", true); - if (server->info().flags & irccd::ServerInfo::Ssl) - response.insert("ssl", true); - if (server->info().flags & irccd::ServerInfo::SslVerify) - response.insert("sslVerify", true); - - /* Channel list */ - auto channels = json::array({}); - - for (const auto &c : server->settings().channels) - channels.append(c.name); - - response.insert("channels", std::move(channels)); - - return response; -} - -void ServerInfo::result(Irccdctl &irccdctl, const json::Value &response) const -{ - RemoteCommand::result(irccdctl, response); - - /* Server information */ - std::cout << std::boolalpha; - std::cout << "Name : " << response.valueOr("name", "").toString(true) << std::endl; - std::cout << "Host : " << response.valueOr("host", "").toString(true) << std::endl; - std::cout << "Port : " << response.valueOr("port", "").toString(true) << std::endl; - std::cout << "Ipv6 : " << response.valueOr("ipv6", "").toString(true) << std::endl; - std::cout << "SSL : " << response.valueOr("ssl", "").toString(true) << std::endl; - std::cout << "SSL verified : " << response.valueOr("sslVerify", "").toString(true) << std::endl; - - /* Channels */ - std::cout << "Channels : "; - - for (const json::Value &v : response.valueOr("channels", json::Type::Array, json::array({}))) - std::cout << v.toString() << " "; - - std::cout << std::endl; - - /* Identity */ - std::cout << "Nickname : " << response.valueOr("nickname", "").toString(true) << std::endl; - std::cout << "User name : " << response.valueOr("username", "").toString(true) << std::endl; - std::cout << "Real name : " << response.valueOr("realname", "").toString(true) << std::endl; -} - -} // !command - -} // !irccd
--- a/lib/irccd/command/server-info.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -/* - * command-server-info.h -- implementation of server-info transport command - * - * 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_COMMAND_SERVER_INFO_H_ -#define _IRCCD_COMMAND_SERVER_INFO_H_ - -/** - * @file command-server-info.h - * @brief Implementation of server-info transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class ServerInfo - * @brief Implementation of server-info transport command. - */ -class ServerInfo : public RemoteCommand { -public: - ServerInfo(); - - /** - * @copydoc RemoteCommand::help - */ - std::string help() const override; - - /** - * @copydoc TransportCommand::args - */ - std::vector<Arg> args() const override; - - json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; - - /** - * @copydoc TransportCommand::exec - */ - json::Value exec(Irccd &irccd, const json::Value &object) const override; - - void result(Irccdctl &irccdctl, const json::Value &response) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_COMMAND_SERVER_INFO_H_
--- a/lib/irccd/command/server-invite.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -/* - * command-server-invite.cpp -- implementation of server-invite transport command - * - * 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/irccd.h> - -#include "server-invite.h" - -namespace irccd { - -namespace command { - -ServerInvite::ServerInvite() - : RemoteCommand("server-invite", "Server") -{ -} - -std::string ServerInvite::help() const -{ - return ""; -} - -std::vector<RemoteCommand::Arg> ServerInvite::args() const -{ - return { - { "server", true }, - { "nickname", true }, - { "channel", true } - }; -} - -json::Value ServerInvite::request(Irccdctl &, const RemoteCommandRequest &args) const -{ - return json::object({ - { "server", args.args()[0] }, - { "target", args.args()[1] }, - { "channel", args.args()[2] } - }); -} - -json::Value ServerInvite::exec(Irccd &irccd, const json::Value &request) const -{ - irccd.requireServer( - request.at("server").toString())->invite( - request.at("target").toString(), - request.at("channel").toString() - ); - - return RemoteCommand::exec(irccd, request); -} - -} // !command - -} // !irccd -
--- a/lib/irccd/command/server-invite.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ -/* - * server-invite.h -- implementation of server-invite transport command - * - * 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_COMMAND_SERVER_INVITE_H_ -#define _IRCCD_COMMAND_SERVER_INVITE_H_ - -/** - * @file server-invite.h - * @brief Implementation of server-invite transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class ServerInvite - * @brief Implementation of server-invite transport command. - */ -class ServerInvite : public RemoteCommand { -public: - ServerInvite(); - - /** - * @copydoc RemoteCommand::help - */ - std::string help() const override; - - /** - * @copydoc RemoteCommand::args - */ - std::vector<Arg> args() const override; - - /** - * @copydoc RemoteCommand::request - */ - json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; - - /** - * @copydoc TransportCommand::exec - */ - json::Value exec(Irccd &irccd, const json::Value &request) const override; - - -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_COMMAND_SERVER_INVITE_H_
--- a/lib/irccd/command/server-join.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +0,0 @@ -/* - * server-join.cpp -- implementation of server-join transport command - * - * 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/irccd.h> - -#include "server-join.h" - -namespace irccd { - -namespace command { - -ServerJoin::ServerJoin() - : RemoteCommand("server-join", "Server") -{ -} - -std::string ServerJoin::help() const -{ - return ""; -} - -std::vector<RemoteCommand::Arg> ServerJoin::args() const -{ - return { - { "server", true }, - { "channel", true }, - { "password", false } - }; -} - -json::Value ServerJoin::request(Irccdctl &, const RemoteCommandRequest &args) const -{ - auto req = json::object({ - { "server", args.args()[0] }, - { "channel", args.args()[1] } - }); - - if (args.length() == 3) - req.insert("password", args.args()[2]); - - return req; -} - -json::Value ServerJoin::exec(Irccd &irccd, const json::Value &request) const -{ - irccd.requireServer( - request.at("server").toString())->join( - request.at("channel").toString(), - request.valueOr("password", json::Type::String, "").toString() - ); - - return RemoteCommand::exec(irccd, request); -} - -} // !command - -} // !irccd
--- a/lib/irccd/command/server-join.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/* - * server-join.h -- implementation of server-join transport command - * - * 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_COMMAND_SERVER_JOIN_H_ -#define _IRCCD_COMMAND_SERVER_JOIN_H_ - -/** - * @file server-join.h - * @brief Implementation of server-join transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class ServerJoin - * @brief Implementation of server-join transport command. - */ -class ServerJoin : public RemoteCommand { -public: - ServerJoin(); - - /** - * @copydoc RemoteCommand::help - */ - std::string help() const override; - - /** - * @copydoc RemoteCommand::args - */ - std::vector<Arg> args() const override; - - /** - * @copydoc RemoteCommand::request - */ - json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; - - /** - * @copydoc RemoteCommand::exec - */ - json::Value exec(Irccd &irccd, const json::Value &request) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_COMMAND_SERVER_JOIN_H_
--- a/lib/irccd/command/server-kick.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -/* - * command-server-kick.cpp -- implementation of server-kick transport command - * - * 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/irccd.h> - -#include "server-kick.h" - -namespace irccd { - -namespace command { - -ServerKick::ServerKick() - : RemoteCommand("server-kick", "Server") -{ -} - -std::string ServerKick::help() const -{ - return ""; -} - -std::vector<RemoteCommand::Arg> ServerKick::args() const -{ - return { - { "server", true }, - { "target", true }, - { "channel", true }, - { "reason", false } - }; -} - -json::Value ServerKick::request(Irccdctl &, const RemoteCommandRequest &args) const -{ - auto req = json::object({ - { "server", args.arg(0) }, - { "target", args.arg(1) }, - { "channel", args.arg(2) } - }); - - if (args.length() == 4) - req.insert("reason", args.arg(3)); - - return req; -} - -json::Value ServerKick::exec(Irccd &irccd, const json::Value &request) const -{ - irccd.requireServer(request.at("server").toString())->kick( - request.at("target").toString(), - request.at("channel").toString(), - request.valueOr("reason", json::Type::String, "").toString() - ); - - return RemoteCommand::exec(irccd, request); -} - -} // !command - -} // !irccd
--- a/lib/irccd/command/server-kick.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/* - * server-kick.h -- implementation of server-kick transport command - * - * 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_COMMAND_SERVER_KICK_H_ -#define _IRCCD_COMMAND_SERVER_KICK_H_ - -/** - * @file server-kick.h - * @brief Implementation of server-kick transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class ServerKick - * @brief Implementation of server-kick transport command. - */ -class ServerKick : public RemoteCommand { -public: - ServerKick(); - - /** - * @copydoc RemoteCommand::help - */ - std::string help() const override; - - /** - * @copydoc RemoteCommand::args - */ - std::vector<Arg> args() const override; - - /** - * @copydoc RemoteCommand::request - */ - json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; - - /** - * @copydoc RemoteCommand::exec - */ - json::Value exec(Irccd &irccd, const json::Value &request) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_COMMAND_SERVER_KICK_H_
--- a/lib/irccd/command/server-list.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -/* - * server-list.cpp -- implementation of server-list transport command - * - * 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 <iostream> - -#include <irccd/irccd.h> - -#include "server-list.h" - -namespace irccd { - -namespace command { - -ServerList::ServerList() - : RemoteCommand("server-list", "Server") -{ -} - -std::string ServerList::help() const -{ - return ""; -} - -json::Value ServerList::exec(Irccd &irccd, const json::Value &) const -{ - auto json = json::object({}); - auto list = json::array({}); - - for (const auto &pair : irccd.servers()) - list.append(pair.first); - - json.insert("list", std::move(list)); - - return json; -} - -void ServerList::result(Irccdctl &, const json::Value &response) const -{ - for (const auto &n : response.valueOr("list", json::Type::Array, json::array({}))) - std::cout << n.toString() << std::endl; -} - -} // !command - -} // !irccd
--- a/lib/irccd/command/server-list.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -/* - * server-list.h -- implementation of server-list transport command - * - * 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_COMMAND_SERVER_LIST_H_ -#define _IRCCD_COMMAND_SERVER_LIST_H_ - -/** - * @file command-server-list.h - * @brief Implementation of server-list transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class ServerList - * @brief Implementation of server-list transport command. - */ -class ServerList : public RemoteCommand { -public: - ServerList(); - - /** - * @copydoc RemoteCommand::help - */ - std::string help() const override; - - json::Value exec(Irccd &irccd, const json::Value &request) const override; - - void result(Irccdctl &irccdctl, const json::Value &response) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_COMMAND_SERVER_LIST_H_
--- a/lib/irccd/command/server-me.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -/* - * server-me.cpp -- implementation of server-me transport command - * - * 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/irccd.h> - -#include "server-me.h" - -namespace irccd { - -namespace command { - -ServerMe::ServerMe() - : RemoteCommand("server-me", "Server") -{ -} - -std::string ServerMe::help() const -{ - return ""; -} - -std::vector<RemoteCommand::Arg> ServerMe::args() const -{ - return { - { "server", true }, - { "target", true }, - { "message", true } - }; -} - -json::Value ServerMe::request(Irccdctl &, const RemoteCommandRequest &args) const -{ - return json::object({ - { "server", args.arg(0) }, - { "target", args.arg(1) }, - { "message", args.arg(2) } - }); -} - -json::Value ServerMe::exec(Irccd &irccd, const json::Value &request) const -{ - irccd.requireServer(request.at("server").toString())->me( - request.at("target").toString(), - request.at("message").toString() - ); - - return nullptr; -} - -} // !command - -} // !irccd
--- a/lib/irccd/command/server-me.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/* - * server-me.h -- implementation of server-me transport command - * - * 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_COMMAND_SERVER_ME_H_ -#define _IRCCD_COMMAND_SERVER_ME_H_ - -/** - * @file command-.h - * @brief Implementation of transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class ServerMe - * @brief Implementation of server-me transport command. - */ -class ServerMe : public RemoteCommand { -public: - ServerMe(); - - /** - * @copydoc RemoteCommand::help - */ - std::string help() const override; - - /** - * @copydoc RemoteCommand::args - */ - std::vector<Arg> args() const override; - - /** - * @copydoc RemoteCommand::request - */ - json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; - - /** - * @copydoc RemoteCommand::exec - */ - json::Value exec(Irccd &irccd, const json::Value &request) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_COMMAND_SERVER_ME_H_
--- a/lib/irccd/command/server-message.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -/* - * server-message.cpp -- implementation of server-message transport command - * - * 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/irccd.h> - -#include "server-message.h" - -namespace irccd { - -namespace command { - -ServerMessage::ServerMessage() - : RemoteCommand("server-message", "Server") -{ -} - -std::string ServerMessage::help() const -{ - return ""; -} - -std::vector<RemoteCommand::Arg> ServerMessage::args() const -{ - return { - { "server", true }, - { "target", true }, - { "message", true } - }; -} - -json::Value ServerMessage::request(Irccdctl &, const RemoteCommandRequest &args) const -{ - return json::object({ - { "server", args.arg(0) }, - { "target", args.arg(1) }, - { "message", args.arg(2) } - }); -} - -json::Value ServerMessage::exec(Irccd &irccd, const json::Value &request) const -{ - irccd.requireServer(request.at("server").toString())->me( - request.at("target").toString(), - request.at("message").toString() - ); - - return nullptr; -} - -} // !command - -} // !irccd
--- a/lib/irccd/command/server-message.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/* - * server-message.h -- implementation of server-message transport command - * - * 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_COMMAND_SERVER_MESSAGE_H_ -#define _IRCCD_COMMAND_SERVER_MESSAGE_H_ - -/** - * @file command-server-message.h - * @brief Implementation of server-message transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class ServerMessage - * @brief Implementation of server-message transport command. - */ -class ServerMessage : public RemoteCommand { -public: - ServerMessage(); - - /** - * @copydoc RemoteCommand::help - */ - std::string help() const override; - - /** - * @copydoc RemoteCommand::args - */ - std::vector<Arg> args() const override; - - /** - * @copydoc RemoteCommand::request - */ - json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; - - /** - * @copydoc RemoteCommand::exec - */ - json::Value exec(Irccd &irccd, const json::Value &request) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_COMMAND_SERVER_MESSAGE_H_
--- a/lib/irccd/command/server-mode.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/* - * command-server-mode.cpp -- implementation of server-mode transport command - * - * 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/irccd.h> - -#include "server-mode.h" - -namespace irccd { - -namespace command { - -ServerMode::ServerMode() - : RemoteCommand("server-mode", "Server") -{ -} - -std::string ServerMode::help() const -{ - return ""; -} - -std::vector<RemoteCommand::Arg> ServerMode::args() const -{ - return { - { "server", true }, - { "mode", true } - }; -} - -json::Value ServerMode::request(Irccdctl &, const RemoteCommandRequest &args) const -{ - return json::object({ - { "server", args.arg(0) }, - { "mode", args.arg(1) } - }); -} - -json::Value ServerMode::exec(Irccd &irccd, const json::Value &request) const -{ - irccd.requireServer(request.at("server").toString())->mode(request.at("mode").toString()); - - return nullptr; -} - -} // !command - -} // !irccd
--- a/lib/irccd/command/server-mode.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/* - * server-mode.h -- implementation of server-mode transport command - * - * 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_COMMAND_SERVER_MODE_H_ -#define _IRCCD_COMMAND_SERVER_MODE_H_ - -/** - * @file server-mode.h - * @brief Implementation of server-mode transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class ServerMode - * @brief Implementation of server-mode transport command. - */ -class ServerMode : public RemoteCommand { -public: - ServerMode(); - - /** - * @copydoc RemoteCommand::help - */ - std::string help() const override; - - /** - * @copydoc RemoteCommand::args - */ - std::vector<Arg> args() const override; - - /** - * @copydoc RemoteCommand::request - */ - json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; - - /** - * @copydoc RemoteCommand::exec - */ - json::Value exec(Irccd &irccd, const json::Value &request) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_COMMAND_SERVER_MODE_H_
--- a/lib/irccd/command/server-nick.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/* - * server-nick.cpp -- implementation of server-nick transport command - * - * 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/irccd.h> - -#include "server-nick.h" - -namespace irccd { - -namespace command { - -ServerNick::ServerNick() - : RemoteCommand("server-nick", "Server") -{ -} - -std::string ServerNick::help() const -{ - return ""; -} - -std::vector<RemoteCommand::Arg> ServerNick::args() const -{ - return { - { "server", true }, - { "nickname", true } - }; -} - -json::Value ServerNick::request(Irccdctl &, const RemoteCommandRequest &args) const -{ - return json::object({ - { "server", args.arg(0) }, - { "nickname", args.arg(1) } - }); -} - -json::Value ServerNick::exec(Irccd &irccd, const json::Value &object) const -{ - irccd.requireServer(object.at("server").toString())->nick(object.at("nickname").toString()); - - return nullptr; -} - -} // !command - -} // !irccd
--- a/lib/irccd/command/server-nick.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/* - * server-nick.h -- implementation of server-nick transport command - * - * 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_COMMAND_SERVER_NICK_H_ -#define _IRCCD_COMMAND_SERVER_NICK_H_ - -/** - * @file command-server-nick.h - * @brief Implementation of server-nick transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class ServerNick - * @brief Implementation of server-nick transport command. - */ -class ServerNick : public RemoteCommand { -public: - ServerNick(); - - /** - * @copydoc RemoteCommand::help - */ - std::string help() const override; - - /** - * @copydoc RemoteCommand::args - */ - std::vector<Arg> args() const override; - - /** - * @copydoc RemoteCommand::request - */ - json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; - - /** - * @copydoc RemoteCommand::exec - */ - json::Value exec(Irccd &irccd, const json::Value &request) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_COMMAND_SERVER_NICK_H_
--- a/lib/irccd/command/server-notice.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ -/* - * server-notice.cpp -- implementation of server-notice transport command - * - * 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/irccd.h> - -#include "server-notice.h" - -namespace irccd { - -namespace command { - -ServerNotice::ServerNotice() - : RemoteCommand("server-notice", "Server") -{ -} - -std::string ServerNotice::help() const -{ - return ""; -} - -std::vector<RemoteCommand::Arg> ServerNotice::args() const -{ - return { - { "server", true }, - { "target", true }, - { "message", true } - }; -} - -json::Value ServerNotice::request(Irccdctl &, const RemoteCommandRequest &args) const -{ - return json::object({ - { "server", args.arg(0) }, - { "target", args.arg(1) }, - { "message", args.arg(2) } - }); -} - -json::Value ServerNotice::exec(Irccd &irccd, const json::Value &request) const -{ - irccd.requireServer(request.at("server").toString())->notice( - request.at("target").toString(), - request.at("message").toString() - ); - - return nullptr; -} - -} // !command - -} // !irccd -
--- a/lib/irccd/command/server-notice.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/* - * server-notice.h -- implementation of server-notice transport command - * - * 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_COMMAND_SERVER_NOTICE_H_ -#define _IRCCD_COMMAND_SERVER_NOTICE_H_ - -/** - * @file server-notice.h - * @brief Implementation of server-notice transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class ServerNotice - * @brief Implementation of server-notice transport command. - */ -class ServerNotice : public RemoteCommand { -public: - ServerNotice(); - - /** - * @copydoc RemoteCommand::help - */ - std::string help() const override; - - /** - * @copydoc RemoteCommand::args - */ - std::vector<Arg> args() const override; - - /** - * @copydoc RemoteCommand::request - */ - json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; - - /** - * @copydoc RemoteCommand::exec - */ - json::Value exec(Irccd &irccd, const json::Value &request) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_COMMAND_SERVER_NOTICE_H_
--- a/lib/irccd/command/server-part.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +0,0 @@ -/* - * server-part.cpp -- implementation of server-part transport command - * - * 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/irccd.h> - -#include "server-part.h" - -namespace irccd { - -namespace command { - -ServerPart::ServerPart() - : RemoteCommand("server-part", "Server") -{ -} - -std::string ServerPart::help() const -{ - return ""; -} - -std::vector<RemoteCommand::Arg> ServerPart::args() const -{ - return { - { "server", true }, - { "channel", true }, - { "reason", false } - }; -} - -json::Value ServerPart::request(Irccdctl &, const RemoteCommandRequest &args) const -{ - auto req = json::object({ - { "server", args.arg(0) }, - { "channel", args.arg(1) } - }); - - if (args.length() == 3) - req.insert("reason", args.arg(2)); - - return req; -} - -json::Value ServerPart::exec(Irccd &irccd, const json::Value &request) const -{ - irccd.requireServer(request.at("server").toString())->part( - request.at("channel").toString(), - request.valueOr("reason", "").toString() - ); - - return nullptr; -} - -} // !command - -} // !irccd
--- a/lib/irccd/command/server-part.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/* - * server-part.h -- implementation of server-part transport command - * - * 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_COMMAND_SERVER_PART_H_ -#define _IRCCD_COMMAND_SERVER_PART_H_ - -/** - * @file server-part.h - * @brief Implementation of server-part transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class ServerPart - * @brief Implementation of server-part transport command. - */ -class ServerPart : public RemoteCommand { -public: - ServerPart(); - - /** - * @copydoc RemoteCommand::help - */ - std::string help() const override; - - /** - * @copydoc RemoteCommand::args - */ - std::vector<Arg> args() const override; - - /** - * @copydoc RemoteCommand::request - */ - json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; - - /** - * @copydoc RemoteCommand::exec - */ - json::Value exec(Irccd &irccd, const json::Value &request) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_COMMAND_SERVER_PART_H_
--- a/lib/irccd/command/server-reconnect.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/* - * command-server-reconnect.cpp -- implementation of server-reconnect transport command - * - * 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/irccd.h> - -#include "server-reconnect.h" - -namespace irccd { - -namespace command { - -ServerReconnect::ServerReconnect() - : RemoteCommand("server-reconnect", "Server") -{ -} - -std::string ServerReconnect::help() const -{ - return ""; -} - -std::vector<RemoteCommand::Arg> ServerReconnect::args() const -{ - return {{ "server", false }}; -} - -json::Value ServerReconnect::request(Irccdctl &, const RemoteCommandRequest &args) const -{ - return args.length() == 0 ? nullptr : json::object({ { "server", args.arg(0) } }); -} - -json::Value ServerReconnect::exec(Irccd &irccd, const json::Value &request) const -{ - auto server = request.find("server"); - - if (server != request.end() && server->isString()) - irccd.requireServer(server->toString())->reconnect(); - else - for (auto &pair : irccd.servers()) - pair.second->reconnect(); - - return nullptr; -} - -} // !command - -} // !irccd
--- a/lib/irccd/command/server-reconnect.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/* - * server-reconnect.h -- implementation of server-reconnect transport command - * - * 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_COMMAND_SERVER_RECONNECT_H_ -#define _IRCCD_COMMAND_SERVER_RECONNECT_H_ - -/** - * @file server-reconnect.h - * @brief Implementation of server-reconnect transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class ServerReconnect - * @brief Implementation of server-reconnect transport command. - */ -class ServerReconnect : public RemoteCommand { -public: - ServerReconnect(); - - /** - * @copydoc RemoteCommand::help - */ - std::string help() const override; - - /** - * @copydoc RemoteCommand::args - */ - std::vector<Arg> args() const override; - - /** - * @copydoc RemoteCommand::request - */ - json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; - - /** - * @copydoc RemoteCommand::exec - */ - json::Value exec(Irccd &irccd, const json::Value &request) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_COMMAND_SERVER_RECONNECT_H_
--- a/lib/irccd/command/server-topic.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -/* - * command-server-topic.cpp -- implementation of server-topic transport command - * - * 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/irccd.h> - -#include "server-topic.h" - -namespace irccd { - -namespace command { - -ServerTopic::ServerTopic() - : RemoteCommand("server-topic", "Server") -{ -} - -std::string ServerTopic::help() const -{ - return ""; -} - -std::vector<RemoteCommand::Arg> ServerTopic::args() const -{ - return { - { "server", true }, - { "channel", true }, - { "topic", true } - }; -} - -json::Value ServerTopic::request(Irccdctl &, const RemoteCommandRequest &args) const -{ - return json::object({ - { "server", args.arg(0) }, - { "channel", args.arg(1) }, - { "topic", args.arg(2) } - }); -} - -json::Value ServerTopic::exec(Irccd &irccd, const json::Value &request) const -{ - irccd.requireServer(request.at("server").toString())->topic( - request.at("channel").toString(), - request.at("topic").toString() - ); - - return nullptr; -} - -} // !command - -} // !irccd
--- a/lib/irccd/command/server-topic.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/* - * server-topic.h -- implementation of server-topic transport command - * - * 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_COMMAND_SERVER_TOPIC_H_ -#define _IRCCD_COMMAND_SERVER_TOPIC_H_ - -/** - * @file server-topic.h - * @brief Implementation of server-topic transport command. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class ServerTopic - * @brief Implementation of server-topic transport command. - */ -class ServerTopic : public RemoteCommand { -public: - ServerTopic(); - - /** - * @copydoc RemoteCommand::help - */ - std::string help() const override; - - /** - * @copydoc RemoteCommand::args - */ - std::vector<Arg> args() const override; - - /** - * @copydoc RemoteCommand::request - */ - json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override; - - /** - * @copydoc RemoteCommand::exec - */ - json::Value exec(Irccd &irccd, const json::Value &request) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCD_COMMAND_SERVER_TOPIC_H_
--- a/lib/irccd/command/watch.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,245 +0,0 @@ -/* - * command-watch.cpp -- implementation of irccdctl watch - * - * 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 <functional> -#include <iostream> -#include <sstream> -#include <unordered_map> - -#include <irccd/irccdctl.h> - -#include "watch.h" - -namespace irccd { - -namespace command { - -namespace { - -void onChannelMode(const json::Value &v) -{ - std::cout << "event: onChannelMode\n"; - std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; - std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; - std::cout << "mode: " << v.valueOr("mode", "").toString() << "\n"; - std::cout << "argument: " << v.valueOr("argument", "").toString() << "\n"; -} - -void onChannelNotice(const json::Value &v) -{ - std::cout << "event: onChannelNotice\n"; - std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; - std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; - std::cout << "channel: " << v.valueOr("channel", "").toString() << "\n"; - std::cout << "message: " << v.valueOr("message", "").toString() << "\n"; -} - -void onConnect(const json::Value &v) -{ - std::cout << "event: onConnect\n"; - std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; -} - -void onInvite(const json::Value &v) -{ - std::cout << "event: onInvite\n"; - std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; - std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; - std::cout << "channel: " << v.valueOr("channel", "").toString() << "\n"; -} - -void onJoin(const json::Value &v) -{ - std::cout << "event: onJoin\n"; - std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; - std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; - std::cout << "channel: " << v.valueOr("channel", "").toString() << "\n"; -} - -void onKick(const json::Value &v) -{ - std::cout << "event: onKick\n"; - std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; - std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; - std::cout << "channel: " << v.valueOr("channel", "").toString() << "\n"; - std::cout << "target: " << v.valueOr("target", "").toString() << "\n"; - std::cout << "reason: " << v.valueOr("reason", "").toString() << "\n"; -} - -void onMessage(const json::Value &v) -{ - std::cout << "event: onMessage\n"; - std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; - std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; - std::cout << "channel: " << v.valueOr("channel", "").toString() << "\n"; - std::cout << "message: " << v.valueOr("message", "").toString() << "\n"; -} - -void onMe(const json::Value &v) -{ - std::cout << "event: onMe\n"; - std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; - std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; - std::cout << "target: " << v.valueOr("target", "").toString() << "\n"; - std::cout << "message: " << v.valueOr("message", "").toString() << "\n"; -} - -void onMode(const json::Value &v) -{ - std::cout << "event: onMode\n"; - std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; - std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; - std::cout << "mode: " << v.valueOr("mode", "").toString() << "\n"; -} - -void onNames(const json::Value &v) -{ - std::cout << "event: onNames\n"; - std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; - std::cout << "channel: " << v.valueOr("channel", "").toString() << "\n"; - std::cout << "names: " << v.valueOr("names", "").toJson(0) << "\n"; -} - -void onNick(const json::Value &v) -{ - std::cout << "event: onNick\n"; - std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; - std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; - std::cout << "nickname: " << v.valueOr("nickname", "").toString() << "\n"; -} - -void onNotice(const json::Value &v) -{ - std::cout << "event: onNotice\n"; - std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; - std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; - std::cout << "message: " << v.valueOr("message", "").toString() << "\n"; -} - -void onPart(const json::Value &v) -{ - std::cout << "event: onPart\n"; - std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; - std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; - std::cout << "channel: " << v.valueOr("channel", "").toString() << "\n"; - std::cout << "reason: " << v.valueOr("reason", "").toString() << "\n"; -} - -void onQuery(const json::Value &v) -{ - std::cout << "event: onQuery\n"; - std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; - std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; - std::cout << "message: " << v.valueOr("message", "").toString() << "\n"; -} - -void onTopic(const json::Value &v) -{ - std::cout << "event: onTopic\n"; - std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; - std::cout << "origin: " << v.valueOr("origin", "").toString() << "\n"; - std::cout << "channel: " << v.valueOr("channel", "").toString() << "\n"; - std::cout << "topic: " << v.valueOr("topic", "").toString() << "\n"; -} - -void onWhois(const json::Value &v) -{ - std::cout << "event: onWhois\n"; - std::cout << "server: " << v.valueOr("server", "").toString() << "\n"; - std::cout << "nickname: " << v.valueOr("nickname", "").toString() << "\n"; - std::cout << "username: " << v.valueOr("username", "").toString() << "\n"; - std::cout << "host: " << v.valueOr("host", "").toString() << "\n"; - std::cout << "realname: " << v.valueOr("realname", "").toString() << "\n"; -} - -const std::unordered_map<std::string, std::function<void (const json::Value &)>> events{ - { "onChannelMode", onChannelMode }, - { "onChannelNotice", onChannelNotice }, - { "onConnect", onConnect }, - { "onInvite", onInvite }, - { "onJoin", onJoin }, - { "onKick", onKick }, - { "onMessage", onMessage }, - { "onMe", onMe }, - { "onMode", onMode }, - { "onNames", onNames }, - { "onNick", onNick }, - { "onNotice", onNotice }, - { "onPart", onPart }, - { "onQuery", onQuery }, - { "onTopic", onTopic }, - { "onWhois", onWhois } -}; - -} // !namespace - -Watch::Watch() - : RemoteCommand("watch", "General") -{ -} - -std::vector<RemoteCommand::Option> Watch::options() const -{ - return {{ "format", "f", "format", "format", "output format" }}; -} - -std::string Watch::help() const -{ - std::ostringstream oss; - - oss << "Start watching irccd events.\n\n"; - oss << "You can use different output formats, native which is a human readable\n"; - oss << "format or json, pretty formatted json."; - - return oss.str(); -} - -json::Value Watch::request(Irccdctl &ctl, const RemoteCommandRequest &request) const -{ - std::string format = request.optionOr("format", "native"); - - if (format != "native" && format != "json") - throw std::invalid_argument("invalid format given: " + format); - - while (ctl.connection().isConnected()) { - try { - auto object = ctl.connection().next(-1); - auto it = events.find(object.valueOr("event", "").toString()); - - /* Silently ignore to avoid breaking user output */ - if (it == events.end()) - continue; - - if (format == "json") { - std::cout << object.toJson() << std::endl; - } else { - it->second(object); - std::cout << std::endl; - } - } catch (...) { - } - } - - throw std::runtime_error("connection lost"); - - return nullptr; -} - -} // !command - -} // !irccd
--- a/lib/irccd/command/watch.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -/* - * command-watch.h -- implementation of irccdctl watch - * - * 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 _IRCCDCTL_COMMAND_WATCH_H_ -#define _IRCCDCTL_COMMAND_WATCH_H_ - -/** - * @file command-watch.h - * @brief Implementation of irccdctl watch. - */ - -#include "command.h" - -namespace irccd { - -namespace command { - -/** - * @class Watch - * @brief Implementation of irccdctl watch. - */ -class Watch : public RemoteCommand { -public: - Watch(); - - std::vector<Option> options() const override; - - /** - * @copydoc RemoteCommand::help - */ - std::string help() const override; - - /** - * @copydoc RemoteCommand::request - */ - json::Value request(Irccdctl &ctl, const RemoteCommandRequest &request) const override; -}; - -} // !command - -} // !irccd - -#endif // !_IRCCDCTL_COMMAND_WATCH_H_
--- a/lib/irccd/config.cpp Sat Mar 26 14:41:53 2016 +0100 +++ b/lib/irccd/config.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -30,15 +30,13 @@ # include <cstdlib> #endif -#include <irccd/private/ini.h> - -#include <irccd/logger.h> -#include <irccd/path.h> -#include <irccd/private/sockets.h> -#include <irccd/system.h> -#include <irccd/util.h> - #include "config.h" +#include "ini.h" +#include "logger.h" +#include "path.h" +#include "sockets.h" +#include "system.h" +#include "util.h" #include "irccd.h" using namespace std;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/connection.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,48 @@ +/* + * connection.cpp -- value wrapper for connecting to irccd + * + * 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/logger.h> + +#include "connection.h" + +namespace irccd { + +json::Value Connection::next(const std::string &name, int timeout) +{ + m_timer.reset(); + + while (isConnected()) { + json::Value object = next(clamp(timeout)); + + if (object.isObject() && object["response"].toString() == name) + return object; + } + + throw std::runtime_error("connection lost"); +} + +void Connection::verify(const std::string &name, int timeout) +{ + auto object = next(name, timeout); + auto value = object.at("status").toString(); + + if (!value.empty() && value != "ok") + throw std::runtime_error(object.at("error").toString()); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/connection.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,238 @@ +/* + * connection.h -- value wrapper for connecting to irccd + * + * 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_CONNECTION_H_ +#define _IRCCD_CONNECTION_H_ + +#include <cassert> +#include <stdexcept> + +#include <irccd/json.h> +#include <irccd/system.h> +#include <irccd/util.h> + +#include "elapsed-timer.h" +#include "sockets.h" + +namespace irccd { + +/** + * @class Connection + * @brief Abstract class for connecting to irccd from Ip or Local addresses. + */ +class Connection { +protected: + ElapsedTimer m_timer; + + /** + * Clamp the time to wait to be sure that it will be never less than 0. + */ + inline int clamp(int timeout) noexcept + { + return timeout < 0 ? -1 : (timeout - (int)m_timer.elapsed() < 0) ? 0 : (timeout - m_timer.elapsed()); + } + +public: + /** + * Default constructor. + */ + Connection() = default; + + /** + * Default destructor. + */ + virtual ~Connection() = default; + + /** + * Wait for the next requested response. + * + * @param name the response name + * @param timeout the optional timeout + * @return the object + * @throw net::Error on errors or on timeout + */ + json::Value next(const std::string &name, int timeout = 30000); + + /** + * Just wait if the operation succeeded. + * + * @param name the response name + * @param timeout the timeout + */ + void verify(const std::string &name, int timeout = 30000); + + /** + * Check if the socket is still connected. + * + * @return true if connected + */ + virtual bool isConnected() const noexcept = 0; + + /** + * Try to connect to the host. + * + * @param timeout the maximum time in milliseconds + * @throw net::Error on errors or timeout + */ + virtual void connect(int timeout = 30000) = 0; + + /** + * Try to send the message in 30 seconds. The message must not end with \r\n\r\n, it is added automatically. + * + * @pre msg must not be empty + * @param msg the message to send + * @param timeout the maximum time in milliseconds + * @throw net::Error on errors + */ + virtual void send(std::string msg, int timeout = 30000) = 0; + + /** + * Get the next event from irccd. + * + * This functions throws if the connection is lost. + * + * @param timeout the maximum time in milliseconds + * @return the next event + * @throw net::Error on errors or disconnection + */ + virtual json::Value next(int timeout = 30000) = 0; +}; + +/** + * @class ConnectionBase + * @brief Implementation for Ip or Local. + */ +template <typename Address> +class ConnectionBase : public Connection { +private: + net::SocketTcp<Address> m_socket; + net::Listener<> m_listener; + Address m_address; + + /* Input buffer */ + std::string m_input; + +public: + /** + * Construct the socket but do not connect immediately. + * + * @param address the address + */ + ConnectionBase(Address address) + : m_address(std::move(address)) + { + m_socket.set(net::option::SockBlockMode{false}); + m_listener.set(m_socket.handle(), net::Condition::Readable); + } + + /** + * @copydoc Connection::isConnected + */ + bool isConnected() const noexcept override + { + return m_socket.state() == net::State::Connected; + } + + /** + * @copydoc Connection::connect + */ + void connect(int timeout) override; + + /** + * @copydoc Connection::send + */ + void send(std::string msg, int timeout) override; + + /** + * @copydoc Connection::next + */ + json::Value next(int timeout) override; +}; + +template <typename Address> +void ConnectionBase<Address>::connect(int timeout) +{ + m_socket.connect(m_address); + + if (m_socket.state() == net::State::Connecting) { + m_listener.set(m_socket.handle(), net::Condition::Writable); + m_listener.wait(timeout); + m_socket.connect(); + m_listener.unset(m_socket.handle(), net::Condition::Writable); + } +} + +template <typename Address> +void ConnectionBase<Address>::send(std::string msg, int timeout) +{ + assert(!msg.empty()); + + /* Add termination */ + msg += "\r\n\r\n"; + + m_listener.remove(m_socket.handle()); + m_listener.set(m_socket.handle(), net::Condition::Writable); + m_timer.reset(); + + while (!msg.empty()) { + /* Do not wait the time that is already passed */ + m_listener.wait(clamp(timeout)); + + /* Try to send at most as possible */ + msg.erase(0, m_socket.send(msg)); + } + + /* Timeout? */ + if (!msg.empty()) + throw std::runtime_error("operation timed out while sending to irccd"); +} + +template <typename Address> +json::Value ConnectionBase<Address>::next(int timeout) +{ + /* Maybe there is already something */ + std::string buffer = util::nextNetwork(m_input); + + m_listener.remove(m_socket.handle()); + m_listener.set(m_socket.handle(), net::Condition::Readable); + m_timer.reset(); + + /* Read if there is nothing */ + while (buffer.empty() && isConnected()) { + /* Wait and read */ + m_listener.wait(clamp(timeout)); + m_input += m_socket.recv(512); + + /* Finally try */ + buffer = util::nextNetwork(m_input); + } + + if (!isConnected()) + throw std::runtime_error("connection lost"); + + json::Value value(json::Buffer{buffer}); + + if (!value.isObject()) + throw std::invalid_argument("invalid message received"); + + return value; +} + +} // !irccd + +#endif // !_IRCCD_CONNECTION_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/directory.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,188 @@ +/* + * directory.cpp -- open and read directories + * + * 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 <stdexcept> + +#include "directory.h" + +#if defined(_WIN32) +# include <Windows.h> +#else +# include <cstring> +# include <cerrno> + +# include <sys/types.h> +# include <dirent.h> +#endif + +namespace irccd { + +#if defined(_WIN32) + +namespace { + +std::string systemError() +{ + LPSTR error = nullptr; + std::string errmsg = "Unknown error"; + + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&error, 0, NULL); + + if (error) { + errmsg = std::string(error); + LocalFree(error); + } + + return errmsg; +} + +} // !namespace + +void Directory::systemLoad(const std::string &path, int flags) +{ + std::ostringstream oss; + HANDLE handle; + WIN32_FIND_DATA fdata; + + oss << path << "\\*"; + handle = FindFirstFile(oss.str().c_str(), &fdata); + + if (handle == nullptr) + throw std::runtime_error(systemError()); + + do { + DirectoryEntry entry; + + entry.name = fdata.cFileName; + if (entry.name == "." && !(flags & Directory::Dot)) + continue; + if (entry.name == ".." && !(flags & Directory::DotDot)) + continue; + + switch (fdata.dwFileAttributes) { + case FILE_ATTRIBUTE_DIRECTORY: + entry.type = DirectoryEntry::Dir; + break; + case FILE_ATTRIBUTE_NORMAL: + entry.type = DirectoryEntry::File; + break; + case FILE_ATTRIBUTE_REPARSE_POINT: + entry.type = DirectoryEntry::Link; + break; + default: + break; + } + + m_list.push_back(entry); + } while (FindNextFile(handle, &fdata) != 0); + + FindClose(handle); +} + +#else + +void Directory::systemLoad(const std::string &path, int flags) +{ + DIR *dp; + struct dirent *ent; + + if ((dp = opendir(path.c_str())) == nullptr) + throw std::runtime_error(std::strerror(errno)); + + while ((ent = readdir(dp)) != nullptr) { + DirectoryEntry entry; + + entry.name = ent->d_name; + if (entry.name == "." && !(flags & Directory::Dot)) + continue; + if (entry.name == ".." && !(flags & Directory::DotDot)) + continue; + + switch (ent->d_type) { + case DT_DIR: + entry.type = DirectoryEntry::Dir; + break; + case DT_REG: + entry.type = DirectoryEntry::File; + break; + case DT_LNK: + entry.type = DirectoryEntry::Link; + break; + default: + break; + } + + m_list.push_back(entry); + } + + closedir(dp); +} + +#endif + +bool operator==(const DirectoryEntry &e1, const DirectoryEntry &e2) +{ + return e1.name == e2.name && e1.type == e2.type; +} + +Directory::Directory() +{ +} + +Directory::Directory(const std::string &path, int flags) +{ + systemLoad(path, flags); +} + +Directory::List::iterator Directory::begin() +{ + return m_list.begin(); +} + +Directory::List::const_iterator Directory::cbegin() const +{ + return m_list.cbegin(); +} + +Directory::List::iterator Directory::end() +{ + return m_list.end(); +} + +Directory::List::const_iterator Directory::cend() const +{ + return m_list.cend(); +} + +int Directory::count() const +{ + return m_list.size(); +} + +bool operator==(const Directory &d1, const Directory &d2) +{ + return d1.m_list == d2.m_list; +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/directory.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,139 @@ +/* + * directory.h -- open and read directories + * + * 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 _DIRECTORY_H_ +#define _DIRECTORY_H_ + +#include <cstddef> +#include <string> +#include <vector> + +namespace irccd { + +/** + * @class Entry + * @brief entry in the directory list + */ +class DirectoryEntry { +public: + /** + * @enum Type + * @brief Describe the type of an entry + */ + enum Type { + Unknown = 0, + File, + Dir, + Link + }; + + std::string name; //! name of entry (base name) + Type type{Unknown}; //! type of file + + friend bool operator==(const DirectoryEntry &e1, const DirectoryEntry &e2); +}; + +/** + * @class Directory + * @brief class to manipulate directories + * + * This class allow the user to iterate directories in a for range based + * loop using iterators. + */ +class Directory { +public: + /** + * @enum Flags + * @brief optional flags to read directories + */ + enum Flags { + Dot = (1 << 0), //!< If set, lists "." too + DotDot = (1 << 1) //!< If set, lists ".." too + }; + + using List = std::vector<DirectoryEntry>; + + // C++ Container compatibility + using value_type = List::value_type; + using iterator = List::iterator; + using const_iterator = List::const_iterator; + +private: + List m_list; + + void systemLoad(const std::string &path, int flags); + +public: + /** + * Default constructor, does nothing. + */ + Directory(); + + /** + * Open a directory and read all its content. + * @param path the path + * @param flags the optional flags + */ + Directory(const std::string &path, int flags = 0); + + /** + * Virtual destructor defaulted. + */ + virtual ~Directory() = default; + + /** + * Return an iterator the beginning. + * + * @return the iterator + */ + List::iterator begin(); + + /** + * Return a const iterator the beginning. + * + * @return the iterator + */ + List::const_iterator cbegin() const; + + /** + * Return an iterator to past the end. + * + * @return the iterator + */ + List::iterator end(); + + /** + * Return a const iterator to past the end. + * + * @return the iterator + */ + List::const_iterator cend() const; + + /** + * Get the number of entries in the directory. + * + * @return the number + */ + int count() const; + + friend bool operator==(const Directory &d1, const Directory &d2); +}; + +} // !irccd + +#endif // !_DIRECTORY_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/elapsed-timer.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,64 @@ +/* + * elapsed-timer.cpp -- measure elapsed time + * + * 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.h" + +using std::chrono::duration_cast; +using std::chrono::high_resolution_clock; +using std::chrono::milliseconds; + +namespace irccd { + +ElapsedTimer::ElapsedTimer() noexcept +{ + m_last = high_resolution_clock::now(); +} + +void ElapsedTimer::pause() noexcept +{ + /* + * When we put the timer on pause, do not forget to set the already + * elapsed time. + */ + (void)elapsed(); + m_paused = true; +} + +void ElapsedTimer::restart() noexcept +{ + m_paused = false; + m_last = high_resolution_clock::now(); +} + +void ElapsedTimer::reset() noexcept +{ + m_elapsed = 0; + m_last = high_resolution_clock::now(); +} + +unsigned ElapsedTimer::elapsed() noexcept +{ + if (!m_paused) { + m_elapsed += duration_cast<milliseconds>(high_resolution_clock::now() - m_last).count(); + m_last = high_resolution_clock::now(); + } + + return m_elapsed; +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/elapsed-timer.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,86 @@ +/* + * elapsed-timer.h -- measure elapsed time + * + * 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_ELAPSED_TIMER_H_ +#define _IRCCD_ELAPSED_TIMER_H_ + +/** + * @file ElapsedTimer.h + * @brief Measure elapsed time + */ + +#include <chrono> + +namespace irccd { + +/** + * @class ElapsedTimer + * @brief Measure elapsed time + * + * This class provides an abstraction to measure elapsed time since the + * construction of the object. + * + * It uses std::chrono::high_resolution_clock for more precision and uses + * milliseconds only. + */ +class ElapsedTimer { +public: + using TimePoint = std::chrono::time_point<std::chrono::high_resolution_clock>; + +private: + TimePoint m_last; + bool m_paused{false}; + unsigned m_elapsed{0}; + +public: + /** + * Construct the elapsed timer, start counting. + */ + ElapsedTimer() noexcept; + + /** + * Virtual destructor defaulted. + */ + virtual ~ElapsedTimer() = default; + + /** + * Put the timer on pause, the already elapsed time is stored. + */ + void pause() noexcept; + + /** + * Restart the timer, does not reset it. + */ + void restart() noexcept; + + /** + * Reset the timer to 0. + */ + void reset() noexcept; + + /** + * Get the number of elapsed milliseconds. + * + * @return the milliseconds + */ + unsigned elapsed() noexcept; +}; + +} // !irccd + +#endif // !_IRCCD_ELAPSED_TIMER_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/filesystem.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,187 @@ +/* + * filesystem.cpp -- some file system operation + * + * 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 <cstdlib> +#include <cstring> +#include <stdexcept> +#include <sstream> + +#include <irccd-config.h> + +#if defined(IRCCD_SYSTEM_WINDOWS) +# include <direct.h> +# include <shlwapi.h> +#else +# include <sys/stat.h> +# include <climits> +# include <unistd.h> +# include <libgen.h> +#endif + +#include "filesystem.h" + +namespace irccd { + +namespace fs { + +#if defined(IRCCD_SYSTEM_WINDOWS) +const char Separator('\\'); +#else +const char Separator('/'); +#endif + +std::string baseName(std::string path) +{ +#if defined(IRCCD_SYSTEM_WINDOWS) + std::size_t pos; + + pos = path.find_last_of('\\'); + if (pos == std::string::npos) + pos = path.find_last_of('/'); + if (pos == std::string::npos) + return path; + + return path.substr(pos + 1); +#else + return basename(&path[0]); +#endif +} + +std::string dirName(std::string path) +{ +#if defined(IRCCD_SYSTEM_WINDOWS) + std::size_t pos; + + pos = path.find_last_of('\\'); + if (pos == std::string::npos) + pos = path.find_last_of('/'); + if (pos == std::string::npos) + return path; + + return path.substr(0, pos); +#else + return dirname(&path[0]); +#endif +} + +bool isAbsolute(const std::string &path) noexcept +{ +#if defined(IRCCD_SYSTEM_WINDOWS) + return !isRelative(path); +#else + return path.size() > 0 && path[0] == '/'; +#endif +} + +bool isRelative(const std::string &path) noexcept +{ +#if defined(IRCCD_SYSTEM_WINDOWS) + return PathIsRelativeA(path.c_str()); +#else + return !isAbsolute(path); +#endif +} + +bool exists(const std::string &path) +{ +#if defined(HAVE_ACCESS) + return access(path.c_str(), F_OK) == 0; +#elif defined(HAVE_STAT) + struct stat st; + + return (stat(path.c_str(), &st) == 0); +#else + // worse fallback + std::FILE *file = std::fopen(path.c_str(), "r"); + bool result; + + if (file != nullptr) { + result = true; + std::fclose(file); + } else { + result = false; + } + + return result; +#endif +} + +void mkdir(const std::string &dir, int mode) +{ + std::ostringstream oss; + + oss << "mkdir: "; + + for (std::size_t i = 0; i < dir.length(); ++i) { + if (dir[i] != '/' && dir[i] != '\\') + continue; + + std::string part = dir.substr(0, i); + if (part.length() <= 0 || exists(part)) + continue; + +#if defined(IRCCD_SYSTEM_WINDOWS) + if (::_mkdir(part.c_str()) < 0) { +#else + if (::mkdir(part.c_str(), mode) < 0) { +#endif + oss << part << ": " << std::strerror(errno); + throw std::runtime_error(oss.str()); + } + } + + // Last part +#if defined(IRCCD_SYSTEM_WINDOWS) + if (::_mkdir(dir.c_str()) < 0) { +#else + if (::mkdir(dir.c_str(), mode) < 0) { +#endif + oss << dir << ": " << std::strerror(errno); + throw std::runtime_error(oss.str()); + } + +#if defined(IRCCD_SYSTEM_WINDOWS) + // Windows's mkdir does not use mode. + (void)mode; +#endif +} + +std::string cwd() +{ +#if defined(IRCCD_SYSTEM_WINDOWS) + char path[MAX_PATH]; + + if (!GetCurrentDirectoryA(sizeof (path), path)) + throw std::runtime_error("failed to get current working directory"); + + return path; +#else + char path[PATH_MAX]; + + if (getcwd(path, sizeof (path)) == nullptr) + throw std::runtime_error(std::strerror(errno)); + + return path; +#endif +} + +} // !fs + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/filesystem.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,42 @@ +/* + * filesystem.h -- some file system operation + * + * 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_FILESYSTEM_H_ +#define _IRCCD_FILESYSTEM_H_ + +#include <string> + +namespace irccd { + +namespace fs { + +extern const char Separator; + +std::string baseName(std::string path); +std::string dirName(std::string path); +bool isAbsolute(const std::string &path) noexcept; +bool isRelative(const std::string &path) noexcept; +bool exists(const std::string &path); +void mkdir(const std::string &dir, int mode = 0700); +std::string cwd(); + +} // !fs + +} // !irccd + +#endif // !_IRCCD_FILESYSTEM_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/ini.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,427 @@ +/* + * ini.cpp -- .ini file parsing + * + * 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 <cctype> +#include <cstring> +#include <iostream> +#include <iterator> +#include <fstream> +#include <sstream> +#include <stdexcept> + +#if defined(_WIN32) +# include <Shlwapi.h> // for PathIsRelative +#endif + +#include "ini.h" + +namespace { + +using namespace irccd; +using namespace irccd::ini; + +using StreamIterator = std::istreambuf_iterator<char>; +using TokenIterator = std::vector<Token>::const_iterator; + +inline bool isAbsolute(const std::string &path) noexcept +{ +#if defined(_WIN32) + return !PathIsRelative(path.c_str()); +#else + return path.size() > 0 && path[0] == '/'; +#endif +} + +inline bool isQuote(char c) noexcept +{ + return c == '\'' || c == '"'; +} + +inline bool isSpace(char c) noexcept +{ + /* Custom version because std::isspace includes \n as space */ + return c == ' ' || c == '\t'; +} + +inline bool isList(char c) noexcept +{ + return c == '(' || c == ')' || c == ','; +} + +inline bool isReserved(char c) noexcept +{ + return isList(c) || isQuote(c) || c == '[' || c == ']' || c == '@' || c == '#' || c == '='; +} + +void analyzeLine(int &line, int &column, StreamIterator &it) noexcept +{ + assert(*it == '\n'); + + ++ line; + ++ it; + column = 0; +} + +void analyzeComment(int &column, StreamIterator &it, StreamIterator end) noexcept +{ + assert(*it == '#'); + + while (it != end && *it != '\n') { + ++ column; + ++ it; + } +} + +void analyzeSpaces(int &column, StreamIterator &it, StreamIterator end) noexcept +{ + assert(isSpace(*it)); + + while (it != end && isSpace(*it)) { + ++ column; + ++ it; + } +} + +void analyzeList(Tokens &list, int line, int &column, StreamIterator &it) noexcept +{ + assert(isList(*it)); + + switch (*it++) { + case '(': + list.emplace_back(Token::ListBegin, line, column++); + break; + case ')': + list.emplace_back(Token::ListEnd, line, column++); + break; + case ',': + list.emplace_back(Token::Comma, line, column++); + break; + default: + break; + } +} + +void analyzeSection(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end) +{ + assert(*it == '['); + + std::string value; + int save = column; + + /* Read section name */ + ++ it; + while (it != end && *it != ']') { + if (*it == '\n') + throw Error(line, column, "section not terminated, missing ']'"); + if (isReserved(*it)) + throw Error(line, column, "section name expected after '[', got '" + std::string(1, *it) + "'"); + ++ column; + value += *it++; + } + + if (it == end) + throw Error(line, column, "section name expected after '[', got <EOF>"); + + /* Remove ']' */ + ++ it; + + list.emplace_back(Token::Section, line, save, std::move(value)); +} + +void analyzeAssign(Tokens &list, int &line, int &column, StreamIterator &it) +{ + assert(*it == '='); + + list.push_back({ Token::Assign, line, column++ }); + ++ it; +} + +void analyzeQuotedWord(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end) +{ + std::string value; + int save = column; + char quote = *it++; + + while (it != end && *it != quote) { + // TODO: escape sequence + ++ column; + value += *it++; + } + + if (it == end) + throw Error(line, column, "undisclosed '" + std::string(1, quote) + "', got <EOF>"); + + /* Remove quote */ + ++ it; + + list.push_back({ Token::QuotedWord, line, save, std::move(value) }); +} + +void analyzeWord(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end) +{ + assert(!isReserved(*it)); + + std::string value; + int save = column; + + while (it != end && !std::isspace(*it) && !isReserved(*it)) { + ++ column; + value += *it++; + } + + list.push_back({ Token::Word, line, save, std::move(value) }); +} + +void analyzeInclude(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end) +{ + assert(*it == '@'); + + std::string include; + int save = column; + + /* Read include */ + ++ it; + while (it != end && !isSpace(*it)) { + ++ column; + include += *it++; + } + + if (include != "include") + throw Error(line, column, "expected include after '@' token"); + + list.push_back({ Token::Include, line, save }); +} + +Tokens analyze(StreamIterator &it, StreamIterator end) +{ + Tokens list; + int line = 1; + int column = 0; + + while (it != end) { + if (*it == '\n') + analyzeLine(line, column, it); + else if (*it == '#') + analyzeComment(column, it, end); + else if (*it == '[') + analyzeSection(list, line, column, it, end); + else if (*it == '=') + analyzeAssign(list, line, column, it); + else if (isSpace(*it)) + analyzeSpaces(column, it, end); + else if (*it == '@') + analyzeInclude(list, line, column, it, end); + else if (isQuote(*it)) + analyzeQuotedWord(list, line, column, it, end); + else if (isList(*it)) + analyzeList(list, line, column, it); + else + analyzeWord(list, line, column, it, end); + } + + return list; +} + +void parseOptionValueSimple(Option &option, TokenIterator &it) +{ + assert(it->type() == Token::Word || it->type() == Token::QuotedWord); + + option.push_back((it++)->value()); +} + +void parseOptionValueList(Option &option, TokenIterator &it, TokenIterator end) +{ + assert(it->type() == Token::ListBegin); + + TokenIterator save = it++; + + while (it != end && it->type() != Token::ListEnd) { + switch (it->type()) { + case Token::Comma: + /* Previous must be a word */ + if (it[-1].type() != Token::Word && it[-1].type() != Token::QuotedWord) + throw Error(it->line(), it->column(), "unexpected comma after '" + it[-1].value() + "'"); + + ++ it; + break; + case Token::Word: + case Token::QuotedWord: + option.push_back((it++)->value()); + break; + default: + throw Error(it->line(), it->column(), "unexpected '" + it[-1].value() + "' in list construct"); + break; + } + } + + if (it == end) + throw Error(save->line(), save->column(), "unterminated list construct"); + + /* Remove ) */ + ++ it; +} + +void parseOption(Section &sc, TokenIterator &it, TokenIterator end) +{ + Option option(it->value()); + + TokenIterator save = it; + + /* No '=' or something else? */ + if (++it == end) + throw Error(save->line(), save->column(), "expected '=' assignment, got <EOF>"); + if (it->type() != Token::Assign) + throw Error(it->line(), it->column(), "expected '=' assignment, got " + it->value()); + + /* Empty options are allowed so just test for words */ + if (++it != end) { + if (it->type() == Token::Word || it->type() == Token::QuotedWord) + parseOptionValueSimple(option, it); + else if (it->type() == Token::ListBegin) + parseOptionValueList(option, it, end); + } + + sc.push_back(std::move(option)); +} + +void parseInclude(Document &doc, TokenIterator &it, TokenIterator end) +{ + TokenIterator save = it; + + if (++it == end) + throw Error(save->line(), save->column(), "expected file name after '@include' statement, got <EOF>"); + if (it->type() != Token::Word && it->type() != Token::QuotedWord) + throw Error(it->line(), it->column(), "expected file name after '@include' statement, got " + it->value()); + if (doc.path().empty()) + throw Error(it->line(), it->column(), "'@include' statement invalid with buffer documents"); + + std::string value = (it++)->value(); + std::string file; + + if (!isAbsolute(value)) { +#if defined(_WIN32) + file = doc.path() + "\\" + value; +#else + file = doc.path() + "/" + value; +#endif + } else { + file = value; + } + + Document child(File{file}); + + for (const auto &sc : child) + doc.push_back(sc); +} + +void parseSection(Document &doc, TokenIterator &it, TokenIterator end) +{ + Section sc(it->value()); + + /* Skip [section] */ + ++ it; + + /* Read until next section */ + while (it != end && it->type() != Token::Section) { + if (it->type() != Token::Word) + throw Error(it->line(), it->column(), "unexpected token '" + it->value() + "' in section definition"); + + parseOption(sc, it, end); + } + + doc.push_back(std::move(sc)); +} + +void parse(Document &doc, const Tokens &tokens) +{ + TokenIterator it = tokens.cbegin(); + TokenIterator end = tokens.cend(); + + while (it != end) { + /* Just ignore this */ + switch (it->type()) { + case Token::Include: + parseInclude(doc, it, end); + break; + case Token::Section: + parseSection(doc, it, end); + break; + default: + throw Error(it->line(), it->column(), "unexpected '" + it->value() + "' on root document"); + } + } +} + +} // !namespace + +namespace irccd { + +namespace ini { + +Tokens Document::analyze(const File &file) +{ + std::fstream stream(file.path); + + if (!stream) + throw std::runtime_error(std::strerror(errno)); + + std::istreambuf_iterator<char> it(stream); + std::istreambuf_iterator<char> end; + + return ::analyze(it, end); +} + +Tokens Document::analyze(const Buffer &buffer) +{ + std::istringstream stream(buffer.text); + std::istreambuf_iterator<char> it(stream); + std::istreambuf_iterator<char> end; + + return ::analyze(it, end); +} + +Document::Document(const File &file) + : m_path(file.path) +{ + /* Update path */ + auto pos = m_path.find_last_of("/\\"); + + if (pos != std::string::npos) + m_path.erase(pos); + else + m_path = "."; + + parse(*this, analyze(file)); +} + +Document::Document(const Buffer &buffer) +{ + parse(*this, analyze(buffer)); +} + +void Document::dump(const Tokens &tokens) +{ + for (const Token &token: tokens) + // TODO: add better description + std::cout << token.line() << ":" << token.column() << ": " << token.value() << std::endl; +} + +} // !ini + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/ini.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,538 @@ +/* + * ini.h -- .ini file parsing + * + * 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 _INI_H_ +#define _INI_H_ + +/** + * @file Ini.h + * @brief Configuration file parser. + */ + +#include <algorithm> +#include <exception> +#include <stdexcept> +#include <string> +#include <vector> + +namespace irccd { + +/** + * Namespace for ini related classes. + */ +namespace ini { + +class Document; + +/** + * @class Error + * @brief Error in a file + */ +class Error : public std::exception { +private: + int m_line; //!< line number + int m_column; //!< line column + std::string m_message; //!< error message + +public: + /** + * Constructor. + * + * @param l the line + * @param c the column + * @param m the message + */ + inline Error(int l, int c, std::string m) noexcept + : m_line(l) + , m_column(c) + , m_message(std::move(m)) + { + } + + /** + * Get the line number. + * + * @return the line + */ + inline int line() const noexcept + { + return m_line; + } + + /** + * Get the column number. + * + * @return the column + */ + inline int column() const noexcept + { + return m_column; + } + + /** + * Return the raw error message (no line and column shown). + * + * @return the error message + */ + const char *what() const noexcept override + { + return m_message.c_str(); + } +}; + +/** + * @class Token + * @brief Describe a token read in the .ini source + * + * This class can be used when you want to parse a .ini file yourself. + * + * @see Document::analyze + */ +class Token { +public: + /** + * @brief Token type + */ + enum Type { + Include, //!< include statement + Section, //!< [section] + Word, //!< word without quotes + QuotedWord, //!< word with quotes + Assign, //!< = assignment + ListBegin, //!< begin of list ( + ListEnd, //!< end of list ) + Comma //!< list separation + }; + +private: + Type m_type; + int m_line; + int m_column; + std::string m_value; + +public: + /** + * Construct a token. + * + * @param type the type + * @param line the line + * @param column the column + * @param value the value + */ + Token(Type type, int line, int column, std::string value = "") noexcept + : m_type(type) + , m_line(line) + , m_column(column) + { + switch (type) { + case Include: + m_value = "@include"; + break; + case Section: + case Word: + case QuotedWord: + m_value = value; + break; + case Assign: + m_value = "="; + break; + case ListBegin: + m_value = "("; + break; + case ListEnd: + m_value = ")"; + break; + case Comma: + m_value = ","; + break; + default: + break; + } + } + + /** + * Get the type. + * + * @return the type + */ + inline Type type() const noexcept + { + return m_type; + } + + /** + * Get the line. + * + * @return the line + */ + inline int line() const noexcept + { + return m_line; + } + + /** + * Get the column. + * + * @return the column + */ + inline int column() const noexcept + { + return m_column; + } + + /** + * Get the value. For words, quoted words and section, the value is the content. Otherwise it's the + * characters parsed. + * + * @return the value + */ + inline const std::string &value() const noexcept + { + return m_value; + } +}; + +/** + * List of tokens in order they are analyzed. + */ +using Tokens = std::vector<Token>; + +/** + * @class Option + * @brief Option definition. + */ +class Option : public std::vector<std::string> { +private: + std::string m_key; + +public: + /** + * Construct an empty option. + * + * @param key the key + * @param value the value + */ + inline Option(std::string key) noexcept + : std::vector<std::string>() + , m_key(std::move(key)) + { + } + + /** + * Construct a single option. + * + * @param key the key + * @param value the value + */ + inline Option(std::string key, std::string value) noexcept + : m_key(std::move(key)) + { + push_back(std::move(value)); + } + + /** + * Construct a list option. + * + * @param key the key + * @param values the values + */ + inline Option(std::string key, std::vector<std::string> values) noexcept + : std::vector<std::string>(std::move(values)) + , m_key(std::move(key)) + { + } + + /** + * Get the option key. + * + * @return the key + */ + inline const std::string &key() const noexcept + { + return m_key; + } + + /** + * Get the option value. + * + * @return the value + */ + inline const std::string &value() const noexcept + { + static std::string dummy; + + return empty() ? dummy : (*this)[0]; + } +}; + +/** + * @class Section + * @brief Section that contains one or more options. + */ +class Section : public std::vector<Option> { +private: + std::string m_key; + +public: + /** + * Construct a section with its name. + * + * @param key the key + */ + inline Section(std::string key) noexcept + : m_key(std::move(key)) + { + } + + /** + * Get the section key. + * + * @return the key + */ + inline const std::string &key() const noexcept + { + return m_key; + } + + /** + * Check if the section contains a specific option. + * + * @param key the option key + * @return true if the option exists + */ + inline bool contains(const std::string &key) const noexcept + { + return find(key) != end(); + } + + /** + * Access an option at the specified key. + * + * @param key the key + * @return the option + * @throw std::out_of_range if the key does not exist + */ + inline Option &operator[](const std::string &key) + { + return *find(key); + } + + /** + * Access an option at the specified key. + * + * @param key the key + * @return the option + * @throw std::out_of_range if the key does not exist + */ + inline const Option &operator[](const std::string &key) const + { + return *find(key); + } + + /** + * Find an option by key and return an iterator. + * + * @param key the key + * @return the iterator or end() if not found + */ + inline iterator find(const std::string &key) noexcept + { + return std::find_if(begin(), end(), [&] (const auto &o) { + return o.key() == key; + }); + } + + /** + * Find an option by key and return an iterator. + * + * @param key the key + * @return the iterator or end() if not found + */ + inline const_iterator find(const std::string &key) const noexcept + { + return std::find_if(cbegin(), cend(), [&] (const auto &o) { + return o.key() == key; + }); + } + + /** + * Inherited operators. + */ + using std::vector<Option>::operator[]; +}; + +/** + * @class File + * @brief Source for reading .ini files. + */ +class File { +public: + /** + * Path to the file. + */ + std::string path; +}; + +/** + * @class Buffer + * @brief Source for reading ini from text. + * @note the include statement is not supported with buffers. + */ +class Buffer { +public: + /** + * The ini content. + */ + std::string text; +}; + +/** + * @class Document + * @brief Ini config file loader + */ +class Document : public std::vector<Section> { +private: + std::string m_path; + +public: + /** + * Analyze a file and extract tokens. If the function succeeds, that does not mean the content is valid, + * it just means that there are no syntax error. + * + * For example, this class does not allow adding options under no sections and this function will not + * detect that issue. + * + * @param file the file to read + * @return the list of tokens + * @throws Error on errors + */ + static Tokens analyze(const File &file); + + /** + * Overloaded function for buffers. + * + * @param buffer the buffer to read + * @return the list of tokens + * @throws Error on errors + */ + static Tokens analyze(const Buffer &buffer); + + /** + * Show all tokens and their description. + * + * @param tokens the tokens + */ + static void dump(const Tokens &tokens); + + /** + * Construct a document from a file. + * + * @param file the file to read + * @throws Error on errors + */ + Document(const File &file); + + /** + * Overloaded constructor for buffers. + * + * @param buffer the buffer to read + * @throws Error on errors + */ + Document(const Buffer &buffer); + + /** + * Get the current document path, only useful when constructed from File source. + * + * @return the path + */ + inline const std::string &path() const noexcept + { + return m_path; + } + + /** + * Check if a document has a specific section. + * + * @param key the key + */ + inline bool contains(const std::string &key) const noexcept + { + return std::find_if(begin(), end(), [&] (const auto &sc) { return sc.key() == key; }) != end(); + } + + /** + * Access a section at the specified key. + * + * @param key the key + * @return the section + * @throw std::out_of_range if the key does not exist + */ + inline Section &operator[](const std::string &key) + { + return *find(key); + } + + /** + * Access a section at the specified key. + * + * @param key the key + * @return the section + * @throw std::out_of_range if the key does not exist + */ + inline const Section &operator[](const std::string &key) const + { + return *find(key); + } + + /** + * Find a section by key and return an iterator. + * + * @param key the key + * @return the iterator or end() if not found + */ + inline iterator find(const std::string &key) noexcept + { + return std::find_if(begin(), end(), [&] (const auto &o) { + return o.key() == key; + }); + } + + /** + * Find a section by key and return an iterator. + * + * @param key the key + * @return the iterator or end() if not found + */ + inline const_iterator find(const std::string &key) const noexcept + { + return std::find_if(cbegin(), cend(), [&] (const auto &o) { + return o.key() == key; + }); + } + + /** + * Inherited operators. + */ + using std::vector<Section>::operator[]; +}; + +} // !ini + +} // !irccd + +#endif // !_INI_H_
--- a/lib/irccd/irccd.cpp Sat Mar 26 14:41:53 2016 +0100 +++ b/lib/irccd/irccd.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -19,9 +19,8 @@ #include <algorithm> #include <stdexcept> -#include "private/filesystem.h" - #include "irccd.h" +#include "filesystem.h" #include "logger.h" #include "path.h" #include "util.h" @@ -698,7 +697,7 @@ try { event.exec(*pair.second); - } catch (const js::ErrorInfo &info) { + } catch (const duk::ErrorInfo &info) { log::warning() << "plugin " << pair.second->info().name << ": error: " << info.what() << std::endl; if (!info.fileName.empty()) @@ -850,7 +849,7 @@ plugin->onTimerEnd.connect(std::bind(&Irccd::handleTimerEnd, this, ptr, _1)); /* Store reference to irccd */ - plugin->context().putGlobal("\xff""\xff""irccd", js::RawPointer<Irccd>{this}); + duk::putGlobal(plugin->context(), "\xff""\xff""irccd", duk::RawPointer<Irccd>{this}); /* Initial load now */ try { @@ -934,12 +933,12 @@ auto &ctx = plugin->context(); - js::StackAssert sa(ctx); + duk::StackAssert sa(ctx); try { - ctx.getGlobal<void>("\xff""\xff""timer-" + std::to_string(reinterpret_cast<std::intptr_t>(timer.get()))); - ctx.pcall(0); - ctx.pop(); + duk::getGlobal<void>(ctx, "\xff""\xff""timer-" + std::to_string(reinterpret_cast<std::intptr_t>(timer.get()))); + duk::pcall(ctx, 0); + duk::pop(ctx); } catch (const std::exception &) { log::info() << "failure" << std::endl; }
--- a/lib/irccd/irccd.h Sat Mar 26 14:41:53 2016 +0100 +++ b/lib/irccd/irccd.h Wed Mar 30 13:52:47 2016 +0200 @@ -29,7 +29,7 @@ #include <irccd-config.h> -#include <irccd/private/sockets.h> +#include "sockets.h" #if defined(WITH_JS) # include "plugin.h"
--- a/lib/irccd/irccdctl.cpp Sat Mar 26 14:41:53 2016 +0100 +++ b/lib/irccd/irccdctl.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -25,19 +25,17 @@ #include <irccd-config.h> -#include <irccd/private/elapsed-timer.h> -#include <irccd/private/filesystem.h> -#include <irccd/private/ini.h> -#include <irccd/private/sockets.h> - -#include <irccd/json.h> -#include <irccd/logger.h> -#include <irccd/options.h> -#include <irccd/path.h> -#include <irccd/system.h> -#include <irccd/util.h> - +#include "elapsed-timer.h" +#include "filesystem.h" +#include "ini.h" #include "irccdctl.h" +#include "json.h" +#include "logger.h" +#include "options.h" +#include "path.h" +#include "sockets.h" +#include "system.h" +#include "util.h" namespace irccd {
--- a/lib/irccd/irccdctl.h Sat Mar 26 14:41:53 2016 +0100 +++ b/lib/irccd/irccdctl.h Wed Mar 30 13:52:47 2016 +0200 @@ -24,10 +24,9 @@ #include <memory> #include <string> -#include "private/connection.h" - #include "alias.h" #include "application.h" +#include "connection.h" #include "options.h" namespace irccd {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-directory.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,376 @@ +/* + * 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 <irccd-config.h> + +#include "directory.h" +#include "filesystem.h" +#include "js.h" +#include "js-irccd.h" +#include "path.h" + +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. + */ + Directory directory(base); + + for (const DirectoryEntry &entry : directory) + if (entry.type != DirectoryEntry::Dir && pred(entry.name)) + return base + entry.name; + + if (!recursive) + throw std::out_of_range("entry not found"); + + for (const DirectoryEntry &entry : directory) { + if (entry.type == DirectoryEntry::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 (!recursive) { + ::remove(path.c_str()); + } else { + try { + Directory directory(path); + + for (const DirectoryEntry &entry : directory) { + if (entry.type == DirectoryEntry::Dir) { + (void)remove(ctx, path + fs::Separator + entry.name, true); + } else { + std::string filename = path + fs::Separator + entry.name; + + ::remove(filename.c_str()); + } + } + + ::remove(path.c_str()); + } catch (const std::exception &) { + // TODO: put the error in a log. + } + } + + 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 on errors or not found + */ +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 { + Directory directory(duk::require<std::string>(ctx, 0), duk::optional<int>(ctx, 1, 0)); + + duk::push(ctx, duk::This{}); + duk::push(ctx, "count"); + duk::push(ctx, directory.count()); + duk::defineProperty(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); + duk::push(ctx, "path"); + duk::push(ctx, duk::require<std::string>(ctx, 0)); + duk::defineProperty(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); + duk::push(ctx, "entries"); + duk::push(ctx, duk::Array{}); + + int i = 0; + for (const DirectoryEntry &entry : directory) { + duk::push(ctx, duk::Object{}); + duk::putProperty(ctx, -1, "name", entry.name); + duk::putProperty(ctx, -1, "type", static_cast<int>(entry.type)); + duk::putProperty(ctx, -2, 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>(Directory::Dot) }, + { "DotDot", static_cast<int>(Directory::DotDot) }, + { "TypeUnknown", static_cast<int>(DirectoryEntry::Unknown) }, + { "TypeDir", static_cast<int>(DirectoryEntry::Dir) }, + { "TypeFile", static_cast<int>(DirectoryEntry::File) }, + { "TypeLink", static_cast<int>(DirectoryEntry::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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-directory.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,31 @@ +/* + * js-directory.h -- 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_H +#define IRCCD_JS_DIRECTORY_H + +#include "js.h" + +namespace irccd { + +void loadJsDirectory(duk::ContextPtr ctx) noexcept; + +} // !irccd + +#endif // !_IRCCD_JS_DIRECTORY_H_ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-elapsed-timer.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,134 @@ +/* + * 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.h" +#include "js.h" + +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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-elapsed-timer.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,31 @@ +/* + * js-elapsed-timer.h -- 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_H +#define IRCCD_JS_ELAPSED_TIMER_H + +#include "js.h" + +namespace irccd { + +void loadJsElapsedTimer(duk::ContextPtr ctx) noexcept; + +} // !irccd + +#endif // !IRCCD_JS_ELAPSED_TIMER_H +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-file.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,622 @@ +/* + * js-file.h -- 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 <cerrno> +#include <cstring> +#include <fstream> + +#include <irccd-config.h> + +#if defined(HAVE_STAT) +# include <sys/types.h> +# include <sys/stat.h> +#endif + +#include "filesystem.h" +#include "js-irccd.h" +#include "js-file.h" + +#if defined(HAVE_STAT) + +namespace irccd { + +/* + * duk::File object for Javascript I/O + * ------------------------------------------------------------------ + */ + +File::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)); +} + +File::File(std::FILE *fp, std::function<void (std::FILE *)> destructor) noexcept + : m_stream(fp) + , m_destructor(std::move(destructor)) +{ + assert(m_destructor != nullptr); +} + +File::~File() noexcept +{ + close(); +} + +void File::close() noexcept +{ + if (m_stream) { + m_destructor(m_stream); + m_stream = nullptr; + } +} + +bool File::isClosed() noexcept +{ + return m_stream == nullptr; +} + +void File::seek(long amount, long dir) +{ + if (std::fseek(m_stream, amount, dir) != 0) + throw std::runtime_error(std::strerror(errno)); +} + +unsigned File::tell() +{ + long pos = std::ftell(m_stream); + + if (pos == -1L) + throw std::runtime_error(std::strerror(errno)); + + return pos; +} + +std::string File::readline() +{ + std::string result; + int ch; + + while ((ch = std::fgetc(m_stream)) != EOF && ch != '\n') + result += ch; + + if (ch == EOF && std::ferror(m_stream)) + throw std::runtime_error(std::strerror(errno)); + + return result; +} + +std::string File::read(int amount) +{ + assert(amount != 0); + + std::string result; + int ch; + + for (int i = 0; (ch = std::fgetc(m_stream)) != EOF; ) { + result += ch; + + if (amount > 0 && ++i == amount) + break; + } + + if (ch == EOF && std::ferror(m_stream)) + throw std::runtime_error(std::strerror(errno)); + + return result; +} + +void File::write(const std::string &data) +{ + if (std::fwrite(data.c_str(), data.length(), 1, m_stream) != 1) + throw std::runtime_error(std::strerror(errno)); +} + +bool File::eof() const noexcept +{ + return std::feof(m_stream); +} + +/* + * 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 { + +/* -------------------------------------------------------- + * 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.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 self = duk::self<duk::Pointer<File>>(ctx); + + if (amount == 0 || self->isClosed()) + return 0; + + try { + duk::push(ctx, self->read(amount)); + } 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) +{ + try { + auto file = duk::self<duk::Pointer<File>>(ctx); + + if (file->isClosed() || file->eof()) + return 0; + + duk::push(ctx, file->readline()); + } catch (const std::exception &) { + duk::raise(ctx, SystemError()); + } + + 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 file = duk::self<duk::Pointer<File>>(ctx); + + if (file->isClosed()) + return 0; + + try { + file->seek(amount, type); + } catch (const std::exception &) { + 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->isClosed()) + return 0; + + if (::stat(file->path().c_str(), &st) < 0) + duk::raise(ctx, SystemError()); + + 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 file = duk::self<duk::Pointer<File>>(ctx); + + if (file->isClosed()) + return 0; + + try { + duk::push(ctx, static_cast<int>(file->tell())); + } catch (const std::exception &) { + duk::raise(ctx, SystemError()); + } + + return 1; +} + +/* + * Method: File.write(data) + * -------------------------------------------------------- + * + * Write some characters to the file. + * + * Arguments: + * - data, the character to write. + * Throws: + * - Any exception on error. + */ +duk::Ret methodWrite(duk::ContextPtr ctx) +{ + auto file = duk::self<duk::Pointer<File>>(ctx); + + if (file->isClosed()) + return 0; + + try { + file->write(duk::require<std::string>(ctx, 0)); + } catch (const std::exception &) { + duk::raise(ctx, SystemError()); + } + + return 0; +} + +const duk::FunctionMap methods{ + { "basename", { methodBasename, 0 } }, + { "close", { methodClose, 0 } }, + { "dirname", { methodDirname, 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", static_cast<int>(std::fstream::cur) }, + { "SeekEnd", static_cast<int>(std::fstream::end) }, + { "SeekSet", static_cast<int>(std::fstream::beg) }, +}; + +} // !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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-file.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,185 @@ +/* + * js-file.h -- 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_H +#define IRCCD_JS_FILE_H + +#include <cstdio> + +#include "js.h" + +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; + +protected: + 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 + */ + File(std::string path, const std::string &mode); + + /** + * 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 + */ + File(std::FILE *fp, std::function<void (std::FILE *)> destructor) noexcept; + + /** + * Closes the file. + */ + virtual ~File() noexcept; + + /** + * Get the path. + * + * @return the path + * @warning empty when constructed from the FILE constructor + */ + inline const std::string &path() const noexcept + { + return m_path; + } + + /** + * Force close, can be safely called multiple times. + */ + void close() noexcept; + + /** + * Tells if the file was closed. + * + * @return true if closed + */ + bool isClosed() noexcept; + + /** + * std::fseek wrapper. + * + * @param offset the offset + * @param origin the origin (SEEK_SET, *) + * @throw std::runtime_error on failure + */ + void seek(long offset, long origin); + + /** + * std::ftell wrapper. + * + * @return the position + * @throw std::runtime_error on failure + */ + unsigned tell(); + + /** + * Read until the next line and discards the \\n character. + * + * @return the next line or empty if EOF + * @throw std::runtime_error on failure + */ + std::string readline(); + + /** + * Read the specified amount of characters. + * + * If amount is less than 0, the maximum is read. + * + * @pre amount != 0 + * @param amount the number of characters to read + * @return the read string + * @throw std::runtime_error on failure + */ + std::string read(int amount = -1); + + /** + * Write the string to the file. + * + * @param data the data to write + * @throw std::runtime_error on failure + */ + void write(const std::string &data); + + /** + * Check if the file reached the end. + * + * @return true if eof + */ + bool eof() const noexcept; +}; + +namespace duk { + +template <> +class TypeTraits<File> { +public: + 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); + } + + static inline std::string name() + { + return "\xff""\xff""File"; + } + + static inline std::vector<std::string> inherits() + { + return {}; + } +}; + +} // !duk + +void loadJsFile(duk::ContextPtr ctx); + +} // !irccd + +#endif // !IRCCD_JS_FILE_H +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-irccd.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,89 @@ +/* + * 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 <irccd-config.h> + +#include "js-irccd.h" + +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::getGlobal<void>(ctx, "Irccd"); + duk::getProperty<void>(ctx, -1, "SystemError"); + duk::push(ctx, m_errno); + duk::push(ctx, m_message); + duk::create(ctx, 2); + duk::remove(ctx, -2); +} + +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::getGlobal<void>(ctx, "Error"); + duk::push(ctx, duk::Function{[] (duk::ContextPtr) -> duk::Ret { return 0; }}); + duk::getProperty<void>(ctx, -2, "prototype"); + duk::putProperty(ctx, -2, "prototype"); + duk::create(ctx, 0); + duk::putProperty(ctx, -3, "prototype"); + duk::pop(ctx); + duk::dup(ctx, -1); + duk::putProperty(ctx, -2, "constructor"); + duk::putProperty(ctx, -2, "SystemError"); + + /* Set Irccd as global */ + duk::putGlobal(ctx, "Irccd"); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-irccd.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,51 @@ +/* + * js-irccd.h -- 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_H +#define IRCCD_JS_IRCCD_H + +#include <cerrno> +#include <cstring> + +#include "js.h" + +namespace irccd { + +class SystemError { +private: + int m_errno; + std::string m_message; + +public: + SystemError(); + + SystemError(int e, std::string message); + + void raise(duk::ContextPtr ctx) const; + + std::string name() const + { + return "SystemError"; + } +}; + +void loadJsIrccd(duk::Context &); + +} // !irccd + +#endif // !IRCCD_JS_IRCCD_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-logger.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,99 @@ +/* + * js-logger.h -- 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.h" +#include "logger.h" + +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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-logger.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,30 @@ +/* + * js-logger.h -- 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_H +#define IRCCD_JS_LOGGER_H + +#include "js.h" + +namespace irccd { + +void loadJsLogger(duk::ContextPtr ctx); + +} // !irccd + +#endif // !IRCCD_JS_LOGGER_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-plugin.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,183 @@ +/* + * 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.h" +#include "js-plugin.h" + +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) +{ + if (duk::top(ctx) >= 1) { + try { + duk::push(ctx, duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->requirePlugin(duk::require<std::string>(ctx, 0))->info()); + } catch (...) { + duk::push(ctx, duk::Undefined{}); + } + } else { + duk::push(ctx, duk::getGlobal<duk::RawPointer<Plugin>>(ctx, "\xff""\xff""plugin")->info()); + } + + 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 &pair : duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->plugins()) + duk::putProperty(ctx, -1, i++, pair.first); + + 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.loadPlugin(name, name, true); + }); +} + +/* + * 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.reloadPlugin(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.unloadPlugin(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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-plugin.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,31 @@ +/* + * js-plugin.h -- 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_H +#define IRCCD_JS_PLUGIN_H + +#include "js.h" + +namespace irccd { + +void loadJsPlugin(duk::Context &ctx) noexcept; + +} // !irccd + +#endif // !IRCCD_JS_PLUGIN_H +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-server.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,530 @@ +/* + * 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.h" +#include "js-server.h" +#include "server.h" + +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->info().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)->info().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; + + ServerInfo info; + ServerIdentity identity; + ServerSettings settings; + + /* Information part */ + info.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.recotries = duk::optionalProperty<int>(ctx, 0, "recoTries", (int)settings.recotries); + settings.recotimeout = duk::optionalProperty<int>(ctx, 0, "recoTimeout", (int)settings.recotimeout); + + 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(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")->addServer(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->requireServer(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 &pair : duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->servers()) + duk::putProperty(ctx, -1, pair.first, duk::Shared<Server>{pair.second}); + + 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")->removeServer(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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-server.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,59 @@ +/* + * js-server.h -- 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_H +#define IRCCD_JS_SERVER_H + +#include <irccd/server.h> + +#include "js.h" + +namespace irccd { + +void loadJsServer(duk::ContextPtr ctx); + +namespace duk { + +template <> +class TypeTraits<irccd::Server> { +public: + static inline void prototype(ContextPtr ctx) + { + getGlobal<void>(ctx, "Irccd"); + getGlobal<void>(ctx, "Server"); + getProperty<void>(ctx, -1, "prototype"); + remove(ctx, -2); + remove(ctx, -2); + } + + static inline std::string name() + { + return "\xff""\xff""Server"; + } + + static inline std::vector<std::string> inherits() + { + return {}; + } +}; + +} // !duk + +} // !irccd + +#endif // !IRCCD_JS_SERVER_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-system.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,237 @@ +/* + * 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 <irccd-config.h> + +#include <chrono> + +#if defined(HAVE_POPEN) +# include <cstdio> +#endif + +#include <cstdlib> +#include <thread> + +#include "js-file.h" +#include "js-irccd.h" +#include "js-system.h" +#include "system.h" + +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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-system.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,30 @@ +/* + * js-system.h -- 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_H +#define IRCCD_JS_SYSTEM_H + +#include "js.h" + +namespace irccd { + +void loadJsSystem(duk::ContextPtr ctx); + +} // !irccd + +#endif // !IRCCD_JS_SYSTEM_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-timer.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,140 @@ +/* + * 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.h" +#include "plugin.h" + +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<Plugin>>(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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-timer.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,31 @@ +/* + * js-timer.h -- 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_H +#define IRCCD_JS_TIMER_H + +#include "js.h" + +namespace irccd { + +void loadJsTimer(duk::ContextPtr ctx) noexcept; + +} // !irccd + +#endif // !IRCCD_JS_TIMER_H +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-unicode.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,144 @@ +/* + * 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.h" +#include "unicode.h" + +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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-unicode.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,30 @@ +/* + * 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_H +#define IRCCD_JS_UNICODE_H + +#include "js.h" + +namespace irccd { + +void loadJsUnicode(duk::ContextPtr ctx); + +} // !irccd + +#endif // !IRCCD_JS_UNICODE_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-util.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,150 @@ +/* + * 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.h" +#include "util.h" + +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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js-util.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,30 @@ +/* + * js-util.h -- 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_H +#define IRCCD_JS_UTIL_H + +#include "js.h" + +namespace irccd { + +void loadJsUtil(duk::ContextPtr ctx); + +} // !irccd + +#endif // !IRCCD_JS_UTIL_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/js.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,2381 @@ +/* + * js.h -- JavaScript C++14 wrapper for Duktape + * + * Copyright (c) 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 JS_H +#define JS_H + +/** + * @file js.h + * @brief Bring JavaScript using Duktape. + * + * This file provides usual Duktape function renamed and placed into `duk` namespace. It also replaces error + * code with exceptions when possible. + * + * For convenience, this file also provides templated functions, overloads and much more. + */ + +#include <cassert> +#include <functional> +#include <memory> +#include <string> +#include <type_traits> +#include <unordered_map> +#include <utility> +#include <vector> + +#include <duktape.h> + +namespace irccd { + +/** + * Duktape C++ namespace wrapper. + */ +namespace duk { + +class Context; + +using CodePoint = duk_codepoint_t; +using ContextPtr = duk_context *; +using Index = duk_idx_t; +using Ret = duk_ret_t; +using Size = duk_size_t; + +/** + * @class StackAssert + * @brief Stack sanity checker. + * + * Instanciate this class where you need to manipulate the Duktape stack outside a Duktape/C function, its destructor + * will examinate if the stack size matches the user expected size. + * + * When compiled with NDEBUG, this class does nothing. + * + * To use it, just declare an lvalue at the beginning of your function. + */ +class StackAssert { +#if !defined(NDEBUG) +private: + ContextPtr m_context; + unsigned m_expected; + unsigned m_begin; +#endif + +public: + /** + * Create the stack checker. + * + * No-op if NDEBUG is set. + * + * @param ctx the context + * @param expected the size expected relative to the already existing values + */ + inline StackAssert(ContextPtr ctx, unsigned expected = 0) noexcept +#if !defined(NDEBUG) + : m_context(ctx) + , m_expected(expected) + , m_begin(static_cast<unsigned>(duk_get_top(ctx))) +#endif + { +#if defined(NDEBUG) + (void)ctx; + (void)expected; +#endif + } + + /** + * Verify the expected size. + * + * No-op if NDEBUG is set. + */ + inline ~StackAssert() noexcept + { +#if !defined(NDEBUG) + assert((unsigned)duk_get_top(m_context) - m_begin == m_expected); +#endif + } +}; + +/** + * @class Object + * @brief Empty class tag for push() function. + */ +class Object { +}; + +/** + * @class Array + * @brief Empty class tag for push() function. + */ +class Array { +}; + +/** + * @class Global + * @brief Empty class tag to push the global object. + */ +class Global { +}; + +/** + * @class Undefined + * @brief Empty class tag to push undefined to the stack. + */ +class Undefined { +}; + +/** + * @class Null + * @brief Empty class tag to push null to the stack. + */ +class Null { +}; + +/** + * @class This + * @brief Empty class tag to push this binding to the stack. + */ +class This { +}; + +/** + * @class RawPointer + * @brief Push a non-managed pointer to Duktape, the pointer will never be deleted. + * @note For a managed pointer with prototype, see Pointer + */ +template <typename T> +class RawPointer { +public: + /** + * The pointer to push. + */ + T *object; +}; + +/** + * @brief Manage shared_ptr from C++ and JavaScript + * + * This class allowed you to push and retrieve shared_ptr from C++ and JavaScript without taking care of ownership + * and deletion. + * + */ +template <typename T> +class Shared { +public: + /** + * The shared object. + */ + std::shared_ptr<T> object; +}; + +/** + * @brief Manage pointers from C++ and JavaScript + * + * This class allowed you to push and retrieve C++ pointers from C++ and JavaScript. The object will be deleted when + * the JavaScript garbage collectors collect them so never store a pointer created with this. + * + * The only requirement is to have the function `void prototype(Context &ctx)` in your class T. + */ +template <typename T> +class Pointer { +public: + /** + * The object. + */ + T *object{nullptr}; +}; + +/** + * @class Function + * @brief Duktape/C function definition. + * + * This class wraps the std::function as a Duktape/C function by storing a copied pointer. + */ +class Function { +public: + /** + * The function pointer, must not be null. + */ + duk_c_function function; + + /** + * Number of args that the function takes + */ + duk_idx_t nargs{0}; +}; + +/** + * Map of functions to set on an object. + */ +using FunctionMap = std::unordered_map<std::string, Function>; + +/** + * Map of string to type, ideal for setting constants like enums. + */ +template <typename Type> +using Map = std::unordered_map<std::string, Type>; + +/** + * @class ErrorInfo + * @brief Error description. + * + * This class fills the fields got in an Error object. + */ +class ErrorInfo : public std::exception { +public: + std::string name; //!< name of error + std::string message; //!< error message + std::string stack; //!< stack if available + std::string fileName; //!< filename if applicable + int lineNumber{0}; //!< line number if applicable + + /** + * Get the error message. This effectively returns message field. + * + * @return the message + */ + const char *what() const noexcept override + { + return message.c_str(); + } +}; + +/** + * @class TypeTraits + * @brief Type information to implement new types in JavaScript's context. + * + * This class depending on your needs may have the following functions: + * + * - `static void construct(Context &ctx, Type value)` + * - `static Type get(Context &ctx, int index)` + * - `static bool is(Context &ctx, int index)` + * - `static Type optional(Context &ctx, int index, Type defaultValue)` + * - `static void push(Context &ctx, Type value)` + * - `static Type require(Context &ctx, int index)` + * + * The `construct` function is used in Context::construct to build a new value as this (e.g. constructors). + * + * The `get` function is used in Context::get, Context::getProperty, Context::getGlobal to retrieve a value from the + * stack. + * + * The `is` function is used in Context::is to check if the value on the stack is of type `Type`. + * + * The `optional` function is used in Context::optional to get a value or a replacement if not applicable. + * + * The `push` function is used in Context::push to usually create a new value on the stack but some specializations + * may not (e.g. FunctionMap). + * + * The `require` function is used in Context::require to get a value from the stack or raise a JavaScript exception if + * not applicable. + * + * This class is fully specialized for: `bool`, `const char *`, `double`, `int`, `std::string`. + * + * It is also partially specialized for : `Global`, `Object`, `Array`, `Undefined`, `Null`, `std::vector<Type>`. + */ +template <typename Type> +class TypeTraits { +}; + +/** + * @class Context + * @brief RAII based Duktape handler. + * + * This class is implicitly convertible to duk_context for convenience. + */ +class Context { +private: + using Deleter = void (*)(duk_context *); + using Handle = std::unique_ptr<duk_context, Deleter>; + + Handle m_handle; + + Context(const Context &) = delete; + Context &operator=(const Context &) = delete; + Context(const Context &&) = delete; + Context &operator=(const Context &&) = delete; + +public: + /** + * Create default context. + */ + inline Context() + : m_handle(duk_create_heap_default(), duk_destroy_heap) + { + } + + /** + * Convert the context to the native Duktape/C type. + * + * @return the duk_context + */ + inline operator duk_context *() noexcept + { + return m_handle.get(); + } + + /** + * Convert the context to the native Duktape/C type. + * + * @return the duk_context + */ + inline operator duk_context *() const noexcept + { + return m_handle.get(); + } +}; + +/** + * Get the error object when a JavaScript error has been thrown (e.g. eval failure). + * + * @param ctx the context + * @param index the index + * @return the information + */ +inline ErrorInfo error(ContextPtr ctx, int index) +{ + ErrorInfo error; + + index = duk_normalize_index(ctx, index); + + duk_get_prop_string(ctx, index, "name"); + error.name = duk_to_string(ctx, -1); + duk_get_prop_string(ctx, index, "message"); + error.message = duk_to_string(ctx, -1); + duk_get_prop_string(ctx, index, "fileName"); + error.fileName = duk_to_string(ctx, -1); + duk_get_prop_string(ctx, index, "lineNumber"); + error.lineNumber = duk_to_int(ctx, -1); + duk_get_prop_string(ctx, index, "stack"); + error.stack = duk_to_string(ctx, -1); + duk_pop_n(ctx, 5); + + return error; +} + +/** + * Wrapper for [duk_base64_decode](http://duktape.org/api.html#duk_base64_decode). + * + * @param ctx the context + * @param index the index + */ +inline void base64Decode(ContextPtr ctx, Index index) +{ + duk_base64_decode(ctx, index); +} + +/** + * Wrapper for [duk_base64_encode](http://duktape.org/api.html#duk_base64_encode). + * + * @param ctx the context + * @param index the index + */ +inline void base64Encode(ContextPtr ctx, Index index) +{ + duk_base64_encode(ctx, index); +} + +/** + * Wrapper for [duk_call](http://duktape.org/api.html#duk_call). + * + * @param ctx the context + * @param nargs the number of arguments + */ +inline void call(ContextPtr ctx, Index nargs = 0) +{ + duk_call(ctx, nargs); +} + +/** + * Wrapper for [duk_call_method](http://duktape.org/api.html#duk_call_method). + * + * @param ctx the context + * @param nargs the number of arguments + */ +inline void callMethod(ContextPtr ctx, Index nargs = 0) +{ + duk_call_method(ctx, nargs); +} + +/** + * Wrapper for [duk_call_prop](http://duktape.org/api.html#duk_call_prop). + * + * @param ctx the context + * @param index the object index + * @param nargs the number of arguments + */ +inline void callProperty(ContextPtr ctx, Index index, Index nargs = 0) +{ + duk_call_prop(ctx, index, nargs); +} + +/** + * Wrapper for [duk_char_code_at](http://duktape.org/api.html#duk_char_code_at). + * + * @param ctx the context + * @param index the index + * @param charOffset the offset + */ +inline CodePoint charCodeAt(ContextPtr ctx, Index index, Size charOffset) +{ + return duk_char_code_at(ctx, index, charOffset); +} + +/** + * Wrapper for [duk_check_stack](http://duktape.org/api.html#duk_check_stack). + * + * @param ctx the context + * @param extra the extra space + * @return true if space is available + */ +inline bool checkStack(ContextPtr ctx, Index extra) +{ + return duk_check_stack(ctx, extra); +} + +/** + * Wrapper for [duk_check_stack_top](http://duktape.org/api.html#duk_check_stack_top). + * + * @param ctx the context + * @param top the extra space + * @return true if space is available + */ +inline bool checkStackTop(ContextPtr ctx, Index top) +{ + return duk_check_stack_top(ctx, top); +} + +/** + * Wrapper for [duk_check_type](http://duktape.org/api.html#duk_check_type). + * + * @param ctx the context + * @param index the value index + * @param type the desired type + * @return true if object is given type + */ +inline bool checkType(ContextPtr ctx, Index index, int type) +{ + return duk_check_type(ctx, index, type); +} + +/** + * Wrapper for [duk_check_type_mask](http://duktape.org/api.html#duk_check_type_mask). + * + * @param ctx the context + * @param index the value index + * @param mask the desired mask + * @return true if object is one of the type + */ +inline bool checkTypeMask(ContextPtr ctx, Index index, unsigned mask) +{ + return duk_check_type_mask(ctx, index, mask); +} + +/** + * Wrapper for [duk_compact](http://duktape.org/api.html#duk_compact). + * + * @param ctx the context + * @param objIndex the object index + */ +inline void compact(ContextPtr ctx, Index objIndex) +{ + duk_compact(ctx, objIndex); +} + +/** + * Wrapper for [duk_concat](http://duktape.org/api.html#duk_concat). + * + * @param ctx the context + * @param count the number of values + */ +inline void concat(ContextPtr ctx, Index count) +{ + duk_concat(ctx, count); +} + +/** + * Wrapper for [duk_copy](http://duktape.org/api.html#duk_copy). + * + * @param from the from index + * @param to the destination + */ +inline void copy(ContextPtr ctx, Index from, Index to) +{ + duk_copy(ctx, from, to); +} + +/** + * Wrapper for [duk_def_prop](http://duktape.org/api.html#duk_def_prop). + * + * @param index the object index + * @param flags the flags + */ +inline void defineProperty(ContextPtr ctx, Index index, unsigned flags) +{ + duk_def_prop(ctx, index, flags); +} + +/** + * Wrapper for [duk_del_prop](http://duktape.org/api.html#duk_del_prop). + * + * @param index the object index + * @return true if deleted + */ +inline bool deleteProperty(ContextPtr ctx, Index index) +{ + return duk_del_prop(ctx, index); +} + +/** + * Wrapper for [duk_del_prop](http://duktape.org/api.html#duk_del_prop). + * + * @param index the object index + * @param position the property index + * @return true if deleted + */ +inline bool deleteProperty(ContextPtr ctx, Index index, unsigned position) +{ + return duk_del_prop_index(ctx, index, position); +} + +/** + * Wrapper for [duk_del_prop](http://duktape.org/api.html#duk_del_prop). + * + * @param index the object index + * @param name the property name + * @return true if deleted + */ +inline bool deleteProperty(ContextPtr ctx, Index index, const std::string &name) +{ + return duk_del_prop_string(ctx, index, name.c_str()); +} + +/** + * Wrapper for [duk_dup](http://duktape.org/api.html#duk_dup). + * + * @param index the value to copy + */ +inline void dup(ContextPtr ctx, int index = -1) +{ + duk_dup(ctx, index); +} + +/** + * Wrapper for [duk_equals](http://duktape.org/api.html#duk_equals). + * + * @param ctx the context + * @param index1 the first value + * @param index2 the second value + * @return true if they equal + */ +inline bool equals(ContextPtr ctx, Index index1, Index index2) +{ + return duk_equals(ctx, index1, index2); +} + +/** + * Wrapper for [duk_eval](http://duktape.org/api.html#duk_eval). + * + * @param ctx the context + */ +inline void eval(ContextPtr ctx) +{ + duk_eval(ctx); +} + +/** + * Wrapper for [duk_eval_file](http://duktape.org/api.html#duk_eval_file). + * + * @param ctx the context + * @param path the path + * @param result true to get the result at the top of the stack + */ +inline void evalFile(ContextPtr ctx, const std::string &path, bool result = true) +{ + if (result) + duk_eval_file(ctx, path.c_str()); + else + duk_eval_file_noresult(ctx, path.c_str()); +} + +/** + * Wrapper for [duk_eval_string](http://duktape.org/api.html#duk_eval_string). + * + * @param ctx the context + * @param src the source script + * @param result true to get the result at the top of the stack + */ +inline void evalString(ContextPtr ctx, const std::string &src, bool result = true) +{ + if (result) + duk_eval_string(ctx, src.c_str()); + else + duk_eval_string_noresult(ctx, src.c_str()); +} +/** + * Wrapper for [duk_gc](http://duktape.org/api.html#duk_gc). + * + * @param ctx the context + * @param flags the flags + */ +inline void gc(ContextPtr ctx, unsigned flags = 0) +{ + duk_gc(ctx, flags); +} + +/** + * Wrapper for [duk_has_prop](http://duktape.org/api.html#duk_has_prop). + * + * @param ctx the context + * @param index the object index + * @return true if has + */ +inline bool hasProperty(ContextPtr ctx, Index index) +{ + return duk_has_prop(ctx, index); +} + +/** + * Wrapper for [duk_has_prop](http://duktape.org/api.html#duk_has_prop). + * + * @param ctx the context + * @param index the object index + * @param position the property index + * @return true if has + */ +inline bool hasProperty(ContextPtr ctx, Index index, unsigned position) +{ + return duk_has_prop_index(ctx, index, position); +} + +/** + * Wrapper for [duk_has_prop](http://duktape.org/api.html#duk_has_prop). + * + * @param ctx the context + * @param index the object index + * @param name the property name + * @return true if has + */ +inline bool hasProperty(ContextPtr ctx, int index, const std::string &name) +{ + return duk_has_prop_string(ctx, index, name.c_str()); +} + +/** + * Wrapper for [duk_insert](http://duktape.org/api.html#duk_insert). + * + * @param ctx the context + * @param to the destination + * @note Wrapper of duk_insert + */ +inline void insert(ContextPtr ctx, Index to) +{ + duk_insert(ctx, to); +} + +/** + * Wrapper for [duk_instanceof](http://duktape.org/api.html#duk_instanceof). + * + * @param ctx the context + * @param idx1 the value to test + * @param idx2 the instance requested + * @return true if idx1 is instance of idx2 + */ +inline bool instanceof(ContextPtr ctx, Index idx1, Index idx2) +{ + return duk_instanceof(ctx, idx1, idx2); +} + +/** + * Wrapper for [duk_join](http://duktape.org/api.html#duk_join). + * + * @param ctx the context + * @param count the number of values + */ +inline void join(ContextPtr ctx, Index count) +{ + duk_join(ctx, count); +} + +/** + * Wrapper for [duk_json_decode](http://duktape.org/api.html#duk_json_decode). + * + * @param ctx the context + * @param index the index + */ +inline void jsonDecode(ContextPtr ctx, Index index) +{ + duk_json_decode(ctx, index); +} + +/** + * Wrapper for [duk_json_encode](http://duktape.org/api.html#duk_json_encode). + * + * @param ctx the context + * @param index the index + */ +inline void jsonEncode(ContextPtr ctx, Index index) +{ + duk_json_encode(ctx, index); +} + +/** + * Wrapper for [duk_normalize_index](http://duktape.org/api.html#duk_normalize_index). + * + * @param ctx the context + * @param index the index + */ +inline Index normalizeIndex(ContextPtr ctx, Index index) +{ + return duk_normalize_index(ctx, index); +} + +/** + * Wrapper for [duk_pcall](http://duktape.org/api.html#duk_pcall). + * + * @param ctx the context + * @param nargs the number of arguments + */ +inline int pcall(ContextPtr ctx, Index nargs = 0) +{ + return duk_pcall(ctx, nargs); +} + +/** + * Wrapper for [duk_peval](http://duktape.org/api.html#duk_peval). + * + * @param ctx the context + */ +inline int peval(ContextPtr ctx) +{ + return duk_peval(ctx); +} + +/** + * Wrapper for [duk_peval_file](http://duktape.org/api.html#duk_peval_file). + * + * @param ctx the context + * @param path the path + * @param result true to get the result at the top of the stack + */ +inline int pevalFile(ContextPtr ctx, const std::string &path, bool result = true) +{ + return result ? duk_peval_file(ctx, path.c_str()) : duk_peval_file_noresult(ctx, path.c_str()); +} + +/** + * Wrapper for [duk_peval_string](http://duktape.org/api.html#duk_peval_string). + * + * @param ctx the context + * @param src the source script + * @param result true to get the result at the top of the stack + */ +inline int pevalString(ContextPtr ctx, const std::string &src, bool result = true) +{ + return result ? duk_peval_string(ctx, src.c_str()) : duk_peval_string_noresult(ctx, src.c_str()); +} + +/** + * Wrapper for [duk_pop_n](http://duktape.org/api.html#duk_pop_n). + * + * @param ctx the context + * @param count the number of values to pop + */ +inline void pop(ContextPtr ctx, Index count = 1) +{ + duk_pop_n(ctx, count); +} + +/** + * Wrapper for [duk_remove](http://duktape.org/api.html#duk_remove). + * + * @param ctx the context + * @param index the value to remove + */ +inline void remove(ContextPtr ctx, Index index) +{ + duk_remove(ctx, index); +} + +/** + * Wrapper for [duk_replace](http://duktape.org/api.html#duk_replace). + * + * @param ctx the context + * @param index the value to replace by the value at the top of the stack + */ +inline void replace(ContextPtr ctx, Index index) +{ + duk_replace(ctx, index); +} + +/** + * Wrapper for [duk_set_prototype](http://duktape.org/api.html#duk_set_prototype). + * + * @param ctx the context + * @param index the value index + */ +inline void setPrototype(ContextPtr ctx, Index index) +{ + duk_set_prototype(ctx, index); +} + +/** + * Wrapper for [duk_swap](http://duktape.org/api.html#duk_swap). + * + * @param ctx the context + * @param index1 the first index + * @param index2 the second index + */ +inline void swap(ContextPtr ctx, Index index1, Index index2) +{ + duk_swap(ctx, index1, index2); +} + +/** + * Wrapper for [duk_swap_top](http://duktape.org/api.html#duk_swap_top). + * + * @param ctx the context + * @param index the index + */ +inline void swapTop(ContextPtr ctx, Index index) +{ + duk_swap_top(ctx, index); +} + +/** + * Wrapper for [duk_get_top](http://duktape.org/api.html#duk_get_top). + * + * @param ctx the context + * @return the stack size + */ +inline int top(ContextPtr ctx) noexcept +{ + return duk_get_top(ctx); +} + +/** + * Wrapper for [duk_get_type](http://duktape.org/api.html#duk_get_type). + * + * @param ctx the context + * @param index the idnex + * @return the type + */ +inline int type(ContextPtr ctx, Index index) noexcept +{ + return duk_get_type(ctx, index); +} + +/* + * Push / Get / Require / Is / Optional + * ---------------------------------------------------------- + * + * The following functions are used to push, get or check values from the stack. They use specialization + * of TypeTraits class. + */ + +/** + * Push a value into the stack. Calls TypeTraits<T>::push(*this, value); + * + * @param value the value to forward + */ +template <typename Type> +inline void push(ContextPtr ctx, Type &&value) +{ + TypeTraits<std::decay_t<Type>>::push(ctx, std::forward<Type>(value)); +} + +/** + * Generic template function to get a value from the stack. + * + * @param index the index + * @return the value + */ +template <typename Type> +inline auto get(ContextPtr ctx, int index) -> decltype(TypeTraits<Type>::get(ctx, 0)) +{ + return TypeTraits<Type>::get(ctx, index); +} + +/** + * Require a type at the specified index. + * + * @param index the index + * @return the value + */ +template <typename Type> +inline auto require(ContextPtr ctx, int index) -> decltype(TypeTraits<Type>::require(ctx, 0)) +{ + return TypeTraits<Type>::require(ctx, index); +} + +/** + * Check if a value is a type of T. + * + * The TypeTraits<T> must have `static bool is(ContextPtr ptr, int index)`. + * + * @param index the value index + * @return true if is the type + */ +template <typename T> +inline bool is(ContextPtr ctx, int index) +{ + return TypeTraits<T>::is(ctx, index); +} + +/** + * Get an optional value from the stack, if the value is not available of not the correct type, + * return defaultValue instead. + * + * The TypeTraits<T> must have `static T optional(Context &, int index, T &&defaultValue)`. + * + * @param index the value index + * @param defaultValue the value replacement + * @return the value or defaultValue + */ +template <typename Type> +inline auto optional(ContextPtr ctx, int index, Type &&defaultValue) +{ + return TypeTraits<std::decay_t<Type>>::optional(ctx, index, std::forward<Type>(defaultValue)); +} + +/* + * Properties management + * ---------------------------------------------------------- + * + * The following functions are used to read or set properties on objects or globals also using TypeTraits. + */ + +/** + * Get the property `name' as value from the object at the specified index. + * + * @param index the object index + * @param name the property name + * @return the value + * @note The stack is unchanged + */ +template <typename Type, typename std::enable_if_t<!std::is_void<Type>::value> * = nullptr> +inline auto getProperty(ContextPtr ctx, int index, const std::string &name) -> decltype(get<Type>(ctx, 0)) +{ + duk_get_prop_string(ctx, index, name.c_str()); + decltype(get<Type>(ctx, 0)) value = get<Type>(ctx, -1); + duk_pop(ctx); + + return value; +} + +/** + * Get a property by index, for arrays. + * + * @param index the object index + * @param position the position int the object + * @return the value + * @note The stack is unchanged + */ +template <typename Type, typename std::enable_if_t<!std::is_void<Type>::value> * = nullptr> +inline auto getProperty(ContextPtr ctx, int index, int position) -> decltype(get<Type>(ctx, 0)) +{ + duk_get_prop_index(ctx, index, position); + decltype(get<Type>(ctx, 0)) value = get<Type>(ctx, -1); + duk_pop(ctx); + + return value; +} + +/** + * Get the property `name' and push it to the stack from the object at the specified index. + * + * @param index the object index + * @param name the property name + * @note The stack contains the property value + */ +template <typename Type, typename std::enable_if_t<std::is_void<Type>::value> * = nullptr> +inline void getProperty(ContextPtr ctx, int index, const std::string &name) +{ + duk_get_prop_string(ctx, index, name.c_str()); +} + +/** + * Get the property by index and push it to the stack from the object at the specified index. + * + * @param index the object index + * @param position the position in the object + * @note The stack contains the property value + */ +template <typename Type, typename std::enable_if_t<std::is_void<Type>::value> * = nullptr> +inline void getProperty(ContextPtr ctx, int index, int position) +{ + duk_get_prop_index(ctx, index, position); +} + +/** + * Get an optional property `name` from the object at the specified index. + * + * @param index the object index + * @param name the property name + * @param def the default value + * @return the value or def + * @note The stack is unchanged + */ +template <typename Type, typename DefaultValue> +inline auto optionalProperty(ContextPtr ctx, int index, const std::string &name, DefaultValue &&def) -> decltype(optional(ctx, 0, std::forward<DefaultValue>(def))) +{ + duk_get_prop_string(ctx, index, name.c_str()); + decltype(optional(ctx, 0, std::forward<DefaultValue>(def))) value = optional(ctx, -1, std::forward<DefaultValue>(def)); + duk_pop(ctx); + + return value; +} + +/** + * Get an optional property by index, for arrays + * + * @param index the object index + * @param position the position int the object + * @param def the default value + * @return the value or def + * @note The stack is unchanged + */ +template <typename Type, typename DefaultValue> +inline auto optionalProperty(ContextPtr ctx, int index, int position, DefaultValue &&def) -> decltype(optional(ctx, 0, std::forward<DefaultValue>(def))) +{ + duk_get_prop_index(ctx, index, position); + decltype(optional(ctx, 0, std::forward<DefaultValue>(def))) value = optional(ctx, -1, std::forward<DefaultValue>(def)); + duk_pop(ctx); + + return value; +} + +/** + * Set a property to the object at the specified index. + * + * @param index the object index + * @param name the property name + * @param value the value to forward + * @note The stack is unchanged + */ +template <typename Type> +void putProperty(ContextPtr ctx, int index, const std::string &name, Type &&value) +{ + index = duk_normalize_index(ctx, index); + + push(ctx, std::forward<Type>(value)); + duk_put_prop_string(ctx, index, name.c_str()); +} + +/** + * Set a property by index, for arrays. + * + * @param index the object index + * @param position the position in the object + * @param value the value to forward + * @note The stack is unchanged + */ +template <typename Type> +void putProperty(ContextPtr ctx, int index, int position, Type &&value) +{ + index = duk_normalize_index(ctx, index); + + push(ctx, std::forward<Type>(value)); + duk_put_prop_index(ctx, index, position); +} + +/** + * Put the value that is at the top of the stack as property to the object. + * + * @param index the object index + * @param name the property name + */ +inline void putProperty(ContextPtr ctx, int index, const std::string &name) +{ + duk_put_prop_string(ctx, index, name.c_str()); +} + +/** + * Put the value that is at the top of the stack to the object as index. + * + * @param index the object index + * @param position the position in the object + */ +inline void putProperty(ContextPtr ctx, int index, int position) +{ + duk_put_prop_index(ctx, index, position); +} + +/** + * Get a global value. + * + * @param name the name of the global variable + * @return the value + */ +template <typename Type> +inline auto getGlobal(ContextPtr ctx, const std::string &name, std::enable_if_t<!std::is_void<Type>::value> * = nullptr) -> decltype(get<Type>(ctx, 0)) +{ + duk_get_global_string(ctx, name.c_str()); + decltype(get<Type>(ctx, 0)) value = get<Type>(ctx, -1); + duk_pop(ctx); + + return value; +} + +/** + * Overload that push the value at the top of the stack instead of returning it. + */ +template <typename Type> +inline void getGlobal(ContextPtr ctx, const std::string &name, std::enable_if_t<std::is_void<Type>::value> * = nullptr) noexcept +{ + duk_get_global_string(ctx, name.c_str()); +} + +/** + * Set a global variable. + * + * @param name the name of the global variable + * @param type the value to set + */ +template <typename Type> +inline void putGlobal(ContextPtr ctx, const std::string &name, Type&& type) +{ + push(ctx, std::forward<Type>(type)); + duk_put_global_string(ctx, name.c_str()); +} + +/** + * Put the value at the top of the stack as global property. + * + * @param name the property name + */ +inline void putGlobal(ContextPtr ctx, const std::string &name) +{ + duk_put_global_string(ctx, name.c_str()); +} + +/* + * Extra functions + * ---------------------------------------------------------- + * + * The following functions are implemented for convenience and do not exists in the native Duktape API. + */ + +/** + * Enumerate an object or an array at the specified index. + * + * @param index the object or array index + * @param flags the optional flags to pass to duk_enum + * @param getvalue set to true if you want to extract the value + * @param func the function to call for each properties + */ +template <typename Func> +void enumerate(ContextPtr ctx, int index, duk_uint_t flags, duk_bool_t getvalue, Func &&func) +{ + duk_enum(ctx, index, flags); + + while (duk_next(ctx, -1, getvalue)) { + func(ctx); + duk_pop_n(ctx, 1 + (getvalue ? 1 : 0)); + } + + duk_pop(ctx); +} + +/** + * Return the this binding of the current function. + * + * @return the this binding as the template given + */ +template <typename T> +inline auto self(ContextPtr ctx) -> decltype(TypeTraits<T>::get(ctx, 0)) +{ + duk_push_this(ctx); + decltype(TypeTraits<T>::get(ctx, 0)) value = TypeTraits<T>::get(ctx, -1); + duk_pop(ctx); + + return value; +} + +/** + * Throw an ECMAScript exception. + * + * @param ex the exception + */ +template <typename Exception> +void raise(ContextPtr ctx, const Exception &ex) +{ + ex.raise(ctx); +} + +/** + * Wrapper for duk_throw. + * + * @param ctx the context + */ +inline void raise(ContextPtr ctx) +{ + duk_throw(ctx); +} + +/** + * Wrapper for duk_error. + * + * @param ctx the context + * @param type the error type (e.g. DUK_ERR_REFERENCE_ERROR) + * @param fmt the format string + * @param args the arguments + */ +template <typename... Args> +inline void raise(ContextPtr ctx, int type, const char *fmt, Args&&... args) +{ + duk_error(ctx, type, fmt, std::forward<Args>(args)...); +} + +/** + * Wrapper for duk_new. + * + * @param ctx the context + * @param nargs the number of arguments + */ +inline void create(ContextPtr ctx, int nargs = 0) +{ + duk_new(ctx, nargs); +} + +/** + * Construct the object in place, setting value as this binding. + * + * The TypeTraits<T> must have the following requirements: + * + * - static void construct(Context &, T): must update this with the value and keep the stack unchanged + * + * @param value the value to forward + * @see self + */ +template <typename T> +inline void construct(ContextPtr ctx, T &&value) +{ + TypeTraits<std::decay_t<T>>::construct(ctx, std::forward<T>(value)); +} + +/** + * Sign the given object with the name from T. + * + * This is automatically done for when constructing/pushing object with Shared and Pointer helpers, however you need + * to manually add it when using inheritance. + */ +template <typename T> +inline void sign(ContextPtr ctx, Index index) +{ + StackAssert sa(ctx, 0); + + index = duk_normalize_index(ctx, index); + + duk_push_string(ctx, TypeTraits<T>::name().c_str()); + duk_push_boolean(ctx, true); + duk_def_prop(ctx, index < 0 ? index : index, DUK_DEFPROP_HAVE_VALUE); + + /* Do for inherited classes */ + for (const std::string &parent : TypeTraits<T>::inherits()) { + duk_push_string(ctx, parent.c_str()); + duk_push_boolean(ctx, true); + duk_def_prop(ctx, index < 0 ? index : index, DUK_DEFPROP_HAVE_VALUE); + } +} + +/** + * Check if the object at the given index is signed by T or raise TypeError if not. + * + * @param ctx the context + * @param index the index + * @see sign + */ +template <typename T> +inline void checkSignature(ContextPtr ctx, Index index) +{ + StackAssert sa(ctx, 0); + + if (!is<Object>(ctx, index) || !getProperty<bool>(ctx, index, TypeTraits<T>::name())) + raise(ctx, DUK_ERR_TYPE_ERROR, "invalid this binding"); +} + +/** + * Tells if the object at the specified index is of type T. + * + * @param ctx the context + * @param index the index + */ +template <typename T> +inline bool isSigned(ContextPtr ctx, Index index) +{ + StackAssert sa(ctx, 0); + + return is<Object>(ctx, index) && getProperty<bool>(ctx, index, TypeTraits<T>::name()); +} + +/* ------------------------------------------------------------------ + * Exception handling + * ------------------------------------------------------------------ */ + +/** + * @class Error + * @brief Base ECMAScript error class. + * @warning Override the function create for your own exceptions + */ +class Error { +private: + int m_type{DUK_ERR_ERROR}; + std::string m_message; + +protected: + /** + * Constructor with a type of error specified, specially designed for derived errors. + * + * @param type of error (e.g. DUK_ERR_ERROR) + * @param message the message + */ + inline Error(int type, std::string message) noexcept + : m_type(type) + , m_message(std::move(message)) + { + } + +public: + /** + * Constructor with a message. + * + * @param message the message + */ + inline Error(std::string message) noexcept + : m_message(std::move(message)) + { + } + + /** + * Create the exception on the stack. + * + * @note the default implementation search for the global variables + * @param ctx the context + */ + virtual void raise(ContextPtr ctx) const noexcept + { + duk_error(ctx, m_type, "%s", m_message.c_str()); + } +}; + +/** + * @class EvalError + * @brief Error in eval() function. + */ +class EvalError : public Error { +public: + /** + * Construct an EvalError. + * + * @param message the message + */ + inline EvalError(std::string message) noexcept + : Error(DUK_ERR_EVAL_ERROR, std::move(message)) + { + } +}; + +/** + * @class RangeError + * @brief Value is out of range. + */ +class RangeError : public Error { +public: + /** + * Construct an RangeError. + * + * @param message the message + */ + inline RangeError(std::string message) noexcept + : Error(DUK_ERR_RANGE_ERROR, std::move(message)) + { + } +}; + +/** + * @class ReferenceError + * @brief Trying to use a variable that does not exist. + */ +class ReferenceError : public Error { +public: + /** + * Construct an ReferenceError. + * + * @param message the message + */ + inline ReferenceError(std::string message) noexcept + : Error(DUK_ERR_REFERENCE_ERROR, std::move(message)) + { + } +}; + +/** + * @class SyntaxError + * @brief Syntax error in the script. + */ +class SyntaxError : public Error { +public: + /** + * Construct an SyntaxError. + * + * @param message the message + */ + inline SyntaxError(std::string message) noexcept + : Error(DUK_ERR_SYNTAX_ERROR, std::move(message)) + { + } +}; + +/** + * @class TypeError + * @brief Invalid type given. + */ +class TypeError : public Error { +public: + /** + * Construct an TypeError. + * + * @param message the message + */ + inline TypeError(std::string message) noexcept + : Error(DUK_ERR_TYPE_ERROR, std::move(message)) + { + } +}; + +/** + * @class URIError + * @brief URI manipulation failure. + */ +class URIError : public Error { +public: + /** + * Construct an URIError. + * + * @param message the message + */ + inline URIError(std::string message) noexcept + : Error(DUK_ERR_URI_ERROR, std::move(message)) + { + } +}; + +/* ------------------------------------------------------------------ + * Standard overloads for TypeTraits<T> + * ------------------------------------------------------------------ */ + +/** + * @class TypeTraits<int> + * @brief Default implementation for int. + * + * Provides: get, is, optional, push, require. + */ +template <> +class TypeTraits<int> { +public: + /** + * Get an integer, return 0 if not an integer. + * + * @param ctx the context + * @param index the index + * @return the integer + */ + static inline int get(ContextPtr ctx, int index) + { + return duk_get_int(ctx, index); + } + + /** + * Check if value is an integer. + * + * @param ctx the context + * @param index the index + * @return true if integer + */ + static inline bool is(ContextPtr ctx, int index) + { + return duk_is_number(ctx, index); + } + + /** + * Get an integer, return defaultValue if the value is not an integer. + * + * @param ctx the context + * @param index the index + * @param defaultValue the defaultValue + * @return the integer or defaultValue + */ + static inline int optional(ContextPtr ctx, int index, int defaultValue) + { + return is(ctx, index) ? get(ctx, index) : defaultValue; + } + + /** + * Push an integer. + * + * @param ctx the context + * @param value the value + */ + static inline void push(ContextPtr ctx, int value) + { + duk_push_int(ctx, value); + } + + /** + * Require an integer, throws a JavaScript exception if not an integer. + * + * @param ctx the context + * @param index the index + * @return the integer + */ + static inline int require(ContextPtr ctx, int index) + { + return duk_require_int(ctx, index); + } +}; + +/** + * @class TypeTraits<bool> + * @brief Default implementation for bool. + * + * Provides: get, is, optional, push, require. + */ +template <> +class TypeTraits<bool> { +public: + /** + * Get a boolean, return 0 if not a boolean. + * + * @param ctx the context + * @param index the index + * @return the boolean + */ + static inline bool get(ContextPtr ctx, int index) + { + return duk_get_boolean(ctx, index); + } + + /** + * Check if value is a boolean. + * + * @param ctx the context + * @param index the index + * @return true if boolean + */ + static inline bool is(ContextPtr ctx, int index) + { + return duk_is_boolean(ctx, index); + } + + /** + * Get a bool, return defaultValue if the value is not a boolean. + * + * @param ctx the context + * @param index the index + * @param defaultValue the defaultValue + * @return the boolean or defaultValue + */ + static inline bool optional(ContextPtr ctx, int index, bool defaultValue) + { + return is(ctx, index) ? get(ctx, index) : defaultValue; + } + + /** + * Push a boolean. + * + * @param ctx the context + * @param value the value + */ + static inline void push(ContextPtr ctx, bool value) + { + duk_push_boolean(ctx, value); + } + + /** + * Require a boolean, throws a JavaScript exception if not a boolean. + * + * @param ctx the context + * @param index the index + * @return the boolean + */ + static inline bool require(ContextPtr ctx, int index) + { + return duk_require_boolean(ctx, index); + } +}; + +/** + * @class TypeTraits<double> + * @brief Default implementation for double. + * + * Provides: get, is, optional, push, require. + */ +template <> +class TypeTraits<double> { +public: + /** + * Get a double, return 0 if not a double. + * + * @param ctx the context + * @param index the index + * @return the double + */ + static inline double get(ContextPtr ctx, int index) + { + return duk_get_number(ctx, index); + } + + /** + * Check if value is a double. + * + * @param ctx the context + * @param index the index + * @return true if double + */ + static inline bool is(ContextPtr ctx, int index) + { + return duk_is_number(ctx, index); + } + + /** + * Get a double, return defaultValue if the value is not a double. + * + * @param ctx the context + * @param index the index + * @param defaultValue the defaultValue + * @return the double or defaultValue + */ + static inline double optional(ContextPtr ctx, int index, double defaultValue) + { + return is(ctx, index) ? get(ctx, index) : defaultValue; + } + + /** + * Push a double. + * + * @param ctx the context + * @param value the value + */ + static inline void push(ContextPtr ctx, double value) + { + duk_push_number(ctx, value); + } + + /** + * Require a double, throws a JavaScript exception if not a double. + * + * @param ctx the context + * @param index the index + * @return the double + */ + static inline double require(ContextPtr ctx, int index) + { + return duk_require_number(ctx, index); + } +}; + +/** + * @class TypeTraits<std::string> + * @brief Default implementation for std::string. + * + * Provides: get, is, optional, push, require. + * + * Note: the functions allows embedded '\0'. + */ +template <> +class TypeTraits<std::string> { +public: + /** + * Get a string, return 0 if not a string. + * + * @param ctx the context + * @param index the index + * @return the string + */ + static inline std::string get(ContextPtr ctx, int index) + { + duk_size_t size; + const char *text = duk_get_lstring(ctx, index, &size); + + return std::string{text, size}; + } + + /** + * Check if value is a string. + * + * @param ctx the context + * @param index the index + * @return true if string + */ + static inline bool is(ContextPtr ctx, int index) + { + return duk_is_string(ctx, index); + } + + /** + * Get a string, return defaultValue if the value is not an string. + * + * @param ctx the context + * @param index the index + * @param defaultValue the defaultValue + * @return the string or defaultValue + */ + static inline std::string optional(ContextPtr ctx, int index, std::string defaultValue) + { + return is(ctx, index) ? get(ctx, index) : defaultValue; + } + + /** + * Push a string. + * + * @param ctx the context + * @param value the value + */ + static inline void push(ContextPtr ctx, const std::string &value) + { + duk_push_lstring(ctx, value.c_str(), value.length()); + } + + /** + * Require a string, throws a JavaScript exception if not a string. + * + * @param ctx the context + * @param index the index + * @return the string + */ + static inline std::string require(ContextPtr ctx, int index) + { + duk_size_t size; + const char *text = duk_require_lstring(ctx, index, &size); + + return std::string{text, size}; + } +}; + +/** + * @class TypeTraits<const char *> + * @brief Default implementation for const char literals. + * + * Provides: get, is, optional, push, require. + */ +template <> +class TypeTraits<const char *> { +public: + /** + * Get a string, return 0 if not a string. + * + * @param ctx the context + * @param index the index + * @return the string + */ + static inline const char *get(ContextPtr ctx, int index) + { + return duk_get_string(ctx, index); + } + + /** + * Check if value is a string. + * + * @param ctx the context + * @param index the index + * @return true if string + */ + static inline bool is(ContextPtr ctx, int index) + { + return duk_is_string(ctx, index); + } + + /** + * Get an integer, return defaultValue if the value is not an integer. + * + * @param ctx the context + * @param index the index + * @param defaultValue the defaultValue + * @return the integer or defaultValue + */ + static inline const char *optional(ContextPtr ctx, int index, const char *defaultValue) + { + return is(ctx, index) ? get(ctx, index) : defaultValue; + } + + /** + * Push a string. + * + * @param ctx the context + * @param value the value + */ + static inline void push(ContextPtr ctx, const char *value) + { + duk_push_string(ctx, value); + } + + /** + * Require a string, throws a JavaScript exception if not a string. + * + * @param ctx the context + * @param index the index + * @return the string + */ + static inline const char *require(ContextPtr ctx, int index) + { + return duk_require_string(ctx, index); + } +}; + +/** + * @brief Implementation for non-managed pointers. + * + * Provides: get, is, optional, push, require. + */ +template <typename T> +class TypeTraits<RawPointer<T>> { +public: + /** + * Get a pointer, return nullptr if not a pointer. + * + * @param ctx the context + * @param index the index + * @return the pointer + */ + static inline T *get(ContextPtr ctx, int index) + { + return static_cast<T *>(duk_to_pointer(ctx, index)); + } + + /** + * Check if value is a pointer. + * + * @param ctx the context + * @param index the index + * @return true if pointer + */ + static inline bool is(ContextPtr ctx, int index) + { + return duk_is_pointer(ctx, index); + } + + /** + * Get a pointer, return defaultValue if the value is not a pointer. + * + * @param ctx the context + * @param index the index + * @param defaultValue the defaultValue + * @return the pointer or defaultValue + */ + static inline T *optional(ContextPtr ctx, int index, RawPointer<T> defaultValue) + { + return is(ctx, index) ? get(ctx, index) : defaultValue.object; + } + + /** + * Push a pointer. + * + * @param ctx the context + * @param value the value + */ + static inline void push(ContextPtr ctx, const RawPointer<T> &value) + { + duk_push_pointer(ctx, value.object); + } + + /** + * Require a pointer, throws a JavaScript exception if not a pointer. + * + * @param ctx the context + * @param index the index + * @return the pointer + */ + static inline T *require(ContextPtr ctx, int index) + { + return static_cast<T *>(duk_require_pointer(ctx, index)); + } +}; + +/** + * @class TypeTraits<Function> + * @brief Push C++ function to the stack. + * + * Provides: push. + * + * This implementation push a Duktape/C function that is wrapped as C++ for convenience. + */ +template <> +class TypeTraits<Function> { +public: + /** + * Check if the value at the given index is callable. + * + * @param ctx the context + * @param index the value index + * @return true if the value is callable + */ + static bool is(ContextPtr ctx, Index index) + { + return duk_is_callable(ctx, index); + } + + /** + * Push the C++ function, it is wrapped as Duktape/C function and allocated on the heap by moving the + * std::function. + * + * @param ctx the context + * @param fn the function + */ + static void push(ContextPtr ctx, Function fn) + { + duk_push_c_function(ctx, fn.function, fn.nargs); + } +}; + +/** + * @class TypeTraits<FunctionMap> + * @brief Put the functions to the object at the top of the stack. + * + * Provides: push. + */ +template <> +class TypeTraits<FunctionMap> { +public: + /** + * Push all functions to the object at the top of the stack. + * + * @param ctx the context + * @param map the map of function + */ + static inline void push(ContextPtr ctx, const FunctionMap &map) + { + StackAssert sa(ctx, 0); + + for (const auto &entry : map) { + duk_push_c_function(ctx, entry.second.function, entry.second.nargs); + duk_put_prop_string(ctx, -2, entry.first.c_str()); + } + } +}; + +/** + * @class TypeTraits<Object> + * @brief Push empty object to the stack. + * + * Provides: is, push. + */ +template <> +class TypeTraits<Object> { +public: + /** + * Check if value is an object. + * + * @param ctx the context + * @param index the index + * @return true if object + */ + static inline bool is(ContextPtr ctx, int index) + { + return duk_is_object(ctx, index); + } + + /** + * Create an empty object on the stack. + * + * @param ctx the context + */ + static inline void push(ContextPtr ctx, const Object &) + { + duk_push_object(ctx); + } +}; + +/** + * @class TypeTraits<Array> + * @brief Push empty array to the stack. + * + * Provides: is, push. + */ +template <> +class TypeTraits<Array> { +public: + /** + * Check if value is a array. + * + * @param ctx the context + * @param index the index + * @return true if array + */ + static inline bool is(ContextPtr ctx, int index) + { + return duk_is_array(ctx, index); + } + + /** + * Create an empty array on the stack. + * + * @param ctx the context + */ + static inline void push(ContextPtr ctx, const Array &) + { + duk_push_array(ctx); + } +}; + +/** + * @class TypeTraits<Undefined> + * @brief Push undefined value to the stack. + * + * Provides: is, push. + */ +template <> +class TypeTraits<Undefined> { +public: + /** + * Check if value is undefined. + * + * @param ctx the context + * @param index the index + * @return true if undefined + */ + static inline bool is(ContextPtr ctx, int index) + { + return duk_is_undefined(ctx, index); + } + + /** + * Push undefined value on the stack. + * + * @param ctx the context + */ + static inline void push(ContextPtr ctx, const Undefined &) + { + duk_push_undefined(ctx); + } +}; + +/** + * @class TypeTraits<Null> + * @brief Push null value to the stack. + * + * Provides: is, push. + */ +template <> +class TypeTraits<Null> { +public: + /** + * Check if value is null. + * + * @param ctx the context + * @param index the index + * @return true if null + */ + static inline bool is(ContextPtr ctx, int index) + { + return duk_is_null(ctx, index); + } + + /** + * Push null value on the stack. + * + * @param ctx the context + */ + static inline void push(ContextPtr ctx, const Null &) + { + duk_push_null(ctx); + } +}; + +/** + * @brief Push this binding into the stack. + * + * Provides: push. + */ +template <> +class TypeTraits<This> { +public: + /** + * Push this function into the stack. + * + * @param ctx the context + */ + static inline void push(ContextPtr ctx, const This &) + { + duk_push_this(ctx); + } +}; + +/** + * @class TypeTraits<Global> + * @brief Push the global object to the stack. + * + * Provides: push. + */ +template <> +class TypeTraits<Global> { +public: + /** + * Push the global object into the stack. + * + * @param ctx the context + */ + static inline void push(ContextPtr ctx, const Global &) + { + duk_push_global_object(ctx); + } +}; + +/** + * @brief Push a map of key-value pair as objects. + * + * Provides: push. + * + * This class is convenient for settings constants such as enums, string and such. + */ +template <typename T> +class TypeTraits<std::unordered_map<std::string, T>> { +public: + /** + * Put all values from the map as properties to the object at the top of the stack. + * + * @param ctx the context + * @param map the values + * @note You need an object at the top of the stack before calling this function + */ + static void push(ContextPtr ctx, const std::unordered_map<std::string, T> &map) + { + StackAssert sa(ctx, 0); + + for (const auto &pair : map) { + TypeTraits<T>::push(ctx, pair.second); + duk_put_prop_string(ctx, -2, pair.first.c_str()); + } + } +}; + +/** + * @brief Push or get vectors as JavaScript arrays. + * + * Provides: get, push. + */ +template <typename T> +class TypeTraits<std::vector<T>> { +public: + /** + * Get an array from the stack. + * + * @param ctx the context + * @param index the array index + * @return the array or empty array if the value is not an array + */ + static std::vector<T> get(ContextPtr ctx, int index) + { + StackAssert sa(ctx, 0); + + std::vector<T> result; + + if (!duk_is_array(ctx, -1)) + return result; + + int total = duk_get_length(ctx, index); + + for (int i = 0; i < total; ++i) + result.push_back(getProperty<T>(ctx, index, i)); + + return result; + } + + /** + * Create an array with the specified values. + * + * @param ctx the context + * @param array the values + */ + static void push(ContextPtr ctx, const std::vector<T> &array) + { + StackAssert sa(ctx, 1); + + duk_push_array(ctx); + + unsigned i = 0; + for (const auto &v : array) { + TypeTraits<T>::push(ctx, v); + duk_put_prop_index(ctx, -2, i++); + } + } +}; + +/** + * @brief Implementation of managed shared_ptr + * @see Shared + */ +template <typename T> +class TypeTraits<Shared<T>> { +private: + static void apply(ContextPtr ctx, std::shared_ptr<T> value) + { + StackAssert sa(ctx, 0); + + sign<T>(ctx, -1); + + duk_push_pointer(ctx, new std::shared_ptr<T>(std::move(value))); + duk_put_prop_string(ctx, -2, "\xff""\xff""js-shared-ptr"); + duk_push_c_function(ctx, [] (duk_context *ctx) -> Ret { + duk_get_prop_string(ctx, 0, "\xff""\xff""js-shared-ptr"); + delete static_cast<std::shared_ptr<T> *>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + duk_push_null(ctx); + duk_put_prop_string(ctx, 0, "\xff""\xff""js-ptr"); + + return 0; + }, 1); + duk_set_finalizer(ctx, -2); + } + +public: + /** + * Construct the shared_ptr as this. + * + * @param ctx the context + * @param value the value + */ + static void construct(ContextPtr ctx, Shared<T> value) + { + StackAssert sa(ctx, 0); + + duk_push_this(ctx); + apply(ctx, std::move(value.object)); + duk_pop(ctx); + } + + /** + * Push a managed shared_ptr as object. + * + * @param ctx the context + * @param value the value + */ + static void push(ContextPtr ctx, Shared<T> value) + { + StackAssert sa(ctx, 1); + + duk_push_object(ctx); + apply(ctx, value.object); + TypeTraits<T>::prototype(ctx); + duk_set_prototype(ctx, -2); + } + + /** + * Get a managed shared_ptr from the stack. + * + * @param ctx the context + * @param index the object index + * @return the shared_ptr + */ + static std::shared_ptr<T> get(ContextPtr ctx, int index) + { + StackAssert sa(ctx, 0); + + checkSignature<T>(ctx, index); + + duk_get_prop_string(ctx, index, "\xff""\xff""js-shared-ptr"); + std::shared_ptr<T> value = *static_cast<std::shared_ptr<T> *>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + + return value; + } +}; + +/** + * @brief Implementation of managed pointers + * @see Pointer + */ +template <typename T> +class TypeTraits<Pointer<T>> { +private: + static void apply(ContextPtr ctx, T *value) + { + StackAssert sa(ctx, 0); + + sign<T>(ctx, -1); + + duk_push_pointer(ctx, value); + duk_put_prop_string(ctx, -2, "\xff""\xff""js-ptr"); + duk_push_c_function(ctx, [] (duk_context *ctx) -> Ret { + duk_get_prop_string(ctx, 0, "\xff""\xff""js-ptr"); + delete static_cast<T *>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + duk_push_null(ctx); + duk_put_prop_string(ctx, 0, "\xff""\xff""js-ptr"); + + return 0; + }, 1); + duk_set_finalizer(ctx, -2); + } + +public: + /** + * Construct the pointer as this. + * + * @param ctx the context + * @param value the value + */ + static void construct(ContextPtr ctx, Pointer<T> value) + { + StackAssert sa(ctx, 0); + + duk_push_this(ctx); + apply(ctx, value.object); + duk_pop(ctx); + } + + /** + * Push a managed pointer as object. + * + * @param ctx the context + * @param value the value + */ + static void push(ContextPtr ctx, Pointer<T> value) + { + StackAssert sa(ctx, 1); + + duk_push_object(ctx); + apply(ctx, value.object); + TypeTraits<T>::prototype(ctx); + duk_set_prototype(ctx, -2); + } + + /** + * Get a managed pointer from the stack. + * + * @param ctx the context + * @param index the object index + * @return the pointer + * @warning Do not store the pointer into the C++ side, the object can be deleted at any time + */ + static T *get(ContextPtr ctx, int index) + { + StackAssert sa(ctx, 0); + + checkSignature<T>(ctx, index); + + duk_get_prop_string(ctx, index, "\xff""\xff""js-ptr"); + T *value = static_cast<T *>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + + return value; + } +}; + +} // !duk + +} // !irccd + +#endif // !JS_H
--- a/lib/irccd/js/CMakeSources.cmake Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -set( - JS_HEADERS - ${CMAKE_CURRENT_LIST_DIR}/directory.h - ${CMAKE_CURRENT_LIST_DIR}/elapsed-timer.h - ${CMAKE_CURRENT_LIST_DIR}/file.h - ${CMAKE_CURRENT_LIST_DIR}/irccd.h - ${CMAKE_CURRENT_LIST_DIR}/js.h - ${CMAKE_CURRENT_LIST_DIR}/logger.h - ${CMAKE_CURRENT_LIST_DIR}/plugin.h - ${CMAKE_CURRENT_LIST_DIR}/server.h - ${CMAKE_CURRENT_LIST_DIR}/system.h - ${CMAKE_CURRENT_LIST_DIR}/timer.h - ${CMAKE_CURRENT_LIST_DIR}/unicode.h - ${CMAKE_CURRENT_LIST_DIR}/util.h -) - -set( - JS_SOURCES - ${CMAKE_CURRENT_LIST_DIR}/directory.cpp - ${CMAKE_CURRENT_LIST_DIR}/elapsed-timer.cpp - ${CMAKE_CURRENT_LIST_DIR}/file.cpp - ${CMAKE_CURRENT_LIST_DIR}/irccd.cpp - ${CMAKE_CURRENT_LIST_DIR}/js.cpp - ${CMAKE_CURRENT_LIST_DIR}/logger.cpp - ${CMAKE_CURRENT_LIST_DIR}/plugin.cpp - ${CMAKE_CURRENT_LIST_DIR}/server.cpp - ${CMAKE_CURRENT_LIST_DIR}/system.cpp - ${CMAKE_CURRENT_LIST_DIR}/timer.cpp - ${CMAKE_CURRENT_LIST_DIR}/unicode.cpp - ${CMAKE_CURRENT_LIST_DIR}/util.cpp -) \ No newline at end of file
--- a/lib/irccd/js/directory.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,389 +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 <irccd-config.h> - -#include <irccd/private/directory.h> -#include <irccd/private/filesystem.h> -#include <irccd/path.h> - -#include <irccd/js/js.h> -#include <irccd/js/irccd.h> - -namespace irccd { - -class JsDirectory : public Directory { -private: - std::string m_path; - -public: - inline JsDirectory(std::string path, int flags) - : Directory(path, flags) - , m_path(std::move(path)) - { - } - - inline const std::string &path() const noexcept - { - return m_path; - } - - static inline const char *name() noexcept - { - return "\xff""\xff""Directory"; - } -}; - -namespace { - -/* - * 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. - */ - Directory directory(base); - - for (const DirectoryEntry &entry : directory) - if (entry.type != DirectoryEntry::Dir && pred(entry.name)) - return base + entry.name; - - if (!recursive) - throw std::out_of_range("entry not found"); - - for (const DirectoryEntry &entry : directory) { - if (entry.type == DirectoryEntry::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. - */ -int find(js::Context &ctx, std::string base, bool recursive, int patternIndex) -{ - base = path::clean(base); - - try { - std::string path; - - if (ctx.is<std::string>(patternIndex)) { - path = findName(base, ctx.get<std::string>(patternIndex), recursive); - } else { - /* Check if it's a valid RegExp object */ - ctx.getGlobal<void>("RegExp"); - - bool isRegex = ctx.instanceof(patternIndex, -1); - - ctx.pop(); - - if (isRegex) - path = findRegex(base, ctx.getProperty<std::string>(patternIndex, "source"), recursive); - else - ctx.raise(js::TypeError{"pattern must be a string or a regex expression"}); - } - - if (path.empty()) - return 0; - - ctx.push(path); - } catch (const std::exception &ex) { - ctx.raise(js::Error{ex.what()}); - } - - return 1; -} - -/* - * Generic remove function for: - * - * - Directory.remove - * - Directory.prototype.remove - */ -int remove(js::Context &ctx, const std::string &path, bool recursive) -{ - if (!recursive) { - ::remove(path.c_str()); - } else { - try { - Directory directory(path); - - for (const DirectoryEntry &entry : directory) { - if (entry.type == DirectoryEntry::Dir) { - (void)remove(ctx, path + fs::Separator + entry.name, true); - } else { - std::string filename = path + fs::Separator + entry.name; - - ::remove(filename.c_str()); - } - } - - ::remove(path.c_str()); - } catch (const std::exception &) { - // TODO: put the error in a log. - } - } - - 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 on errors or not found - */ -int methodFind(js::Context &ctx) -{ - return find(ctx, ctx.self<js::Pointer<JsDirectory>>()->path(), ctx.optional<bool>(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. - */ -int methodRemove(js::Context &ctx) -{ - return remove(ctx, ctx.self<js::Pointer<JsDirectory>>()->path(), ctx.optional<bool>(0, false)); -} - -const js::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 - */ -int constructor(js::Context &ctx) -{ - if (!duk_is_constructor_call(ctx)) - return 0; - - try { - JsDirectory *directory = new JsDirectory(ctx.require<std::string>(0), ctx.optional<int>(1, 0)); - - ctx.construct(js::Pointer<JsDirectory>{directory}); - ctx.push(js::This{}); - ctx.push("count"); - ctx.push(directory->count()); - ctx.defineProperty(-3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); - ctx.push("path"); - ctx.push(directory->path()); - ctx.defineProperty(-3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); - - /* add entries */ - ctx.push("entries"); - ctx.push(js::Array{}); - - int i = 0; - for (const DirectoryEntry &entry : (*directory)) { - ctx.push(js::Object{}); - ctx.putProperty(-1, "name", entry.name); - ctx.putProperty(-1, "type", static_cast<int>(entry.type)); - ctx.putProperty(-2, i++); - } - - ctx.defineProperty(-3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); - } catch (const std::exception &ex) { - ctx.raise(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. - */ -int funcFind(js::Context &ctx) -{ - return find(ctx, ctx.require<std::string>(0), ctx.optional<bool>(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. - */ -int funcRemove(js::Context &ctx) -{ - return remove(ctx, ctx.require<std::string>(0), ctx.optional<bool>(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. - */ -int funcMkdir(js::Context &ctx) -{ - try { - fs::mkdir(ctx.require<std::string>(0), ctx.optional<int>(1, 0700)); - } catch (const std::exception &ex) { - ctx.raise(SystemError{errno, ex.what()}); - } - - return 0; -} - -const js::FunctionMap functions{ - { "find", { funcFind, DUK_VARARGS } }, - { "mkdir", { funcMkdir, DUK_VARARGS } }, - { "remove", { funcRemove, DUK_VARARGS } } -}; - -const js::Map<int> constants{ - { "Dot", static_cast<int>(Directory::Dot) }, - { "DotDot", static_cast<int>(Directory::DotDot) }, - { "TypeUnknown", static_cast<int>(DirectoryEntry::Unknown) }, - { "TypeDir", static_cast<int>(DirectoryEntry::Dir) }, - { "TypeFile", static_cast<int>(DirectoryEntry::File) }, - { "TypeLink", static_cast<int>(DirectoryEntry::Link) } -}; - -} // !namespace - -void loadJsDirectory(js::Context &ctx) noexcept -{ - ctx.getGlobal<void>("Irccd"); - - /* File object */ - ctx.push(js::Function{constructor, 2}); - ctx.push(constants); - ctx.push(functions); - ctx.putProperty(-1, "separator", std::string{fs::Separator}); - - /* Prototype */ - ctx.push(js::Object{}); - ctx.push(methods); - ctx.putProperty(-1, "\xff""\xff""Directory", true); - ctx.putProperty(-2, "prototype"); - - /* Put Directory */ - ctx.putProperty(-2, "Directory"); - ctx.pop(); -} - -} // !irccd
--- a/lib/irccd/js/directory.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -/* - * js-directory.h -- 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_H_ -#define _IRCCD_JS_DIRECTORY_H_ - -#include "js.h" - -namespace irccd { - -void loadJsDirectory(js::Context &ctx) noexcept; - -} // !irccd - -#endif // !_IRCCD_JS_DIRECTORY_H_ -
--- a/lib/irccd/js/elapsed-timer.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,133 +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 <irccd/private/elapsed-timer.h> - -#include "elapsed-timer.h" -#include "js.h" - -namespace irccd { - -class JsElapsedTimer : public ElapsedTimer { -public: - using ElapsedTimer::ElapsedTimer; - - static inline const char *name() noexcept - { - return "\xff""\xff""ElapsedTimer"; - } -}; - -namespace { - -/* - * Method: ElapsedTimer.pause - * ------------------------------------------------------------------ - * - * Pause the timer, without resetting the current elapsed time stored. - */ -int pause(js::Context &ctx) -{ - ctx.self<js::Pointer<JsElapsedTimer>>()->pause(); - - return 0; -} - -/* - * Method: ElapsedTimer.reset - * ------------------------------------------------------------------ - * - * Reset the elapsed time to 0, the status is not modified. - */ -int reset(js::Context &ctx) -{ - ctx.self<js::Pointer<JsElapsedTimer>>()->reset(); - - return 0; -} - -/* - * Method: ElapsedTimer.restart - * ------------------------------------------------------------------ - * - * Restart the timer without resetting the current elapsed time. - */ -int restart(js::Context &ctx) -{ - ctx.self<js::Pointer<JsElapsedTimer>>()->restart(); - - return 0; -} - -/* - * Method: ElapsedTimer.elapsed - * ------------------------------------------------------------------ - * - * Get the number of elapsed milliseconds. - * - * Returns: - * The time elapsed. - */ -int elapsed(js::Context &ctx) -{ - ctx.push<int>(ctx.self<js::Pointer<JsElapsedTimer>>()->elapsed()); - - return 1; -} - -/* - * Function: Irccd.ElapsedTimer() [constructor] - * ------------------------------------------------------------------ - * - * Construct a new ElapsedTimer object. - */ -int constructor(js::Context &ctx) -{ - ctx.construct(js::Pointer<JsElapsedTimer>{new JsElapsedTimer}); - - return 0; -} - -const js::FunctionMap methods{ - { "elapsed", { elapsed, 0 } }, - { "pause", { pause, 0 } }, - { "reset", { reset, 0 } }, - { "restart", { restart, 0 } } -}; - -} // !namespace - -void loadJsElapsedTimer(js::Context &ctx) noexcept -{ - ctx.getGlobal<void>("Irccd"); - - /* Timer object */ - ctx.push(js::Function{constructor, 3}); - - /* Prototype */ - ctx.push(js::Object{}); - ctx.push(methods); - ctx.putProperty(-1, "\xff""\xff""ElapsedTimer", true); - ctx.putProperty(-2, "prototype"); - - /* Put Timer */ - ctx.putProperty(-2, "ElapsedTimer"); - ctx.pop(); -} - -} // !irccd
--- a/lib/irccd/js/elapsed-timer.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -/* - * js-elapsed-timer.h -- 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_H_ -#define _IRCCD_JS_ELAPSED_TIMER_H_ - -#include "js.h" - -namespace irccd { - -void loadJsElapsedTimer(js::Context &ctx) noexcept; - -} // !irccd - -#endif // !_IRCCD_JS_ELAPSED_TIMER_H_ -
--- a/lib/irccd/js/file.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,628 +0,0 @@ -/* - * js-file.h -- 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 <cerrno> -#include <cstring> -#include <fstream> - -#include <irccd-config.h> - -#if defined(HAVE_STAT) -# include <sys/types.h> -# include <sys/stat.h> -#endif - -#include <irccd/js/irccd.h> -#include <irccd/js/file.h> - -#include <irccd/private/filesystem.h> - -#if defined(HAVE_STAT) - -namespace irccd { - -/* - * js::File object for Javascript I/O - * ------------------------------------------------------------------ - */ - -File::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)); -} - -File::File(std::FILE *fp, std::function<void (std::FILE *)> destructor) noexcept - : m_stream(fp) - , m_destructor(std::move(destructor)) -{ - assert(m_destructor != nullptr); -} - -File::~File() noexcept -{ - close(); -} - -void File::close() noexcept -{ - if (m_stream) { - m_destructor(m_stream); - m_stream = nullptr; - } -} - -bool File::isClosed() noexcept -{ - return m_stream == nullptr; -} - -void File::seek(long amount, long dir) -{ - if (std::fseek(m_stream, amount, dir) != 0) - throw std::runtime_error(std::strerror(errno)); -} - -unsigned File::tell() -{ - long pos = std::ftell(m_stream); - - if (pos == -1L) - throw std::runtime_error(std::strerror(errno)); - - return pos; -} - -std::string File::readline() -{ - std::string result; - int ch; - - while ((ch = std::fgetc(m_stream)) != EOF && ch != '\n') - result += ch; - - if (ch == EOF && std::ferror(m_stream)) - throw std::runtime_error(std::strerror(errno)); - - return result; -} - -std::string File::read(int amount) -{ - assert(amount != 0); - - std::string result; - int ch; - - for (int i = 0; (ch = std::fgetc(m_stream)) != EOF; ) { - result += ch; - - if (amount > 0 && ++i == amount) - break; - } - - if (ch == EOF && std::ferror(m_stream)) - throw std::runtime_error(std::strerror(errno)); - - return result; -} - -void File::write(const std::string &data) -{ - if (std::fwrite(data.c_str(), data.length(), 1, m_stream) != 1) - throw std::runtime_error(std::strerror(errno)); -} - -bool File::eof() const noexcept -{ - return std::feof(m_stream); -} - -/* - * js::TypeInfo specialization for struct stat - * ------------------------------------------------------------------ - */ - -namespace js { - -template <> -class TypeInfo<struct stat> { -public: - static void push(Context &ctx, const struct stat &st) - { - ctx.push(Object{}); - -#if defined(HAVE_STAT_ST_ATIME) - ctx.putProperty(-2, "atime", static_cast<int>(st.st_atime)); -#endif -#if defined(HAVE_STAT_ST_BLKSIZE) - ctx.putProperty(-2, "blksize", static_cast<int>(st.st_blksize)); -#endif -#if defined(HAVE_STAT_ST_BLOCKS) - ctx.putProperty(-2, "blocks", static_cast<int>(st.st_blocks)); -#endif -#if defined(HAVE_STAT_ST_CTIME) - ctx.putProperty(-2, "ctime", static_cast<int>(st.st_ctime)); -#endif -#if defined(HAVE_STAT_ST_DEV) - ctx.putProperty(-2, "dev", static_cast<int>(st.st_dev)); -#endif -#if defined(HAVE_STAT_ST_GID) - ctx.putProperty(-2, "gid", static_cast<int>(st.st_gid)); -#endif -#if defined(HAVE_STAT_ST_INO) - ctx.putProperty(-2, "ino", static_cast<int>(st.st_ino)); -#endif -#if defined(HAVE_STAT_ST_MODE) - ctx.putProperty(-2, "mode", static_cast<int>(st.st_mode)); -#endif -#if defined(HAVE_STAT_ST_MTIME) - ctx.putProperty(-2, "mtime", static_cast<int>(st.st_mtime)); -#endif -#if defined(HAVE_STAT_ST_NLINK) - ctx.putProperty(-2, "nlink", static_cast<int>(st.st_nlink)); -#endif -#if defined(HAVE_STAT_ST_RDEV) - ctx.putProperty(-2, "rdev", static_cast<int>(st.st_rdev)); -#endif -#if defined(HAVE_STAT_ST_SIZE) - ctx.putProperty(-2, "size", static_cast<int>(st.st_size)); -#endif -#if defined(HAVE_STAT_ST_UID) - ctx.putProperty(-2, "uid", static_cast<int>(st.st_uid)); -#endif - } -}; - -} // !js - -namespace { - -#endif // !HAVE_STAT - -/* -------------------------------------------------------- - * File methods - * -------------------------------------------------------- */ - -/* - * Method: File.basename() - * -------------------------------------------------------- - * - * Synonym of `Irccd.File.basename(path)` but with the path from the file. - * - * Returns: - * The base name. - */ -int methodBasename(js::Context &ctx) -{ - ctx.push(fs::baseName(ctx.self<js::Pointer<File>>()->path())); - - return 1; -} - -/* - * Method: File.close() - * -------------------------------------------------------- - * - * Force close of the file, automatically called when object is collected. - */ -int methodClose(js::Context &ctx) -{ - ctx.self<js::Pointer<File>>()->close(); - - return 0; -} - -/* - * Method: File.dirname() - * -------------------------------------------------------- - * - * Synonym of `Irccd.File.dirname(path)` but with the path from the file. - * - * Returns: - * The directory name. - */ -int methodDirname(js::Context &ctx) -{ - ctx.push(fs::dirName(ctx.self<js::Pointer<File>>()->path())); - - 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. - */ -int methodRead(js::Context &ctx) -{ - auto amount = ctx.optional<int>(0, -1); - auto self = ctx.self<js::Pointer<File>>(); - - if (amount == 0 || self->isClosed()) - return 0; - - try { - ctx.push(self->read(amount)); - } catch (const std::exception &) { - ctx.raise(SystemError()); - } - - return 1; -} - -/* - * Method: File.readline() - * -------------------------------------------------------- - * - * Read the next line available. - * - * Returns: - * The next line or undefined if eof. - * Throws: - * - Any exception on error. - */ -int methodReadline(js::Context &ctx) -{ - try { - auto file = ctx.self<js::Pointer<File>>(); - - if (file->isClosed() || file->eof()) - return 0; - - ctx.push(file->readline()); - } catch (const std::exception &) { - ctx.raise(SystemError()); - } - - return 1; -} - -/* - * Method: File.remove() - * -------------------------------------------------------- - * - * Synonym of File.remove(path) but with the path from the file. - * - * Throws: - * - Any exception on error. - */ -int methodRemove(js::Context &ctx) -{ - if (::remove(ctx.self<js::Pointer<File>>()->path().c_str()) < 0) - ctx.raise(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. - */ -int methodSeek(js::Context &ctx) -{ - auto type = ctx.require<int>(0); - auto amount = ctx.require<int>(1); - auto file = ctx.self<js::Pointer<File>>(); - - if (file->isClosed()) - return 0; - - try { - file->seek(amount, type); - } catch (const std::exception &) { - ctx.raise(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. - */ -int methodStat(js::Context &ctx) -{ - struct stat st; - auto file = ctx.self<js::Pointer<File>>(); - - if (file->isClosed()) - return 0; - - if (::stat(file->path().c_str(), &st) < 0) - ctx.raise(SystemError()); - - ctx.push(st); - - return 1; -} - -#endif // !HAVE_STAT - -/* - * Method: File.tell() - * -------------------------------------------------------- - * - * Get the actual position in the file. - * - * Returns: - * The position. - * Throws: - * - Any exception on error. - */ -int methodTell(js::Context &ctx) -{ - auto file = ctx.self<js::Pointer<File>>(); - - if (file->isClosed()) - return 0; - - try { - ctx.push(static_cast<int>(file->tell())); - } catch (const std::exception &) { - ctx.raise(SystemError()); - } - - return 1; -} - -/* - * Method: File.write(data) - * -------------------------------------------------------- - * - * Write some characters to the file. - * - * Arguments: - * - data, the character to write. - * Throws: - * - Any exception on error. - */ -int methodWrite(js::Context &ctx) -{ - auto file = ctx.self<js::Pointer<File>>(); - - if (file->isClosed()) - return 0; - - try { - file->write(ctx.require<std::string>(0)); - } catch (const std::exception &) { - ctx.raise(SystemError()); - } - - return 0; -} - -const js::FunctionMap methods{ - { "basename", { methodBasename, 0 } }, - { "close", { methodClose, 0 } }, - { "dirname", { methodDirname, 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. - */ -int constructor(js::Context &ctx) -{ - if (!duk_is_constructor_call(ctx)) - return 0; - - std::string path = ctx.require<std::string>(0); - std::string mode = ctx.require<std::string>(1); - - try { - ctx.construct(js::Pointer<File>{new File(path, mode)}); - } catch (const std::exception &) { - ctx.raise(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. - */ -int functionBasename(js::Context &ctx) -{ - ctx.push(fs::baseName(ctx.require<std::string>(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. - */ -int functionDirname(js::Context &ctx) -{ - ctx.push(fs::dirName(ctx.require<std::string>(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. - */ -int functionExists(js::Context &ctx) -{ - ctx.push(fs::exists(ctx.require<std::string>(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. - */ -int functionRemove(js::Context &ctx) -{ - if (::remove(ctx.require<std::string>(0).c_str()) < 0) - ctx.raise(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. - */ -int functionStat(js::Context &ctx) -{ - struct stat st; - - if (::stat(ctx.require<std::string>(0).c_str(), &st) < 0) - ctx.raise(SystemError()); - - ctx.push(st); - - return 1; -} - -#endif // !HAVE_STAT - -const js::FunctionMap functions{ - { "basename", { functionBasename, 1 } }, - { "dirname", { functionDirname, 1 } }, - { "exists", { functionExists, 1 } }, - { "remove", { functionRemove, 1 } }, -#if defined(HAVE_STAT) - { "stat", { functionStat, 1 } }, -#endif -}; - -const js::Map<int> constants{ - { "SeekCur", static_cast<int>(std::fstream::cur) }, - { "SeekEnd", static_cast<int>(std::fstream::end) }, - { "SeekSet", static_cast<int>(std::fstream::beg) }, -}; - -} // !namespace - -void loadJsFile(js::Context &ctx) -{ - ctx.getGlobal<void>("Irccd"); - - /* File object */ - ctx.push(js::Function{constructor, 2}); - ctx.push(constants); - ctx.push(functions); - - /* Prototype */ - ctx.push(js::Object{}); - ctx.push(methods); - ctx.putProperty(-1, "\xff""\xff""File", true); - ctx.putProperty(-2, "prototype"); - - /* Put File */ - ctx.putProperty(-2, "File"); - ctx.pop(); -} - -} // !irccd
--- a/lib/irccd/js/file.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,184 +0,0 @@ -/* - * js-file.h -- 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_H_ -#define _IRCCD_JS_FILE_H_ - -#include <cstdio> - -#include "js.h" - -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; - -protected: - 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 - */ - File(std::string path, const std::string &mode); - - /** - * 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 - */ - File(std::FILE *fp, std::function<void (std::FILE *)> destructor) noexcept; - - /** - * Closes the file. - */ - virtual ~File() noexcept; - - /** - * Get the path. - * - * @return the path - * @warning empty when constructed from the FILE constructor - */ - inline const std::string &path() const noexcept - { - return m_path; - } - - /** - * Force close, can be safely called multiple times. - */ - void close() noexcept; - - /** - * Tells if the file was closed. - * - * @return true if closed - */ - bool isClosed() noexcept; - - /** - * std::fseek wrapper. - * - * @param offset the offset - * @param origin the origin (SEEK_SET, *) - * @throw std::runtime_error on failure - */ - void seek(long offset, long origin); - - /** - * std::ftell wrapper. - * - * @return the position - * @throw std::runtime_error on failure - */ - unsigned tell(); - - /** - * Read until the next line and discards the \n character. - * - * @return the next line or empty if EOF - * @throw std::runtime_error on failure - */ - std::string readline(); - - /** - * Read the specified amount of characters. - * - * If amount is less than 0, the maximum is read. - * - * @pre amount != 0 - * @param amount the number of characters to read - * @return the read string - * @throw std::runtime_error on failure - */ - std::string read(int amount = -1); - - /** - * Write the string to the file. - * - * @param data the data to write - * @throw std::runtime_error on failure - */ - void write(const std::string &data); - - /** - * Check if the file reached the end. - * - * @return true if eof - */ - bool eof() const noexcept; - - /** - * Get Javascript object signature. - * - * @return the signature - */ - static inline const char *name() noexcept - { - return "\xff""\xff""File"; - } - - /** - * Get the prototype for pushing new File objects. - * - * @param ctx the context - */ - static inline void prototype(js::Context &ctx) - { - js::StackAssert sa(ctx, 1); - - ctx.getGlobal<void>("Irccd"); - ctx.getProperty<void>(-1, "File"); - ctx.getProperty<void>(-1, "prototype"); - ctx.remove(-2); - ctx.remove(-2); - } -}; - -void loadJsFile(js::Context &ctx); - -} // !irccd - -#endif // !_IRCCD_JS_FILE_H_ -
--- a/lib/irccd/js/irccd.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +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 <irccd-config.h> - -#include <irccd/js/irccd.h> - -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::create(js::Context &ctx) const -{ - ctx.getGlobal<void>("Irccd"); - ctx.getProperty<void>(-1, "SystemError"); - ctx.push(m_errno); - ctx.push(m_message); - duk_new(ctx, 2); - ctx.remove(-2); -} - -int constructor(js::Context &ctx) -{ - ctx.push(js::This{}); - ctx.putProperty(-1, "errno", ctx.require<int>(0)); - ctx.putProperty(-1, "message", ctx.require<std::string>(1)); - ctx.putProperty(-1, "name", "SystemError"); - ctx.pop(); - - return 0; -} - -void loadJsIrccd(js::Context &ctx) -{ - /* Irccd */ - ctx.push(js::Object{}); - - /* Version */ - ctx.push(js::Object{}); - ctx.putProperty(-1, "major", IRCCD_VERSION_MAJOR); - ctx.putProperty(-1, "minor", IRCCD_VERSION_MINOR); - ctx.putProperty(-1, "patch", IRCCD_VERSION_PATCH); - ctx.putProperty(-2, "version"); - - /* Create the SystemError that inherits from Error */ - ctx.push(js::Function{constructor, 2}); - - /* Prototype */ - ctx.getGlobal<void>("Error"); - duk_new(ctx, 0); - ctx.dup(-2); - ctx.putProperty(-2, "constructor"); - ctx.putProperty(-2, "prototype"); - ctx.putProperty(-2, "SystemError"); - - /* Set Irccd as global */ - ctx.putGlobal("Irccd"); -} - -} // !irccd
--- a/lib/irccd/js/irccd.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -/* - * js-irccd.h -- 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_H_ -#define _IRCCD_JS_IRCCD_H_ - -#include <cerrno> -#include <cstring> - -#include "js.h" - -namespace irccd { - -class SystemError { -private: - int m_errno; - std::string m_message; - -public: - SystemError(); - - SystemError(int e, std::string message); - - void create(js::Context &ctx) const; -}; - -void loadJsIrccd(js::Context &); - -} // !irccd - -#endif // !_IRCCD_JS_IRCCD_H_
--- a/lib/irccd/js/js.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,105 +0,0 @@ -/* - * js.cpp -- JavaScript C++14 wrapper for Duktape - * - * 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.h" - -using namespace std::string_literals; - -namespace irccd { - -namespace js { - -ErrorInfo Context::error(int index) -{ - ErrorInfo error; - - index = duk_normalize_index(m_handle.get(), index); - - error.name = optionalProperty<std::string>(index, "name", ""); - error.message = optionalProperty<std::string>(index, "message", ""); - error.fileName = optionalProperty<std::string>(index, "fileName", ""); - error.lineNumber = optionalProperty<int>(index, "lineNumber", 0); - error.stack = optionalProperty<std::string>(index, "stack", ""); - - return error; -} - -void Context::pcall(unsigned nargs) -{ - if (duk_pcall(m_handle.get(), nargs) != 0) { - ErrorInfo info = error(-1); - duk_pop(m_handle.get()); - - throw info; - } -} - -void Context::peval() -{ - if (duk_peval(m_handle.get()) != 0) { - ErrorInfo info = error(-1); - duk_pop(m_handle.get()); - - throw info; - } -} - -void TypeInfo<Function>::push(Context &ctx, Function fn) -{ - /* 1. Push function wrapper */ - duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t { - Context context(ctx); - - duk_push_current_function(ctx); - duk_get_prop_string(ctx, -1, "\xff""\xff""js-func"); - Function *f = static_cast<Function *>(duk_to_pointer(ctx, -1)); - duk_pop_2(ctx); - - return static_cast<duk_ret_t>(f->function(context)); - }, fn.nargs); - - /* 2. Store the moved function */ - duk_push_pointer(ctx, new Function(std::move(fn))); - duk_put_prop_string(ctx, -2, "\xff""\xff""js-func"); - - /* 3. Store deletion flags */ - duk_push_boolean(ctx, false); - duk_put_prop_string(ctx, -2, "\xff""\xff""js-deleted"); - - /* 4. Push and set a finalizer */ - duk_push_c_function(ctx, [] (duk_context *ctx) { - duk_get_prop_string(ctx, 0, "\xff""\xff""js-deleted"); - - if (!duk_to_boolean(ctx, -1)) { - duk_push_boolean(ctx, true); - duk_put_prop_string(ctx, 0, "\xff""\xff""js-deleted"); - duk_get_prop_string(ctx, 0, "\xff""\xff""js-func"); - delete static_cast<Function *>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - } - - duk_pop(ctx); - - return 0; - }, 1); - duk_set_finalizer(ctx, -2); -} - -} // !js - -} // !irccd
--- a/lib/irccd/js/js.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2183 +0,0 @@ -/* - * js.h -- JavaScript C++14 wrapper for Duktape - * - * 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 _JS_H_ -#define _JS_H_ - -/** - * @file Js.h - * @brief Bring JavaScript using Duktape - * - * This file provides usual Duktape function renamed and placed into `js` namespace. It also replaces error - * code with exceptions when possible. - * - * For convenience, this file also provides templated functions, overloads and much more. - */ - -#include <cassert> -#include <functional> -#include <memory> -#include <string> -#include <type_traits> -#include <unordered_map> -#include <utility> -#include <vector> - -#include <duktape.h> - -namespace irccd { - -/** - * Duktape C++ namespace wrapper. - */ -namespace js { - -class Context; - -/** - * Typedef for readability. - */ -using ContextPtr = duk_context *; - -/* - * Basic types to manipulate with the stack - * ------------------------------------------------------------------ - * - * The following types can be used in some of the operations like Context::push or Context::is, they are defined - * usually as empty classes to determine the appropriate action to execute. - * - * For example, `ctx.push(js::Object{})` will push an empty object into the stack. - */ - -/** - * @class Object - * @brief Empty class tag for push() function. - */ -class Object { -}; - -/** - * @class Array - * @brief Empty class tag for push() function. - */ -class Array { -}; - -/** - * @class Global - * @brief Empty class tag to push the global object. - */ -class Global { -}; - -/** - * @class Undefined - * @brief Empty class tag to push undefined to the stack. - */ -class Undefined { -}; - -/** - * @class Null - * @brief Empty class tag to push null to the stack. - */ -class Null { -}; - -/** - * @class This - * @brief Empty class tag to push this binding to the stack. - */ -class This { -}; - -/** - * @class RawPointer - * @brief Push a non-managed pointer to Duktape, the pointer will never be deleted. - * @note For a managed pointer with prototype, see Pointer - */ -template <typename T> -class RawPointer { -public: - /** - * The pointer to push. - */ - T *object; -}; - -/* - * Extended type manipulation - * ------------------------------------------------------------------ - * - * The following types are different as there are no equivalent in the native Duktape API, they are available for - * convenience. - */ - -/** - * @brief Manage shared_ptr from C++ and JavaScript - * - * This class allowed you to push and retrieve shared_ptr from C++ and JavaScript without taking care of ownership - * and deletion. - * - * The only requirement is to have the function `void prototype(Context &ctx)` in your class T. - */ -template <typename T> -class Shared { -public: - /** - * The shared object. - */ - std::shared_ptr<T> object; -}; - -/** - * @brief Manage pointers from C++ and JavaScript - * - * This class allowed you to push and retrieve C++ pointers from C++ and JavaScript. The object will be deleted when - * the JavaScript garbage collectors collect them so never store a pointer created with this. - * - * The only requirement is to have the function `void prototype(Context &ctx)` in your class T. - */ -template <typename T> -class Pointer { -public: - /** - * The object. - */ - T *object{nullptr}; -}; - -/** - * @class Function - * @brief Duktape/C function definition. - * - * This class wraps the std::function as a Duktape/C function by storing a copied pointer. - */ -class Function { -public: - /** - * The function pointer, must not be null. - */ - std::function<int (Context &)> function; - - /** - * Number of args that the function takes - */ - int nargs{0}; -}; - -/** - * Map of functions to set on an object. - */ -using FunctionMap = std::unordered_map<std::string, Function>; - -/** - * Map of string to type, ideal for setting constants like enums. - */ -template <typename Type> -using Map = std::unordered_map<std::string, Type>; - -/** - * @class ErrorInfo - * @brief Error description. - * - * This class fills the fields got in an Error object. - */ -class ErrorInfo : public std::exception { -public: - std::string name; //!< name of error - std::string message; //!< error message - std::string stack; //!< stack if available - std::string fileName; //!< filename if applicable - int lineNumber{0}; //!< line number if applicable - - /** - * Get the error message. This effectively returns message field. - * - * @return the message - */ - const char *what() const noexcept override - { - return message.c_str(); - } -}; - -/** - * @class TypeInfo - * @brief Type information to implement new types in JavaScript's context. - * - * This class depending on your needs may have the following functions: - * - * - `static void construct(Context &ctx, Type value)` - * - `static Type get(Context &ctx, int index)` - * - `static bool is(Context &ctx, int index)` - * - `static Type optional(Context &ctx, int index, Type defaultValue)` - * - `static void push(Context &ctx, Type value)` - * - `static Type require(Context &ctx, int index)` - * - * The `construct` function is used in Context::construct to build a new value as this (e.g. constructors). - * - * The `get` function is used in Context::get, Context::getProperty, Context::getGlobal to retrieve a value from the - * stack. - * - * The `is` function is used in Context::is to check if the value on the stack is of type `Type`. - * - * The `optional` function is used in Context::optional to get a value or a replacement if not applicable. - * - * The `push` function is used in Context::push to usually create a new value on the stack but some specializations - * may not (e.g. FunctionMap). - * - * The `require` function is used in Context::require to get a value from the stack or raise a JavaScript exception if - * not applicable. - * - * This class is fully specialized for: `bool`, `const char *`, `double`, `int`, `std::string`. - * - * It is also partially specialized for : `Global`, `Object`, `Array`, `Undefined`, `Null`, `std::vector<Type>`. - */ -template <typename Type> -class TypeInfo { -}; - -/** - * @class File - * @brief Evaluate script from file. - * @see Context::eval - * @see Context::peval - */ -class File { -public: - /** - * Path to the file. - */ - std::string path; - - /** - * Evaluate the file. - * - * @param ctx the context - */ - inline void eval(duk_context *ctx) - { - duk_eval_file(ctx, path.c_str()); - } - - /** - * Evaluate in protected mode the file. - * - * @param ctx the context - */ - inline int peval(duk_context *ctx) - { - return duk_peval_file(ctx, path.c_str()); - } -}; - -/** - * @class Script - * @brief Evaluate script from raw text. - * @see Context::eval - * @see Context::peval - */ -class Script { -public: - /** - * The script content. - */ - std::string text; - - /** - * Evaluate the script. - * - * @param ctx the context - */ - inline void eval(duk_context *ctx) - { - duk_eval_string(ctx, text.c_str()); - } - - /** - * Evaluate in protected mode the script. - * - * @param ctx the context - */ - inline int peval(duk_context *ctx) - { - return duk_peval_string(ctx, text.c_str()); - } -}; - -/** - * @class Context - * @brief RAII based Duktape handler. - * - * This class is implicitly convertible to duk_context for convenience. - */ -class Context { -private: - using Deleter = void (*)(duk_context *); - using Handle = std::unique_ptr<duk_context, Deleter>; - - Handle m_handle; - - /* Move and copy forbidden */ - Context(const Context &) = delete; - Context &operator=(const Context &) = delete; - Context(const Context &&) = delete; - Context &operator=(const Context &&) = delete; - -public: - /** - * Create default context. - */ - inline Context() - : m_handle(duk_create_heap_default(), duk_destroy_heap) - { - } - - /** - * Create borrowed context that will not be deleted. - * - * @param ctx the pointer to duk_context - */ - inline Context(ContextPtr ctx) noexcept - : m_handle(ctx, [] (ContextPtr) {}) - { - } - - /** - * Convert the context to the native Duktape/C type. - * - * @return the duk_context - */ - inline operator duk_context *() noexcept - { - return m_handle.get(); - } - - /** - * Convert the context to the native Duktape/C type. - * - * @return the duk_context - */ - inline operator duk_context *() const noexcept - { - return m_handle.get(); - } - - /* - * Basic functions - * ---------------------------------------------------------- - * - * The following functions are just standard wrappers around the native Duktape C functions, they are - * defined with the same signature except that for convenience some parameters have default sane values. - */ - - /** - * Call the object at the top of the stack. - * - * @param ctx the context - * @param nargs the number of arguments - * @note Non-protected - */ - inline void call(unsigned nargs = 0) - { - duk_call(m_handle.get(), nargs); - } - - /** - * Copy a value from from to to, overwriting the previous value. If either index is invalid, throws an error. - * - * @param from the from index - * @param to the destination - */ - inline void copy(int from, int to) - { - duk_copy(m_handle.get(), from, to); - } - - /** - * Define a property. - * - * @param index the object index - * @param flags the flags - * @note Wrapper of duk_def_prop - */ - inline void defineProperty(int index, int flags) - { - duk_def_prop(m_handle.get(), index, flags); - } - - /** - * Delete a property. - * - * @param index the object index - * @return true if deleted - * @note Wrapper of duk_del_prop - */ - inline bool deleteProperty(int index) - { - return duk_del_prop(m_handle.get(), index); - } - - /** - * Delete a property by index. - * - * @param index the object index - * @param position the property index - * @return true if deleted - * @note Wrapper of duk_del_prop_index - */ - inline bool deleteProperty(int index, int position) - { - return duk_del_prop_index(m_handle.get(), index, position); - } - - /** - * Delete a property by name. - * - * @param index the object index - * @param name the property name - * @return true if deleted - * @note Wrapper of duk_del_prop_string - */ - inline bool deleteProperty(int index, const std::string &name) - { - return duk_del_prop_string(m_handle.get(), index, name.c_str()); - } - - /** - * Push a duplicate of value at from_index to the stack. If from_index is invalid, throws an error. - * - * @param index the value to copy - * @note Wrapper of duk_dup - */ - inline void dup(int index = -1) - { - duk_dup(m_handle.get(), index); - } - - /** - * Evaluate a non-protected chunk that is at the top of the stack. - */ - inline void eval() - { - duk_eval(m_handle.get()); - } - - /** - * Check if the object as a property. - * - * @param index the object index - * @return true if has - * @note Wrapper of duk_has_prop - */ - inline bool hasProperty(int index) - { - return duk_has_prop(m_handle.get(), index); - } - - /** - * Check if the object as a property by index. - * - * @param index the object index - * @param position the property index - * @return true if has - * @note Wrapper of duk_has_prop_index - */ - inline bool hasProperty(int index, int position) - { - return duk_has_prop_index(m_handle.get(), index, position); - } - - /** - * Check if the object as a property by string - * - * @param index the object index - * @param name the property name - * @return true if has - * @note Wrapper of duk_has_prop_string - */ - inline bool hasProperty(int index, const std::string &name) - { - return duk_has_prop_string(m_handle.get(), index, name.c_str()); - } - - /** - * Check if idx1 is an instance of idx2. - * - * @param ctx the context - * @param idx1 the value to test - * @param idx2 the instance requested - * @return true if idx1 is instance of idx2 - * @note Wrapper of duk_instanceof - */ - inline bool instanceof(int idx1, int idx2) - { - return duk_instanceof(m_handle.get(), idx1, idx2); - } - - /** - * Insert a value at to with a value popped from the stack top. The previous value at to and any values above - * it are moved up the stack by a step. If to is an invalid index, throws an error. - * - * @note Negative indices are evaluated prior to popping the value at the stack top - * @param to the destination - * @note Wrapper of duk_insert - */ - inline void insert(int to) - { - duk_insert(m_handle.get(), to); - } - - /** - * Pop a certain number of values from the top of the stack. - * - * @param ctx the context - * @param count the number of values to pop - * @note Wrapper of duk_pop_n - */ - inline void pop(unsigned count = 1) - { - duk_pop_n(m_handle.get(), count); - } - - /** - * Remove value at index. Elements above index are shifted down the stack by a step. If to is an invalid index, - * throws an error. - * - * @param index the value to remove - * @note Wrapper of duk_remove - */ - inline void remove(int index) - { - duk_remove(m_handle.get(), index); - } - - /** - * Replace value at to_index with a value popped from the stack top. If to_index is an invalid index, - * throws an error. - * - * @param index the value to replace by the value at the top of the stack - * @note Negative indices are evaluated prior to popping the value at the stack top. - * @note Wrapper of duk_replace - */ - inline void replace(int index) - { - duk_replace(m_handle.get(), index); - } - - /** - * Swap values at indices index1 and index2. If the indices are the same, the call is a no-op. If either index - * is invalid, throws an error. - * - * @param index1 the first index - * @param index2 the second index - * @note Wrapper of duk_swap - */ - inline void swap(int index1, int index2) - { - duk_swap(m_handle.get(), index1, index2); - } - - /** - * Get the current stack size. - * - * @param ctx the context - * @return the stack size - * @note Wrapper of duk_get_top - */ - inline int top() noexcept - { - return duk_get_top(m_handle.get()); - } - - /** - * Get the type of the value at the specified index. - * - * @param ctx the context - * @param index the idnex - * @return the type - * @note Wrapper of duk_get_type - */ - inline int type(int index) noexcept - { - return duk_get_type(m_handle.get(), index); - } - - /* - * Extended native functions - * ---------------------------------------------------------- - * - * The following functions have different behaviour than the original native Duktape C functions, see their - * descriptions for more information - */ - - /** - * Call in protected mode the object at the top of the stack. - * - * @param nargs the number of arguments - * @throw ErrorInfo on errors - * @note Wrapper of duk_pcall - */ - void pcall(unsigned nargs = 0); - - /** - * Evaluate a non-protected source. - * - * @param source the source - * @see File - * @see Script - * @note Wrapper of duk_eval - */ - template <typename Source> - inline void eval(Source &&source) - { - source.eval(m_handle.get()); - } - - /** - * Evaluate a protected chunk that is at the top of the stack. - * - * @throw ErrorInfo the error - * @note Wrapper of duk_peval - */ - void peval(); - - /** - * Evaluate a protected source. - * - * @param source the source - * @see File - * @see Script - * @throw ErrorInfo on failure - * @note Wrapper of duk_peval - */ - template <typename Source> - inline void peval(Source &&source) - { - if (source.peval(m_handle.get()) != 0) { - ErrorInfo info = error(-1); - duk_pop(m_handle.get()); - - throw info; - } - } - - /* - * Push / Get / Require / Is / Optional - * ---------------------------------------------------------- - * - * The following functions are used to push, get or check values from the stack. They use specialization - * of TypeInfo class. - */ - - /** - * Push a value into the stack. Calls TypeInfo<T>::push(*this, value); - * - * @param value the value to forward - */ - template <typename Type> - inline void push(Type &&value) - { - TypeInfo<std::decay_t<Type>>::push(*this, std::forward<Type>(value)); - } - - /** - * Generic template function to get a value from the stack. - * - * @param index the index - * @return the value - */ - template <typename Type> - inline auto get(int index) -> decltype(TypeInfo<Type>::get(*this, 0)) - { - return TypeInfo<Type>::get(*this, index); - } - - /** - * Require a type at the specified index. - * - * @param index the index - * @return the value - */ - template <typename Type> - inline auto require(int index) -> decltype(TypeInfo<Type>::require(*this, 0)) - { - return TypeInfo<Type>::require(*this, index); - } - - /** - * Check if a value is a type of T. - * - * The TypeInfo<T> must have `static bool is(ContextPtr ptr, int index)`. - * - * @param index the value index - * @return true if is the type - */ - template <typename T> - inline bool is(int index) - { - return TypeInfo<T>::is(*this, index); - } - - /** - * Get an optional value from the stack, if the value is not available of not the correct type, - * return defaultValue instead. - * - * The TypeInfo<T> must have `static T optional(Context &, int index, T &&defaultValue)`. - * - * @param index the value index - * @param defaultValue the value replacement - * @return the value or defaultValue - */ - template <typename Type, typename DefaultValue> - inline auto optional(int index, DefaultValue &&defaultValue) - { - return TypeInfo<std::decay_t<Type>>::optional(*this, index, std::forward<DefaultValue>(defaultValue)); - } - - /* - * Properties management - * ---------------------------------------------------------- - * - * The following functions are used to read or set properties on objects or globals also using TypeInfo. - */ - - /** - * Get the property `name' as value from the object at the specified index. - * - * @param index the object index - * @param name the property name - * @return the value - * @note The stack is unchanged - */ - template <typename Type, typename std::enable_if_t<!std::is_void<Type>::value> * = nullptr> - inline auto getProperty(int index, const std::string &name) -> decltype(get<Type>(0)) - { - duk_get_prop_string(m_handle.get(), index, name.c_str()); - decltype(get<Type>(0)) value = get<Type>(-1); - duk_pop(m_handle.get()); - - return value; - } - - /** - * Get an optional property `name` from the object at the specified index. - * - * @param index the object index - * @param name the property name - * @param def the default value - * @return the value or def - * @note The stack is unchanged - */ - template <typename Type, typename DefaultValue> - inline auto optionalProperty(int index, const std::string &name, DefaultValue &&def) -> decltype(optional<Type>(0, std::forward<DefaultValue>(def))) - { - duk_get_prop_string(m_handle.get(), index, name.c_str()); - decltype(optional<Type>(0, std::forward<DefaultValue>(def))) value = optional<Type>(-1, std::forward<DefaultValue>(def)); - duk_pop(m_handle.get()); - - return value; - } - - /** - * Get a property by index, for arrays. - * - * @param index the object index - * @param position the position int the object - * @return the value - * @note The stack is unchanged - */ - template <typename Type, typename std::enable_if_t<!std::is_void<Type>::value> * = nullptr> - inline auto getProperty(int index, int position) -> decltype(get<Type>(0)) - { - duk_get_prop_index(m_handle.get(), index, position); - decltype(get<Type>(0)) value = get<Type>(-1); - duk_pop(m_handle.get()); - - return value; - } - - /** - * Get an optional property by index, for arrays - * - * @param index the object index - * @param position the position int the object - * @param def the default value - * @return the value or def - * @note The stack is unchanged - */ - template <typename Type, typename DefaultValue> - inline auto optionalProperty(int index, int position, DefaultValue &&def) -> decltype(optional<Type>(0, std::forward<DefaultValue>(def))) - { - duk_get_prop_index(m_handle.get(), index, position); - decltype(optional<Type>(0, std::forward<DefaultValue>(def))) value = optional<Type>(-1, std::forward<DefaultValue>(def)); - duk_pop(m_handle.get()); - - return value; - } - - /** - * Get the property `name' and push it to the stack from the object at the specified index. - * - * @param index the object index - * @param name the property name - * @note The stack contains the property value - */ - template <typename Type, typename std::enable_if_t<std::is_void<Type>::value> * = nullptr> - inline void getProperty(int index, const std::string &name) - { - duk_get_prop_string(m_handle.get(), index, name.c_str()); - } - - /** - * Get the property by index and push it to the stack from the object at the specified index. - * - * @param index the object index - * @param position the position in the object - * @note The stack contains the property value - */ - template <typename Type, typename std::enable_if_t<std::is_void<Type>::value> * = nullptr> - inline void getProperty(int index, int position) - { - duk_get_prop_index(m_handle.get(), index, position); - } - - /** - * Set a property to the object at the specified index. - * - * @param index the object index - * @param name the property name - * @param value the value to forward - * @note The stack is unchanged - */ - template <typename Type> - void putProperty(int index, const std::string &name, Type &&value) - { - index = duk_normalize_index(m_handle.get(), index); - - push(std::forward<Type>(value)); - duk_put_prop_string(m_handle.get(), index, name.c_str()); - } - - /** - * Set a property by index, for arrays. - * - * @param index the object index - * @param position the position in the object - * @param value the value to forward - * @note The stack is unchanged - */ - template <typename Type> - void putProperty(int index, int position, Type &&value) - { - index = duk_normalize_index(m_handle.get(), index); - - push(std::forward<Type>(value)); - duk_put_prop_index(m_handle.get(), index, position); - } - - /** - * Put the value that is at the top of the stack as property to the object. - * - * @param index the object index - * @param name the property name - */ - inline void putProperty(int index, const std::string &name) - { - duk_put_prop_string(m_handle.get(), index, name.c_str()); - } - - /** - * Put the value that is at the top of the stack to the object as index. - * - * @param index the object index - * @param position the position in the object - */ - inline void putProperty(int index, int position) - { - duk_put_prop_index(m_handle.get(), index, position); - } - - /** - * Get a global value. - * - * @param name the name of the global variable - * @return the value - */ - template <typename Type> - inline auto getGlobal(const std::string &name, std::enable_if_t<!std::is_void<Type>::value> * = nullptr) -> decltype(get<Type>(0)) - { - duk_get_global_string(m_handle.get(), name.c_str()); - decltype(get<Type>(0)) value = get<Type>(-1); - duk_pop(m_handle.get()); - - return value; - } - - /** - * Overload that push the value at the top of the stack instead of returning it. - */ - template <typename Type> - inline void getGlobal(const std::string &name, std::enable_if_t<std::is_void<Type>::value> * = nullptr) noexcept - { - duk_get_global_string(m_handle.get(), name.c_str()); - } - - /** - * Set a global variable. - * - * @param name the name of the global variable - * @param type the value to set - */ - template <typename Type> - inline void putGlobal(const std::string &name, Type&& type) - { - push(std::forward<Type>(type)); - duk_put_global_string(m_handle.get(), name.c_str()); - } - - /** - * Put the value at the top of the stack as global property. - * - * @param name the property name - */ - inline void putGlobal(const std::string &name) - { - duk_put_global_string(m_handle.get(), name.c_str()); - } - - /* - * Extra functions - * ---------------------------------------------------------- - * - * The following functions are implemented for convenience and do not exists in the native Duktape API. - */ - - /** - * Get the error object when a JavaScript error has been thrown (e.g. eval failure). - * - * @param index the index - * @return the information - */ - ErrorInfo error(int index); - - /** - * Enumerate an object or an array at the specified index. - * - * @param index the object or array index - * @param flags the optional flags to pass to duk_enum - * @param getvalue set to true if you want to extract the value - * @param func the function to call for each properties - */ - template <typename Func> - void enumerate(int index, duk_uint_t flags, duk_bool_t getvalue, Func&& func) - { - duk_enum(m_handle.get(), index, flags); - - while (duk_next(m_handle.get(), -1, getvalue)) { - func(*this); - duk_pop_n(m_handle.get(), 1 + (getvalue ? 1 : 0)); - } - - duk_pop(m_handle.get()); - } - - /** - * Return the this binding of the current function. - * - * @return the this binding as the template given - */ - template <typename T> - inline auto self() -> decltype(TypeInfo<T>::get(*this, 0)) - { - duk_push_this(m_handle.get()); - decltype(TypeInfo<T>::get(*this, 0)) value = TypeInfo<T>::get(*this, -1); - duk_pop(m_handle.get()); - - return value; - } - - inline void raise() - { - duk_throw(m_handle.get()); - } - - /** - * Throw an ECMAScript exception. - * - * @param ex the exception - */ - template <typename Exception> - void raise(const Exception &ex) - { - ex.create(*this); - raise(); - } - - /** - * Construct the object in place, setting value as this binding. - * - * The TypeInfo<T> must have the following requirements: - * - * - static void construct(Context &, T): must update this with the value and keep the stack unchanged - * - * @param value the value to forward - * @see self - */ - template <typename T> - inline void construct(T &&value) - { - TypeInfo<std::decay_t<T>>::construct(*this, std::forward<T>(value)); - } -}; - -/** - * @class StackAssert - * @brief Stack sanity checker. - * - * Instanciate this class where you need to manipulate the Duktape stack outside a Duktape/C function, its destructor - * will examinate if the stack size matches the user expected size. - * - * When compiled with NDEBUG, this class does nothing. - * - * To use it, just declare an lvalue at the beginning of your function. - */ -class StackAssert { -#if !defined(NDEBUG) -private: - Context &m_context; - unsigned m_expected; - unsigned m_begin; -#endif - -public: - /** - * Create the stack checker. - * - * No-op if NDEBUG is set. - * - * @param ctx the context - * @param expected the size expected relative to the already existing values - */ - inline StackAssert(Context &ctx, unsigned expected = 0) noexcept -#if !defined(NDEBUG) - : m_context(ctx) - , m_expected(expected) - , m_begin(static_cast<unsigned>(m_context.top())) -#endif - { -#if defined(NDEBUG) - (void)ctx; - (void)expected; -#endif - } - - /** - * Verify the expected size. - * - * No-op if NDEBUG is set. - */ - inline ~StackAssert() noexcept - { -#if !defined(NDEBUG) - assert(m_context.top() - m_begin == m_expected); -#endif - } -}; - -/* ------------------------------------------------------------------ - * Exception handling - * ------------------------------------------------------------------ */ - -/** - * @class Error - * @brief Base ECMAScript error class. - * @warning Override the function create for your own exceptions - */ -class Error { -protected: - std::string m_name; //!< Name of exception (e.g RangeError) - std::string m_message; //!< The message - - /** - * Constructor with a type of error specified, specially designed for derived errors. - * - * @param name the error name (e.g RangeError) - * @param message the message - */ - inline Error(std::string name, std::string message) noexcept - : m_name(std::move(name)) - , m_message(std::move(message)) - { - } - -public: - /** - * Constructor with a message. - * - * @param message the message - */ - inline Error(std::string message) noexcept - : m_name("Error") - , m_message(std::move(message)) - { - } - - /** - * Get the error type (e.g RangeError). - * - * @return the name - */ - inline const std::string &name() const noexcept - { - return m_name; - } - - /** - * Create the exception on the stack. - * - * @note the default implementation search for the global variables - * @param ctx the context - */ - virtual void create(Context &ctx) const noexcept - { - duk_get_global_string(ctx, m_name.c_str()); - duk_push_string(ctx, m_message.c_str()); - duk_new(ctx, 1); - duk_push_string(ctx, m_name.c_str()); - duk_put_prop_string(ctx, -2, "name"); - } -}; - -/** - * @class EvalError - * @brief Error in eval() function. - */ -class EvalError : public Error { -public: - /** - * Construct an EvalError. - * - * @param message the message - */ - inline EvalError(std::string message) noexcept - : Error("EvalError", std::move(message)) - { - } -}; - -/** - * @class RangeError - * @brief Value is out of range. - */ -class RangeError : public Error { -public: - /** - * Construct an RangeError. - * - * @param message the message - */ - inline RangeError(std::string message) noexcept - : Error("RangeError", std::move(message)) - { - } -}; - -/** - * @class ReferenceError - * @brief Trying to use a variable that does not exist. - */ -class ReferenceError : public Error { -public: - /** - * Construct an ReferenceError. - * - * @param message the message - */ - inline ReferenceError(std::string message) noexcept - : Error("ReferenceError", std::move(message)) - { - } -}; - -/** - * @class SyntaxError - * @brief Syntax error in the script. - */ -class SyntaxError : public Error { -public: - /** - * Construct an SyntaxError. - * - * @param message the message - */ - inline SyntaxError(std::string message) noexcept - : Error("SyntaxError", std::move(message)) - { - } -}; - -/** - * @class TypeError - * @brief Invalid type given. - */ -class TypeError : public Error { -public: - /** - * Construct an TypeError. - * - * @param message the message - */ - inline TypeError(std::string message) noexcept - : Error("TypeError", std::move(message)) - { - } -}; - -/** - * @class URIError - * @brief URI manipulation failure. - */ -class URIError : public Error { -public: - /** - * Construct an URIError. - * - * @param message the message - */ - inline URIError(std::string message) noexcept - : Error("URIError", std::move(message)) - { - } -}; - -/* ------------------------------------------------------------------ - * Standard overloads for TypeInfo<T> - * ------------------------------------------------------------------ */ - -/** - * @class TypeInfo<int> - * @brief Default implementation for int. - * - * Provides: get, is, optional, push, require. - */ -template <> -class TypeInfo<int> { -public: - /** - * Get an integer, return 0 if not an integer. - * - * @param ctx the context - * @param index the index - * @return the integer - */ - static inline int get(Context &ctx, int index) - { - return duk_get_int(ctx, index); - } - - /** - * Check if value is an integer. - * - * @param ctx the context - * @param index the index - * @return true if integer - */ - static inline bool is(Context &ctx, int index) - { - return duk_is_number(ctx, index); - } - - /** - * Get an integer, return defaultValue if the value is not an integer. - * - * @param ctx the context - * @param index the index - * @param defaultValue the defaultValue - * @return the integer or defaultValue - */ - static inline int optional(Context &ctx, int index, int defaultValue) - { - if (!duk_is_number(ctx, index)) - return defaultValue; - - return duk_get_int(ctx, index); - } - - /** - * Push an integer. - * - * @param ctx the context - * @param value the value - */ - static inline void push(Context &ctx, int value) - { - duk_push_int(ctx, value); - } - - /** - * Require an integer, throws a JavaScript exception if not an integer. - * - * @param ctx the context - * @param index the index - * @return the integer - */ - static inline int require(Context &ctx, int index) - { - return duk_require_int(ctx, index); - } -}; - -/** - * @class TypeInfo<bool> - * @brief Default implementation for bool. - * - * Provides: get, is, optional, push, require. - */ -template <> -class TypeInfo<bool> { -public: - /** - * Get a boolean, return 0 if not a boolean. - * - * @param ctx the context - * @param index the index - * @return the boolean - */ - static inline bool get(Context &ctx, int index) - { - return duk_get_boolean(ctx, index); - } - - /** - * Check if value is a boolean. - * - * @param ctx the context - * @param index the index - * @return true if boolean - */ - static inline bool is(Context &ctx, int index) - { - return duk_is_boolean(ctx, index); - } - - /** - * Get a bool, return defaultValue if the value is not a boolean. - * - * @param ctx the context - * @param index the index - * @param defaultValue the defaultValue - * @return the boolean or defaultValue - */ - static inline bool optional(Context &ctx, int index, bool defaultValue) - { - if (!duk_is_boolean(ctx, index)) - return defaultValue; - - return duk_get_boolean(ctx, index); - } - - /** - * Push a boolean. - * - * @param ctx the context - * @param value the value - */ - static inline void push(Context &ctx, bool value) - { - duk_push_boolean(ctx, value); - } - - /** - * Require a boolean, throws a JavaScript exception if not a boolean. - * - * @param ctx the context - * @param index the index - * @return the boolean - */ - static inline bool require(Context &ctx, int index) - { - return duk_require_boolean(ctx, index); - } -}; - -/** - * @class TypeInfo<double> - * @brief Default implementation for double. - * - * Provides: get, is, optional, push, require. - */ -template <> -class TypeInfo<double> { -public: - /** - * Get a double, return 0 if not a double. - * - * @param ctx the context - * @param index the index - * @return the double - */ - static inline double get(Context &ctx, int index) - { - return duk_get_number(ctx, index); - } - - /** - * Check if value is a double. - * - * @param ctx the context - * @param index the index - * @return true if double - */ - static inline bool is(Context &ctx, int index) - { - return duk_is_number(ctx, index); - } - - /** - * Get a double, return defaultValue if the value is not a double. - * - * @param ctx the context - * @param index the index - * @param defaultValue the defaultValue - * @return the double or defaultValue - */ - static inline double optional(Context &ctx, int index, double defaultValue) - { - if (!duk_is_number(ctx, index)) - return defaultValue; - - return duk_get_number(ctx, index); - } - - /** - * Push a double. - * - * @param ctx the context - * @param value the value - */ - static inline void push(Context &ctx, double value) - { - duk_push_number(ctx, value); - } - - /** - * Require a double, throws a JavaScript exception if not a double. - * - * @param ctx the context - * @param index the index - * @return the double - */ - static inline double require(Context &ctx, int index) - { - return duk_require_number(ctx, index); - } -}; - -/** - * @class TypeInfo<std::string> - * @brief Default implementation for std::string. - * - * Provides: get, is, optional, push, require. - * - * Note: the functions allows embedded '\0'. - */ -template <> -class TypeInfo<std::string> { -public: - /** - * Get a string, return 0 if not a string. - * - * @param ctx the context - * @param index the index - * @return the string - */ - static inline std::string get(Context &ctx, int index) - { - duk_size_t size; - const char *text = duk_get_lstring(ctx, index, &size); - - return std::string{text, size}; - } - - /** - * Check if value is a string. - * - * @param ctx the context - * @param index the index - * @return true if string - */ - static inline bool is(Context &ctx, int index) - { - return duk_is_string(ctx, index); - } - - /** - * Get a string, return defaultValue if the value is not an string. - * - * @param ctx the context - * @param index the index - * @param defaultValue the defaultValue - * @return the string or defaultValue - */ - static inline std::string optional(Context &ctx, int index, std::string defaultValue) - { - if (!duk_is_string(ctx, index)) - return defaultValue; - - return get(ctx, index); - } - - /** - * Push a string. - * - * @param ctx the context - * @param value the value - */ - static inline void push(Context &ctx, const std::string &value) - { - duk_push_lstring(ctx, value.c_str(), value.length()); - } - - /** - * Require a string, throws a JavaScript exception if not a string. - * - * @param ctx the context - * @param index the index - * @return the string - */ - static inline std::string require(Context &ctx, int index) - { - duk_size_t size; - const char *text = duk_require_lstring(ctx, index, &size); - - return std::string{text, size}; - } -}; - -/** - * @class TypeInfo<const char *> - * @brief Default implementation for const char literals. - * - * Provides: get, is, optional, push, require. - */ -template <> -class TypeInfo<const char *> { -public: - /** - * Get a string, return 0 if not a string. - * - * @param ctx the context - * @param index the index - * @return the string - */ - static inline const char *get(Context &ctx, int index) - { - return duk_get_string(ctx, index); - } - - /** - * Check if value is a string. - * - * @param ctx the context - * @param index the index - * @return true if string - */ - static inline bool is(Context &ctx, int index) - { - return duk_is_string(ctx, index); - } - - /** - * Get an integer, return defaultValue if the value is not an integer. - * - * @param ctx the context - * @param index the index - * @param defaultValue the defaultValue - * @return the integer or defaultValue - */ - static inline const char *optional(Context &ctx, int index, const char *defaultValue) - { - if (!duk_is_string(ctx, index)) - return defaultValue; - - return duk_get_string(ctx, index); - } - - /** - * Push a string. - * - * @param ctx the context - * @param value the value - */ - static inline void push(Context &ctx, const char *value) - { - duk_push_string(ctx, value); - } - - /** - * Require a string, throws a JavaScript exception if not a string. - * - * @param ctx the context - * @param index the index - * @return the string - */ - static inline const char *require(Context &ctx, int index) - { - return duk_require_string(ctx, index); - } -}; - -/** - * @brief Implementation for non-managed pointers. - * - * Provides: get, is, optional, push, require. - */ -template <typename T> -class TypeInfo<RawPointer<T>> { -public: - /** - * Get a pointer, return nullptr if not a pointer. - * - * @param ctx the context - * @param index the index - * @return the pointer - */ - static inline T *get(Context &ctx, int index) - { - return static_cast<T *>(duk_to_pointer(ctx, index)); - } - - /** - * Check if value is a pointer. - * - * @param ctx the context - * @param index the index - * @return true if pointer - */ - static inline bool is(Context &ctx, int index) - { - return duk_is_pointer(ctx, index); - } - - /** - * Get a pointer, return defaultValue if the value is not a pointer. - * - * @param ctx the context - * @param index the index - * @param defaultValue the defaultValue - * @return the pointer or defaultValue - */ - static inline T *optional(Context &ctx, int index, RawPointer<T> defaultValue) - { - if (!duk_is_pointer(ctx, index)) - return defaultValue.object; - - return static_cast<T *>(duk_to_pointer(ctx, index)); - } - - /** - * Push a pointer. - * - * @param ctx the context - * @param value the value - */ - static inline void push(Context &ctx, const RawPointer<T> &value) - { - duk_push_pointer(ctx, value.object); - } - - /** - * Require a pointer, throws a JavaScript exception if not a pointer. - * - * @param ctx the context - * @param index the index - * @return the pointer - */ - static inline T *require(Context &ctx, int index) - { - return static_cast<T *>(duk_require_pointer(ctx, index)); - } -}; - -/** - * @class TypeInfo<Function> - * @brief Push C++ function to the stack. - * - * Provides: push. - * - * This implementation push a Duktape/C function that is wrapped as C++ for convenience. - */ -template <> -class TypeInfo<Function> { -public: - /** - * Push the C++ function, it is wrapped as Duktape/C function and allocated on the heap by moving the - * std::function. - * - * @param ctx the context - * @param fn the function - */ - static void push(Context &ctx, Function fn); -}; - -/** - * @class TypeInfo<FunctionMap> - * @brief Put the functions to the object at the top of the stack. - * - * Provides: push. - */ -template <> -class TypeInfo<FunctionMap> { -public: - /** - * Push a map of function to the object at the top of the stack. - * - * @param ctx the context - * @param map the map of function - */ - static inline void push(Context &ctx, const FunctionMap &map) - { - for (const auto &entry : map) - ctx.putProperty(-1, entry.first, entry.second); - } -}; - -/** - * @class TypeInfo<Object> - * @brief Push empty object to the stack. - * - * Provides: is, push. - */ -template <> -class TypeInfo<Object> { -public: - /** - * Check if value is an object. - * - * @param ctx the context - * @param index the index - * @return true if object - */ - static inline bool is(Context &ctx, int index) - { - return duk_is_object(ctx, index); - } - - /** - * Create an empty object on the stack. - * - * @param ctx the context - */ - static inline void push(Context &ctx, const Object &) - { - duk_push_object(ctx); - } -}; - -/** - * @class TypeInfo<Array> - * @brief Push empty array to the stack. - * - * Provides: is, push. - */ -template <> -class TypeInfo<Array> { -public: - /** - * Check if value is a array. - * - * @param ctx the context - * @param index the index - * @return true if array - */ - static inline bool is(Context &ctx, int index) - { - return duk_is_array(ctx, index); - } - - /** - * Create an empty array on the stack. - * - * @param ctx the context - */ - static inline void push(Context &ctx, const Array &) - { - duk_push_array(ctx); - } -}; - -/** - * @class TypeInfo<Undefined> - * @brief Push undefined value to the stack. - * - * Provides: is, push. - */ -template <> -class TypeInfo<Undefined> { -public: - /** - * Check if value is undefined. - * - * @param ctx the context - * @param index the index - * @return true if undefined - */ - static inline bool is(Context &ctx, int index) - { - return duk_is_undefined(ctx, index); - } - - /** - * Push undefined value on the stack. - * - * @param ctx the context - */ - static inline void push(Context &ctx, const Undefined &) - { - duk_push_undefined(ctx); - } -}; - -/** - * @class TypeInfo<Null> - * @brief Push null value to the stack. - * - * Provides: is, push. - */ -template <> -class TypeInfo<Null> { -public: - /** - * Check if value is null. - * - * @param ctx the context - * @param index the index - * @return true if null - */ - static inline bool is(Context &ctx, int index) - { - return duk_is_null(ctx, index); - } - - /** - * Push null value on the stack. - * - * @param ctx the context - */ - static inline void push(Context &ctx, const Null &) - { - duk_push_null(ctx); - } -}; - -/** - * @brief Push this binding into the stack. - * - * Provides: push. - */ -template <> -class TypeInfo<This> { -public: - /** - * Push this function into the stack. - * - * @param ctx the context - */ - static inline void push(Context &ctx, const This &) - { - duk_push_this(ctx); - } -}; - -/** - * @class TypeInfo<Global> - * @brief Push the global object to the stack. - * - * Provides: push. - */ -template <> -class TypeInfo<Global> { -public: - /** - * Push the global object into the stack. - * - * @param ctx the context - */ - static inline void push(Context &ctx, const Global &) - { - duk_push_global_object(ctx); - } -}; - -/** - * @brief Push a map of key-value pair as objects. - * - * Provides: push. - * - * This class is convenient for settings constants such as enums, string and such. - */ -template <typename T> -class TypeInfo<std::unordered_map<std::string, T>> { -public: - /** - * Put all values from the map as properties to the object at the top of the stack. - * - * @param ctx the context - * @param map the values - * @note You need an object at the top of the stack before calling this function - */ - static void push(Context &ctx, const std::unordered_map<std::string, T> &map) - { - for (const auto &pair : map) { - TypeInfo<T>::push(ctx, pair.second); - duk_put_prop_string(ctx, -2, pair.first.c_str()); - } - } -}; - -/** - * @brief Push or get vectors as JavaScript arrays. - * - * Provides: get, push. - */ -template <typename T> -class TypeInfo<std::vector<T>> { -public: - /** - * Get an array from the stack. - * - * @param ctx the context - * @param index the array index - * @return the array or empty array if the value is not an array - */ - static std::vector<T> get(Context &ctx, int index) - { - std::vector<T> result; - - if (!duk_is_array(ctx, -1)) - return result; - - int total = duk_get_length(ctx, index); - for (int i = 0; i < total; ++i) - result.push_back(ctx.getProperty<T>(index, i)); - - return result; - } - - /** - * Create an array with the specified values. - * - * @param ctx the context - * @param array the values - */ - static void push(Context &ctx, const std::vector<T> &array) - { - duk_push_array(ctx); - - int i = 0; - for (const auto &v : array) { - TypeInfo<T>::push(ctx, v); - duk_put_prop_index(ctx, -2, i++); - } - } -}; - -/** - * @brief Implementation of managed shared_ptr - * @see Shared - */ -template <typename T> -class TypeInfo<Shared<T>> { -private: - static void apply(Context &ctx, std::shared_ptr<T> value) - { - duk_push_boolean(ctx, false); - duk_put_prop_string(ctx, -2, "\xff""\xff""js-deleted"); - duk_push_pointer(ctx, new std::shared_ptr<T>(value)); - duk_put_prop_string(ctx, -2, "\xff""\xff""js-shared-ptr"); - duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t { - duk_get_prop_string(ctx, 0, "\xff""\xff""js-deleted"); - - if (!duk_to_boolean(ctx, -1)) { - duk_push_boolean(ctx, true); - duk_put_prop_string(ctx, 0, "\xff""\xff""js-deleted"); - duk_get_prop_string(ctx, 0, "\xff""\xff""js-shared-ptr"); - delete static_cast<std::shared_ptr<T> *>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - } - - duk_pop(ctx); - - return 0; - }, 1); - duk_set_finalizer(ctx, -2); - } - -public: - /** - * Construct the shared_ptr as this. - * - * @param ctx the context - * @param value the value - */ - static void construct(Context &ctx, Shared<T> value) - { - duk_push_this(ctx); - apply(ctx, std::move(value.object)); - duk_pop(ctx); - } - - /** - * Push a managed shared_ptr as object. - * - * @param ctx the context - * @param value the value - */ - static void push(Context &ctx, Shared<T> value) - { - js::StackAssert sa(ctx, 1); - - duk_push_object(ctx); - value.object->prototype(ctx); - duk_set_prototype(ctx, -2); - apply(ctx, value.object); - } - - /** - * Get a managed shared_ptr from the stack. - * - * @param ctx the context - * @param index the object index - * @return the shared_ptr - */ - static std::shared_ptr<T> get(Context &ctx, int index) - { - /* Verify that it is the correct type */ - duk_get_prop_string(ctx, index, T::name()); - - if (duk_get_type(ctx, -1) == DUK_TYPE_UNDEFINED) { - duk_pop(ctx); - ctx.raise(ReferenceError("invalid this binding")); - } else { - duk_pop(ctx); - } - - duk_get_prop_string(ctx, index, "\xff""\xff""js-shared-ptr"); - std::shared_ptr<T> value = *static_cast<std::shared_ptr<T> *>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - - return value; - } -}; - -/** - * @brief Implementation of managed pointers - * @see Pointer - */ -template <typename T> -class TypeInfo<Pointer<T>> { -private: - static void apply(Context &ctx, T *value) - { - duk_push_boolean(ctx, false); - duk_put_prop_string(ctx, -2, "\xff""\xff""js-deleted"); - duk_push_pointer(ctx, value); - duk_put_prop_string(ctx, -2, "\xff""\xff""js-ptr"); - duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t { - duk_get_prop_string(ctx, 0, "\xff""\xff""js-deleted"); - - if (!duk_to_boolean(ctx, -1)) { - duk_push_boolean(ctx, true); - duk_put_prop_string(ctx, 0, "\xff""\xff""js-deleted"); - duk_get_prop_string(ctx, 0, "\xff""\xff""js-ptr"); - delete static_cast<T *>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - } - - duk_pop(ctx); - - return 0; - }, 1); - duk_set_finalizer(ctx, -2); - } - -public: - /** - * Construct the pointer as this. - * - * @param ctx the context - * @param value the value - */ - static void construct(Context &ctx, Pointer<T> value) - { - duk_push_this(ctx); - apply(ctx, value.object); - duk_pop(ctx); - } - - /** - * Push a managed pointer as object. - * - * @param ctx the context - * @param value the value - */ - static void push(Context &ctx, Pointer<T> value) - { - js::StackAssert sa(ctx, 1); - - duk_push_object(ctx); - apply(ctx, value.object); - value.object->prototype(ctx); - duk_set_prototype(ctx, -2); - } - - /** - * Get a managed pointer from the stack. - * - * @param ctx the context - * @param index the object index - * @return the pointer - * @warning Do not store the pointer into the C++ side, the object can be deleted at any time - */ - static T *get(Context &ctx, int index) - { - /* Verify that it is the correct type */ - duk_get_prop_string(ctx, index, T::name()); - - if (duk_get_type(ctx, -1) == DUK_TYPE_UNDEFINED) { - duk_pop(ctx); - ctx.raise(ReferenceError("invalid this binding")); - } else { - duk_pop(ctx); - } - - duk_get_prop_string(ctx, index, "\xff""\xff""js-ptr"); - T *value = static_cast<T *>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - - return value; - } -}; - -} // !js - -} // !irccd - -#endif // !_JS_H_
--- a/lib/irccd/js/logger.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,98 +0,0 @@ -/* - * js-logger.h -- 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 <irccd/js/logger.h> -#include <irccd/logger.h> - -namespace irccd { - -namespace { - -int print(js::Context &ctx, std::ostream &out) -{ - /* - * Get the message before we start printing stuff to avoid - * empty lines. - */ - out << "plugin "; - out << ctx.getGlobal<std::string>("\xff""\xff""name") << ": "; - out << ctx.get<std::string>(0) << std::endl; - - return 0; -} - -/* - * Function: Irccd.Logger.info(message) - * -------------------------------------------------------- - * - * Write a verbose message. - * - * Arguments: - * - message, the message. - */ -int info(js::Context &ctx) -{ - return print(ctx, log::info()); -} - -/* - * Function: Irccd.Logger.warning(message) - * -------------------------------------------------------- - * - * Write a warning message. - * - * Arguments: - * - message, the warning. - */ -int warning(js::Context &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. - */ -int debug(js::Context &ctx) -{ - return print(ctx, log::debug()); -} - -const js::FunctionMap functions{ - { "info", { info, 1 } }, - { "warning", { warning, 1 } }, - { "debug", { debug, 1 } }, -}; - -} // !namespace - -void loadJsLogger(js::Context &ctx) -{ - ctx.getGlobal<void>("Irccd"); - ctx.push(js::Object{}); - ctx.push(functions); - ctx.putProperty(-2, "Logger"); - ctx.pop(); -} - -} // !irccd
--- a/lib/irccd/js/logger.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -/* - * js-logger.h -- 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_H_ -#define _IRCCD_JS_LOGGER_H_ - -#include "js.h" - -namespace irccd { - -void loadJsLogger(js::Context &ctx); - -} // !irccd - -#endif // !_IRCCD_JS_LOGGER_H_
--- a/lib/irccd/js/plugin.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,181 +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/irccd.h> -#include <irccd/js/plugin.h> - -namespace irccd { - -namespace { - -/* - * Wrap function for these functions because they all takes the same arguments. - * - * - load, - * - reload, - * - unload. - */ -template <typename Func> -int wrap(js::Context &ctx, int nret, Func &&func) -{ - std::string name = ctx.require<std::string>(0); - - try { - func(*ctx.getGlobal<js::RawPointer<Irccd>>("\xff""\xff""irccd"), name); - } catch (const std::out_of_range &ex) { - ctx.raise(js::ReferenceError(ex.what())); - } catch (const std::exception &ex) { - ctx.raise(js::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. - */ -int info(js::Context &ctx) -{ - if (ctx.top() >= 1) { - try { - ctx.push(ctx.getGlobal<js::RawPointer<Irccd>>("\xff""\xff""irccd")->requirePlugin(ctx.require<std::string>(0))->info()); - } catch (...) { - ctx.push(js::Undefined{}); - } - } else { - ctx.push(ctx.getGlobal<js::RawPointer<Plugin>>("\xff""\xff""plugin")->info()); - } - - 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. - */ -int list(js::Context &ctx) -{ - ctx.push(js::Array{}); - - int i = 0; - for (const auto &pair : ctx.getGlobal<js::RawPointer<Irccd>>("\xff""\xff""irccd")->plugins()) - ctx.putProperty(-1, i++, pair.first); - - 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. - */ -int load(js::Context &ctx) -{ - return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) { - irccd.loadPlugin(name, name, true); - }); -} - -/* - * 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. - */ -int reload(js::Context &ctx) -{ - return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) { - irccd.reloadPlugin(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. - */ -int unload(js::Context &ctx) -{ - return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) { - irccd.unloadPlugin(name); - }); -} - -const js::FunctionMap functions{ - { "info", { info, DUK_VARARGS } }, - { "list", { list, 0 } }, - { "load", { load, 1 } }, - { "reload", { reload, 1 } }, - { "unload", { unload, 1 } } -}; - -} // !namespace - -void loadJsPlugin(js::Context &ctx) noexcept -{ - ctx.getGlobal<void>("Irccd"); - ctx.push(js::Object{}); - ctx.push(functions); - ctx.push(js::Object{}); - ctx.putProperty(-2, "config"); - ctx.putProperty(-2, "Plugin"); - ctx.pop(); -} - -} // !irccd
--- a/lib/irccd/js/plugin.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -/* - * js-plugin.h -- 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_H_ -#define _IRCCD_JS_PLUGIN_H_ - -#include "js.h" - -namespace irccd { - -void loadJsPlugin(js::Context &ctx) noexcept; - -} // !irccd - -#endif // !_IRCCD_JS_TIMER_H_ -
--- a/lib/irccd/js/server.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /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/irccd.h> -#include <irccd/server.h> - -#include <irccd/js/js.h> - -namespace irccd { - -namespace { - -/* - * Method: Server.cmode(channel, mode) - * ------------------------------------------------------------------ - * - * Change a channel mode. - * - * Arguments: - * - channel, the channel, - * - mode, the mode. - */ -int cmode(js::Context &ctx) -{ - ctx.self<js::Shared<Server>>()->cmode(ctx.require<std::string>(0), ctx.require<std::string>(1)); - - return 0; -} - -/* - * Method: Server.cnotice(channel, message) - * ------------------------------------------------------------------ - * - * Send a channel notice. - * - * Arguments: - * - channel, the channel, - * - message, the message. - */ -int cnotice(js::Context &ctx) -{ - ctx.self<js::Shared<Server>>()->cnotice(ctx.require<std::string>(0), ctx.require<std::string>(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 - */ -int info(js::Context &ctx) -{ - auto server = ctx.self<js::Shared<Server>>(); - - ctx.push(js::Object{}); - ctx.putProperty(-1, "name", server->info().name); - ctx.putProperty(-1, "host", server->info().host); - ctx.putProperty<int>(-1, "port", server->info().port); - ctx.putProperty<bool>(-1, "ssl", server->info().flags & ServerInfo::Ssl); - ctx.putProperty<bool>(-1, "sslVerify", server->info().flags & ServerInfo::SslVerify); - ctx.putProperty(-1, "commandChar", server->settings().command); - ctx.putProperty(-1, "realname", server->identity().realname); - ctx.putProperty(-1, "nickname", server->identity().nickname); - ctx.putProperty(-1, "username", server->identity().username); - - /* Channels */ - ctx.push(js::Array{}); - - int i = 0; - for (const auto &channel : server->settings().channels) - ctx.putProperty(-1, i++, channel.name); - - ctx.putProperty(-2, "channels"); - - return 1; -} - -/* - * Method: Server.invite(target, channel) - * ------------------------------------------------------------------ - * - * Invite someone to a channel. - * - * Arguments: - * - target, the target to invite, - * - channel, the channel. - */ -int invite(js::Context &ctx) -{ - ctx.self<js::Shared<Server>>()->invite(ctx.require<std::string>(0), ctx.require<std::string>(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. - */ -int join(js::Context &ctx) -{ - ctx.self<js::Shared<Server>>()->join(ctx.require<std::string>(0), ctx.optional<std::string>(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. - */ -int kick(js::Context &ctx) -{ - ctx.self<js::Shared<Server>>()->kick( - ctx.require<std::string>(0), - ctx.require<std::string>(1), - ctx.optional<std::string>(2, "") - ); - - return 0; -} - -/* - * Method: Server.me(target, message) - * ------------------------------------------------------------------ - * - * Send a CTCP Action. - * - * Arguments: - * - target, the target or a channel, - * - message, the message. - */ -int me(js::Context &ctx) -{ - ctx.self<js::Shared<Server>>()->me(ctx.require<std::string>(0), ctx.require<std::string>(1)); - - return 0; -} - -/* - * Method: Server.message(target, message) - * ------------------------------------------------------------------ - * - * Send a message. - * - * Arguments: - * - target, the target or a channel, - * - message, the message. - */ -int message(js::Context &ctx) -{ - ctx.self<js::Shared<Server>>()->message(ctx.require<std::string>(0), ctx.require<std::string>(1)); - - return 0; -} - -/* - * Method: Server.mode(mode) - * ------------------------------------------------------------------ - * - * Change your mode. - * - * Arguments: - * - mode, the new mode. - */ -int mode(js::Context &ctx) -{ - ctx.self<js::Shared<Server>>()->mode(ctx.require<std::string>(0)); - - return 0; -} - -/* - * Method: Server.names(channel) - * ------------------------------------------------------------------ - * - * Get the list of names from a channel. - * - * Arguments: - * - channel, the channel. - */ -int names(js::Context &ctx) -{ - ctx.self<js::Shared<Server>>()->names(ctx.require<std::string>(0)); - - return 0; -} - -/* - * Method: Server.nick(nickname) - * ------------------------------------------------------------------ - * - * Change the nickname. - * - * Arguments: - * - nickname, the nickname. - */ -int nick(js::Context &ctx) -{ - ctx.self<js::Shared<Server>>()->nick(ctx.require<std::string>(0)); - - return 0; -} - -/* - * Method: Server.notice(target, message) - * ------------------------------------------------------------------ - * - * Send a private notice. - * - * Arguments: - * - target, the target, - * - message, the notice message. - */ -int notice(js::Context &ctx) -{ - ctx.self<js::Shared<Server>>()->notice(ctx.require<std::string>(0), ctx.require<std::string>(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. - */ -int part(js::Context &ctx) -{ - ctx.self<js::Shared<Server>>()->part(ctx.require<std::string>(0), ctx.optional<std::string>(1, "")); - - return 0; -} - -/* - * Method: Server.send(raw) - * ------------------------------------------------------------------ - * - * Send a raw message to the IRC server. - * - * Arguments: - * - raw, the raw message (without terminators). - */ -int send(js::Context &ctx) -{ - ctx.self<js::Shared<Server>>()->send(ctx.require<std::string>(0)); - - return 0; -} - -/* - * Method: Server.topic(channel, topic) - * ------------------------------------------------------------------ - * - * Change a channel topic. - * - * Arguments: - * - channel, the channel, - * - topic, the new topic. - */ -int topic(js::Context &ctx) -{ - ctx.self<js::Shared<Server>>()->topic(ctx.require<std::string>(0), ctx.require<std::string>(1)); - - return 0; -} - -/* - * Method: Server.whois(target) - * ------------------------------------------------------------------ - * - * Get whois information. - * - * Arguments: - * - target, the target. - */ -int whois(js::Context &ctx) -{ - ctx.self<js::Shared<Server>>()->whois(ctx.require<std::string>(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). - */ -int toString(js::Context &ctx) -{ - ctx.push(ctx.self<js::Shared<Server>>()->info().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: "!") - */ -int constructor(js::Context &ctx) -{ - if (!duk_is_constructor_call(ctx)) - return 0; - - ServerInfo info; - ServerIdentity identity; - ServerSettings settings; - - /* Information part */ - info.name = ctx.getProperty<std::string>(0, "name"); - info.host = ctx.getProperty<std::string>(0, "host"); - info.port = ctx.optionalProperty<int>(0, "port", info.port); - info.password = ctx.optionalProperty<std::string>(0, "password", ""); - - if (ctx.optionalProperty<bool>(0, "ipv6", false)) - info.flags |= ServerInfo::Ipv6; - - /* Identity part */ - identity.nickname = ctx.optionalProperty<std::string>(0, "nickname", identity.nickname); - identity.username = ctx.optionalProperty<std::string>(0, "username", identity.username); - identity.realname = ctx.optionalProperty<std::string>(0, "realname", identity.realname); - identity.ctcpversion = ctx.optionalProperty<std::string>(0, "version", identity.ctcpversion); - - /* Settings part */ - for (const auto &chan: ctx.getProperty<std::vector<std::string>>(0, "channels")) - settings.channels.push_back(Server::splitChannel(chan)); - - settings.recotries = ctx.optionalProperty<int>(0, "recoTries", settings.recotries); - settings.recotimeout = ctx.optionalProperty<int>(0, "recoTimeout", settings.recotimeout); - - if (ctx.optionalProperty<bool>(0, "joinInvite", false)) - settings.flags |= ServerSettings::JoinInvite; - if (ctx.optionalProperty<bool>(0, "autoRejoin", false)) - settings.flags |= ServerSettings::AutoRejoin; - - try { - ctx.construct(js::Shared<Server>{std::make_shared<Server>(std::move(info), std::move(identity), std::move(settings))}); - } catch (const std::exception &ex) { - ctx.raise(js::Error(ex.what())); - } - - return 0; -} - -/* - * Function: Irccd.Server.add(s) - * ------------------------------------------------------------------ - * - * Register a new server to the irccd instance. - * - * Arguments: - * - s, the server to add. - */ -int add(js::Context &ctx) -{ - std::shared_ptr<Server> server = ctx.get<js::Shared<Server>>(0); - - if (server) - ctx.getGlobal<js::RawPointer<Irccd>>("\xff""\xff""irccd")->addServer(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. - */ -int find(js::Context &ctx) -{ - const auto name = ctx.require<std::string>(0); - const auto irccd = ctx.getGlobal<js::RawPointer<Irccd>>("\xff""\xff""irccd"); - - try { - ctx.push(js::Shared<Server>{irccd->requireServer(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. - */ -int list(js::Context &ctx) -{ - ctx.push(js::Object{}); - - for (const auto &pair : ctx.getGlobal<js::RawPointer<Irccd>>("\xff""\xff""irccd")->servers()) - ctx.putProperty(-1, pair.first, js::Shared<Server>{pair.second}); - - 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. - */ -int remove(js::Context &ctx) -{ - ctx.getGlobal<js::RawPointer<Irccd>>("\xff""\xff""irccd")->removeServer(ctx.require<std::string>(0)); - - return 0; -} - -const js::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 js::FunctionMap functions{ - { "add", { add, 1 } }, - { "find", { find, 1 } }, - { "list", { list, 0 } }, - { "remove", { remove, 1 } } -}; - -} // !namespace - -void loadJsServer(js::Context &ctx) -{ - /* Server prototype for both constructing and passing */ - ctx.push(js::Object{}); - ctx.push(methods); - ctx.putGlobal("\xff""\xff""Server-proto"); - - /* Server object */ - ctx.getGlobal<void>("Irccd"); - ctx.push(js::Function{constructor, 1}); - ctx.push(functions); - - /* Prototype */ - ctx.getGlobal<void>("\xff""\xff""Server-proto"); - ctx.push(methods); - ctx.putProperty(-1, "\xff""\xff""Server", true); - ctx.putProperty(-2, "prototype"); - - /* Put Server */ - ctx.putProperty(-2, "Server"); - ctx.pop(); -} - -} // !irccd
--- a/lib/irccd/js/server.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -/* - * js-server.h -- 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_H_ -#define _IRCCD_JS_SERVER_H_ - -#include "js.h" - -namespace irccd { - -void loadJsServer(js::Context &ctx); - -} // !irccd - -#endif // !_IRCCD_JS_SERVER_H_
--- a/lib/irccd/js/system.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,236 +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 <irccd-config.h> - -#include <chrono> - -#if defined(HAVE_POPEN) -# include <cstdio> -#endif - -#include <cstdlib> -#include <thread> - -#include <irccd/system.h> - -#include <irccd/js/file.h> -#include <irccd/js/irccd.h> -#include <irccd/js/system.h> - -namespace irccd { - -namespace { - -/* - * Function: Irccd.System.env(key) - * ------------------------------------------------------------------ - * - * Get an environment system variable. - * - * Arguments: - * - key, the environment variable. - * Returns: - * The value. - */ -int env(js::Context &ctx) -{ - ctx.push(sys::env(ctx.get<std::string>(0))); - - return 1; -} - -/* - * Function: Irccd.System.exec(cmd) - * ------------------------------------------------------------------ - * - * Execute a system command. - * - * Arguments: - * - cmd, the command to execute. - */ -int exec(js::Context &ctx) -{ - std::system(ctx.get<const char *>(0)); - - return 0; -} - -/* - * Function: Irccd.System.home() - * ------------------------------------------------------------------ - * - * Get the operating system user's home. - * - * Returns: - * The user home directory. - */ -int home(js::Context &ctx) -{ - ctx.push(sys::home()); - - return 1; -} - -/* - * Function: Irccd.System.name() - * ------------------------------------------------------------------ - * - * Get the operating system name. - * - * Returns: - * The system name. - */ -int name(js::Context &ctx) -{ - ctx.push(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(js::Context &ctx) -{ - auto fp = ::popen(ctx.require<const char *>(0), ctx.require<const char *>(1)); - - if (fp == nullptr) - ctx.raise(SystemError{}); - - ctx.push(js::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(js::Context &ctx) -{ - std::this_thread::sleep_for(std::chrono::seconds(ctx.get<int>(0))); - - return 0; -} - -/* - * Function: Irccd.System.ticks() - * ------------------------------------------------------------------ - * - * Get the number of milliseconds since irccd was started. - * - * Returns: - * The number of milliseconds. - */ -int ticks(js::Context &ctx) -{ - ctx.push(static_cast<int>(sys::ticks())); - - return 1; -} - -/* - * Function: Irccd.System.usleep(delay) - * ------------------------------------------------------------------ - * - * Sleep the main loop for the specific delay in microseconds. - */ -int usleep(js::Context &ctx) -{ - std::this_thread::sleep_for(std::chrono::microseconds(ctx.get<int>(0))); - - return 0; -} - -/* - * Function: Irccd.System.uptime() - * ------------------------------------------------------------------ - * - * Get the system uptime. - * - * Returns: - * The system uptime. - */ -int uptime(js::Context &ctx) -{ - ctx.push<int>(sys::uptime()); - - return 0; -} - -/* - * Function: Irccd.System.version() - * ------------------------------------------------------------------ - * - * Get the operating system version. - * - * Returns: - * The system version. - */ -int version(js::Context &ctx) -{ - ctx.push(sys::version()); - - return 1; -} - -const js::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(js::Context &ctx) -{ - ctx.getGlobal<void>("Irccd"); - ctx.push(js::Object{}); - ctx.push(functions); - ctx.putProperty(-2, "System"); - ctx.pop(); -} - -} // !irccd
--- a/lib/irccd/js/system.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -/* - * js-system.h -- 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_H_ -#define _IRCCD_JS_SYSTEM_H_ - -#include "js.h" - -namespace irccd { - -void loadJsSystem(js::Context &ctx); - -} // !irccd - -#endif // !_IRCCD_JS_SYSTEM_H_
--- a/lib/irccd/js/timer.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,143 +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 <irccd/plugin.h> - -#include <irccd/js/js.h> - -namespace irccd { - -class JsTimer : public Timer { -public: - using Timer::Timer; - - ~JsTimer() - { - stop(); - } - - static inline const char *name() noexcept - { - return "\xff""\xff""Timer"; - } -}; - -namespace { - -/* - * Method: Timer.start() - * -------------------------------------------------------- - * - * Start the timer. If the timer is already started the method - * is a no-op. - */ -int start(js::Context &ctx) -{ - auto timer = ctx.self<js::Shared<JsTimer>>(); - - if (!timer->isRunning()) - timer->start(); - - return 0; -} - -/* - * Method: Timer.stop() - * -------------------------------------------------------- - * - * Stop the timer. - */ -int stop(js::Context &ctx) -{ - auto timer = ctx.self<js::Shared<JsTimer>>(); - - if (timer->isRunning()) - timer->stop(); - - return 0; -} - -const js::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. - */ -int constructor(js::Context &ctx) -{ - int type = ctx.require<int>(0); - int delay = ctx.require<int>(1); - - if (!duk_is_callable(ctx, 2)) - ctx.raise(js::TypeError{"missing callback function"}); - - auto timer = std::make_shared<JsTimer>(static_cast<TimerType>(type), delay); - - /* Add this timer to the underlying plugin */ - ctx.getGlobal<js::RawPointer<Plugin>>("\xff""\xff""plugin")->addTimer(timer); - - /* Construct object */ - ctx.construct(js::Shared<JsTimer>{timer}); - - /* Now store the JavaScript function to call */ - ctx.dup(2); - ctx.putGlobal("\xff""\xff""timer-" + std::to_string(reinterpret_cast<std::intptr_t>(timer.get()))); - - return 0; -} - -const js::Map<int> constants{ - { "Single", static_cast<int>(TimerType::Single) }, - { "Repeat", static_cast<int>(TimerType::Repeat) }, -}; - -} // !namespace - -void loadJsTimer(js::Context &ctx) noexcept -{ - ctx.getGlobal<void>("Irccd"); - - /* Timer object */ - ctx.push(js::Function{constructor, 3}); - ctx.push(constants); - - /* Prototype */ - ctx.push(js::Object{}); - ctx.push(methods); - ctx.putProperty(-1, "\xff""\xff""Timer", true); - ctx.putProperty(-2, "prototype"); - - /* Put Timer */ - ctx.putProperty(-2, "Timer"); - ctx.pop(); -} - -} // !irccd
--- a/lib/irccd/js/timer.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -/* - * js-timer.h -- 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_H_ -#define _IRCCD_JS_TIMER_H_ - -#include "js.h" - -namespace irccd { - -void loadJsTimer(js::Context &ctx) noexcept; - -} // !irccd - -#endif // !_IRCCD_JS_TIMER_H_ -
--- a/lib/irccd/js/unicode.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,143 +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 <irccd/unicode.h> - -#include "js.h" - -namespace irccd { - -namespace { - -/* - * Function: Irccd.Unicode.isDigit(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is in the digit category. - */ -int isDigit(js::Context &ctx) -{ - ctx.push(unicode::isdigit(ctx.get<int>(0))); - - return 1; -} - -/* - * Function: Irccd.Unicode.isLetter(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is in the letter category. - */ -int isLetter(js::Context &ctx) -{ - ctx.push(unicode::isalpha(ctx.get<int>(0))); - - return 1; -} - -/* - * Function: Irccd.Unicode.isLower(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is lower case. - */ -int isLower(js::Context &ctx) -{ - ctx.push(unicode::islower(ctx.get<int>(0))); - - return 1; -} - -/* - * Function: Irccd.Unicode.isSpace(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is in the space category. - */ -int isSpace(js::Context &ctx) -{ - ctx.push(unicode::isspace(ctx.get<int>(0))); - - return 1; -} - -/* - * Function: Irccd.Unicode.isTitle(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is title case. - */ -int isTitle(js::Context &ctx) -{ - ctx.push(unicode::istitle(ctx.get<int>(0))); - - return 1; -} - -/* - * Function: Irccd.Unicode.isUpper(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is upper case. - */ -int isUpper(js::Context &ctx) -{ - ctx.push(unicode::isupper(ctx.get<int>(0))); - - return 1; -} - -const js::FunctionMap functions{ - { "isDigit", { isDigit, 1 } }, - { "isLetter", { isLetter, 1 } }, - { "isLower", { isLower, 1 } }, - { "isSpace", { isSpace, 1 } }, - { "isTitle", { isTitle, 1 } }, - { "isUpper", { isUpper, 1 } }, -}; - -} // !namespace - -void loadJsUnicode(js::Context &ctx) -{ - ctx.getGlobal<void>("Irccd"); - ctx.push(js::Object{}); - ctx.push(functions); - ctx.putProperty(-2, "Unicode"); - ctx.pop(); -} - -} // !irccd
--- a/lib/irccd/js/unicode.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +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_H_ -#define _IRCCD_JS_UNICODE_H_ - -#include "js.h" - -namespace irccd { - -void loadJsUnicode(js::Context &ctx); - -} // !irccd - -#endif // !_IRCCD_JS_UNICODE_H_
--- a/lib/irccd/js/util.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,149 +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 <irccd/util.h> - -#include <irccd/js/util.h> - -namespace irccd { - -namespace js { - -/** - * 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 TypeInfo<util::Substitution> { -public: - static util::Substitution get(Context &ctx, int index) - { - util::Substitution params; - - if (!ctx.is<Object>(index)) - return params; - - ctx.enumerate(index, 0, true, [&] (Context &) { - if (ctx.get<std::string>(-2) == "date") - params.time = static_cast<time_t>(ctx.get<double>(-1) / 1000); - else - params.keywords.insert({ctx.get<std::string>(-2), ctx.get<std::string>(-1)}); - }); - - return params; - } -}; - -} // !js - -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. - */ -int format(js::Context &ctx) -{ - try { - ctx.push(util::format(ctx.get<std::string>(0), ctx.get<util::Substitution>(1))); - } catch (const std::exception &ex) { - ctx.raise(js::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. - */ -int splituser(js::Context &ctx) -{ - const char *target = ctx.require<const char *>(0); - char nick[32] = {0}; - - irc_target_get_nick(target, nick, sizeof (nick) -1); - ctx.push(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. - */ -int splithost(js::Context &ctx) -{ - const char *target = ctx.require<const char *>(0); - char host[32] = {0}; - - irc_target_get_host(target, host, sizeof (host) -1); - ctx.push(std::string(host)); - - return 1; -} - -const js::FunctionMap functions{ - { "format", { format, DUK_VARARGS } }, - { "splituser", { splituser, 1 } }, - { "splithost", { splithost, 1 } } -}; - -} // !namespace - -void loadJsUtil(js::Context &ctx) -{ - ctx.getGlobal<void>("Irccd"); - ctx.push(js::Object{}); - ctx.push(functions); - ctx.putProperty(-2, "Util"); - ctx.pop(); -} - -} // !irccd
--- a/lib/irccd/js/util.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -/* - * js-util.h -- 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_H_ -#define _IRCCD_JS_UTIL_H_ - -#include "js.h" - -namespace irccd { - -void loadJsUtil(js::Context &ctx); - -} // !irccd - -#endif // !_IRCCD_JS_UTIL_H_
--- a/lib/irccd/path.cpp Sat Mar 26 14:41:53 2016 +0100 +++ b/lib/irccd/path.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -53,10 +53,10 @@ # include <libproc.h> # endif -# include "private/xdg.h" +# include "xdg.h" #endif -#include "private/filesystem.h" +#include "filesystem.h" #include "path.h" #include "system.h" #include "util.h"
--- a/lib/irccd/plugin.cpp Sat Mar 26 14:41:53 2016 +0100 +++ b/lib/irccd/plugin.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -26,24 +26,22 @@ # include <cstring> #endif -#include "private/filesystem.h" +#include "filesystem.h" +#include "js-directory.h" +#include "js-elapsed-timer.h" +#include "js-file.h" +#include "js-irccd.h" +#include "js-logger.h" +#include "js-plugin.h" +#include "js-server.h" +#include "js-system.h" +#include "js-timer.h" +#include "js-unicode.h" +#include "js-util.h" #include "path.h" -#include "util.h" - -#include "js/directory.h" -#include "js/elapsed-timer.h" -#include "js/file.h" -#include "js/irccd.h" -#include "js/logger.h" -#include "js/plugin.h" -#include "js/server.h" -#include "js/system.h" -#include "js/timer.h" -#include "js/unicode.h" -#include "js/util.h" - #include "plugin.h" #include "server.h" +#include "util.h" using namespace std; @@ -51,33 +49,33 @@ void Plugin::call(const string &name, unsigned nargs) { - m_context.getGlobal<void>(name); + duk::getGlobal<void>(m_context, name); - if (m_context.type(-1) == DUK_TYPE_UNDEFINED) { + if (duk::type(m_context, -1) == DUK_TYPE_UNDEFINED) { /* Function not defined, remove the undefined value and all arguments */ - m_context.pop(nargs + 1); + duk::pop(m_context, nargs + 1); } else { /* Call the function and discard the result */ - m_context.insert(-nargs - 1); - m_context.pcall(nargs); - m_context.pop(); + duk::insert(m_context, -nargs - 1); + duk::pcall(m_context, nargs); + duk::pop(m_context); } } void Plugin::putVars() { - js::StackAssert sa{m_context}; + duk::StackAssert sa{m_context}; /* Save a reference to this */ - m_context.putGlobal("\xff""\xff""plugin", js::RawPointer<Plugin>{this}); - m_context.putGlobal("\xff""\xff""name", m_info.name); - m_context.putGlobal("\xff""\xff""path", m_info.path); - m_context.putGlobal("\xff""\xff""parent", m_info.parent); + duk::putGlobal(m_context, "\xff""\xff""plugin", duk::RawPointer<Plugin>{this}); + duk::putGlobal(m_context, "\xff""\xff""name", m_info.name); + duk::putGlobal(m_context, "\xff""\xff""path", m_info.path); + duk::putGlobal(m_context, "\xff""\xff""parent", m_info.parent); } void Plugin::putPath(const std::string &varname, const std::string &append, path::Path type) { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); bool found = true; std::string foundpath; @@ -98,15 +96,15 @@ if (!found) foundpath = path::clean(path::get(type, path::OwnerSystem) + append); - m_context.getGlobal<void>("Irccd"); - m_context.getProperty<void>(-1, "Plugin"); - m_context.putProperty(-1, varname, foundpath); - m_context.pop(2); + duk::getGlobal<void>(m_context, "Irccd"); + duk::getProperty<void>(m_context, -1, "Plugin"); + duk::putProperty(m_context, -1, varname, foundpath); + duk::pop(m_context, 2); } void Plugin::putPaths() { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); /* * dataPath: DATA + plugin/name (e.g ~/.local/share/irccd/plugins/<name>/) @@ -119,28 +117,30 @@ void Plugin::putConfig(const PluginConfig &config) { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); // TODO: override dataPath, configPath, cachePath /* Store plugin configuration into Irccd.Plugin.config */ - m_context.getGlobal<void>("Irccd"); - m_context.getProperty<void>(-1, "Plugin"); - m_context.getProperty<void>(-1, "config"); + duk::getGlobal<void>(m_context, "Irccd"); + duk::getProperty<void>(m_context, -1, "Plugin"); + duk::getProperty<void>(m_context, -1, "config"); - if (m_context.type(-1) != DUK_TYPE_OBJECT) { - m_context.pop(); - m_context.push(js::Object{}); + if (duk::type(m_context, -1) != DUK_TYPE_OBJECT) { + duk::pop(m_context); + duk::push(m_context, duk::Object{}); } - m_context.push(config); - m_context.putProperty(-2, "config"); - m_context.pop(2); + for (const auto &pair : config) + duk::putProperty(m_context, -1, pair.first, pair.second); + + duk::putProperty(m_context, -2, "config"); + duk::pop(m_context, 2); } Plugin::Plugin(std::string name, std::string path, const PluginConfig &config) { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); m_info.name = std::move(name); m_info.path = std::move(path); @@ -186,23 +186,25 @@ putPaths(); /* Try to load the file (does not call onLoad yet) */ - m_context.peval(js::File{m_info.path}); - m_context.pop(); + if (duk::pevalFile(m_context, m_info.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 */ - m_context.getGlobal<void>("info"); + duk::getGlobal<void>(m_context, "info"); - if (m_context.type(-1) == DUK_TYPE_OBJECT) { - m_info.author = m_context.optionalProperty<std::string>(-1, "author", "unknown"); - m_info.license = m_context.optionalProperty<std::string>(-1, "license", "unknown"); - m_info.summary = m_context.optionalProperty<std::string>(-1, "summary", "unknown"); - m_info.version = m_context.optionalProperty<std::string>(-1, "version", "unknown"); + if (duk::type(m_context, -1) == DUK_TYPE_OBJECT) { + m_info.author = duk::optionalProperty<std::string>(m_context, -1, "author", "unknown"); + m_info.license = duk::optionalProperty<std::string>(m_context, -1, "license", "unknown"); + m_info.summary = duk::optionalProperty<std::string>(m_context, -1, "summary", "unknown"); + m_info.version = duk::optionalProperty<std::string>(m_context, -1, "version", "unknown"); } - m_context.pop(); + duk::pop(m_context); log::debug() << "plugin " << m_info.name << ": " << std::endl; log::debug() << " author: " << m_info.author << std::endl; @@ -211,6 +213,12 @@ log::debug() << " version: " << m_info.version << std::endl; } +Plugin::~Plugin() +{ + for (auto &timer : m_timers) + timer->stop(); +} + const PluginInfo &Plugin::info() const { return m_info; @@ -242,9 +250,11 @@ void Plugin::removeTimer(const std::shared_ptr<Timer> &timer) noexcept { + duk::StackAssert sa(m_context); + /* Remove the JavaScript function */ - m_context.push(js::Null{}); - m_context.putGlobal("\xff""\xff""timer-" + std::to_string(reinterpret_cast<std::intptr_t>(timer.get()))); + duk::push(m_context, duk::Null{}); + duk::putGlobal(m_context, "\xff""\xff""timer-" + std::to_string(reinterpret_cast<std::intptr_t>(timer.get()))); /* Remove from list */ m_timers.erase(timer); @@ -252,229 +262,229 @@ void Plugin::onChannelMode(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string mode, std::string arg) { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); - m_context.push(js::Shared<Server>{server}); - m_context.push(move(origin)); - m_context.push(move(channel)); - m_context.push(move(mode)); - m_context.push(move(arg)); + duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, move(origin)); + duk::push(m_context, move(channel)); + duk::push(m_context, move(mode)); + duk::push(m_context, move(arg)); call("onChannelMode", 5); } void Plugin::onChannelNotice(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string notice) { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); - m_context.push(js::Shared<Server>{server}); - m_context.push(move(origin)); - m_context.push(move(channel)); - m_context.push(move(notice)); + duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, move(origin)); + duk::push(m_context, move(channel)); + duk::push(m_context, move(notice)); call("onChannelNotice", 4); } void Plugin::onCommand(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string message) { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); - m_context.push(js::Shared<Server>{server}); - m_context.push(move(origin)); - m_context.push(move(channel)); - m_context.push(move(message)); + duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, move(origin)); + duk::push(m_context, move(channel)); + duk::push(m_context, move(message)); call("onCommand", 4); } void Plugin::onConnect(std::shared_ptr<Server> server) { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); - m_context.push(js::Shared<Server>{server}); + duk::push(m_context, duk::Shared<Server>{server}); call("onConnect", 1); } void Plugin::onInvite(std::shared_ptr<Server> server, std::string origin, std::string channel) { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); - m_context.push(js::Shared<Server>{server}); - m_context.push(move(origin)); - m_context.push(move(channel)); + duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, move(origin)); + duk::push(m_context, move(channel)); call("onInvite", 3); } void Plugin::onJoin(std::shared_ptr<Server> server, std::string origin, std::string channel) { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); - m_context.push(js::Shared<Server>{server}); - m_context.push(move(origin)); - m_context.push(move(channel)); + duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, move(origin)); + duk::push(m_context, move(channel)); call("onJoin", 3); } void Plugin::onKick(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string target, std::string reason) { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); - m_context.push(js::Shared<Server>{server}); - m_context.push(move(origin)); - m_context.push(move(channel)); - m_context.push(move(target)); - m_context.push(move(reason)); + duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, move(origin)); + duk::push(m_context, move(channel)); + duk::push(m_context, move(target)); + duk::push(m_context, move(reason)); call("onKick", 5); } void Plugin::onLoad() { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); call("onLoad", 0); } void Plugin::onMessage(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string message) { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); - m_context.push(js::Shared<Server>{server}); - m_context.push(move(origin)); - m_context.push(move(channel)); - m_context.push(move(message)); + duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, move(origin)); + duk::push(m_context, move(channel)); + duk::push(m_context, move(message)); call("onMessage", 4); } void Plugin::onMe(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string message) { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); - m_context.push(js::Shared<Server>{server}); - m_context.push(move(origin)); - m_context.push(move(channel)); - m_context.push(move(message)); + duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, move(origin)); + duk::push(m_context, move(channel)); + duk::push(m_context, move(message)); call("onMe", 4); } void Plugin::onMode(std::shared_ptr<Server> server, std::string origin, std::string mode) { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); - m_context.push(js::Shared<Server>{server}); - m_context.push(move(origin)); - m_context.push(move(mode)); + duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, move(origin)); + duk::push(m_context, move(mode)); call("onMode", 3); } void Plugin::onNames(std::shared_ptr<Server> server, std::string channel, std::vector<std::string> names) { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); - m_context.push(js::Shared<Server>{server}); - m_context.push(move(channel)); - m_context.push(move(names)); + duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, move(channel)); + duk::push(m_context, move(names)); call("onNames", 3); } void Plugin::onNick(std::shared_ptr<Server> server, std::string oldnick, std::string newnick) { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); - m_context.push(js::Shared<Server>{server}); - m_context.push(move(oldnick)); - m_context.push(move(newnick)); + duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, move(oldnick)); + duk::push(m_context, move(newnick)); call("onNick", 3); } void Plugin::onNotice(std::shared_ptr<Server> server, std::string origin, std::string notice) { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); - m_context.push(js::Shared<Server>{server}); - m_context.push(move(origin)); - m_context.push(move(notice)); + duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, move(origin)); + duk::push(m_context, move(notice)); call("onNotice", 3); } void Plugin::onPart(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string reason) { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); - m_context.push(js::Shared<Server>{server}); - m_context.push(move(origin)); - m_context.push(move(channel)); - m_context.push(move(reason)); + duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, move(origin)); + duk::push(m_context, move(channel)); + duk::push(m_context, move(reason)); call("onPart", 4); } void Plugin::onQuery(std::shared_ptr<Server> server, std::string origin, std::string message) { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); - m_context.push(js::Shared<Server>{server}); - m_context.push(move(origin)); - m_context.push(move(message)); + duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, move(origin)); + duk::push(m_context, move(message)); call("onQuery", 3); } void Plugin::onQueryCommand(std::shared_ptr<Server> server, std::string origin, std::string message) { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); - m_context.push(js::Shared<Server>{server}); - m_context.push(move(origin)); - m_context.push(move(message)); + duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, move(origin)); + duk::push(m_context, move(message)); call("onQueryCommand", 3); } void Plugin::onReload() { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); call("onReload"); } void Plugin::onTopic(std::shared_ptr<Server> server, std::string origin, std::string channel, std::string topic) { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); - m_context.push(js::Shared<Server>{server}); - m_context.push(move(origin)); - m_context.push(move(channel)); - m_context.push(move(topic)); + duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, move(origin)); + duk::push(m_context, move(channel)); + duk::push(m_context, move(topic)); call("onTopic", 4); } void Plugin::onUnload() { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); call("onUnload"); } void Plugin::onWhois(std::shared_ptr<Server> server, ServerWhois whois) { - js::StackAssert sa(m_context); + duk::StackAssert sa(m_context); - m_context.push(js::Shared<Server>{server}); - m_context.push(js::Object{}); - m_context.putProperty(-1, "nickname", whois.nick); - m_context.putProperty(-1, "username", whois.user); - m_context.putProperty(-1, "realname", whois.realname); - m_context.putProperty(-1, "host", whois.host); - m_context.putProperty(1, "channels", whois.channels); + duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, duk::Object{}); + duk::putProperty(m_context, -1, "nickname", whois.nick); + duk::putProperty(m_context, -1, "username", whois.user); + duk::putProperty(m_context, -1, "realname", whois.realname); + duk::putProperty(m_context, -1, "host", whois.host); + duk::putProperty(m_context, 1, "channels", whois.channels); call("onWhois", 2); } -namespace js { +namespace duk { -void TypeInfo<PluginInfo>::push(Context &ctx, const PluginInfo &info) +void TypeTraits<irccd::PluginInfo>::push(ContextPtr ctx, const PluginInfo &info) { - js::StackAssert sa(ctx, 1); + duk::StackAssert sa(ctx, 1); - ctx.push(js::Object{}); - ctx.putProperty(-1, "name", info.name); - ctx.putProperty(-1, "author", info.author); - ctx.putProperty(-1, "license", info.license); - ctx.putProperty(-1, "summary", info.summary); - ctx.putProperty(-1, "version", info.version); + duk::push(ctx, duk::Object{}); + duk::putProperty(ctx, -1, "name", info.name); + duk::putProperty(ctx, -1, "author", info.author); + duk::putProperty(ctx, -1, "license", info.license); + duk::putProperty(ctx, -1, "summary", info.summary); + duk::putProperty(ctx, -1, "version", info.version); } } // !js
--- a/lib/irccd/plugin.h Sat Mar 26 14:41:53 2016 +0100 +++ b/lib/irccd/plugin.h Wed Mar 30 13:52:47 2016 +0200 @@ -30,11 +30,9 @@ #include <unordered_set> #include <vector> -#include <irccd/path.h> -#include <irccd/private/signals.h> - -#include "js/js.h" - +#include "js.h" +#include "path.h" +#include "signals.h" #include "timer.h" namespace irccd { @@ -102,7 +100,7 @@ private: /* JavaScript context */ - js::Context m_context; + duk::Context m_context; /* Plugin info and its timers */ PluginInfo m_info; @@ -127,6 +125,11 @@ Plugin(std::string name, std::string path, const PluginConfig &config = PluginConfig()); /** + * Temporary, close all timers. + */ + ~Plugin(); + + /** * Get the plugin information. */ const PluginInfo &info() const; @@ -150,7 +153,7 @@ * * @return the context */ - inline js::Context &context() noexcept + inline duk::Context &context() noexcept { return m_context; } @@ -342,15 +345,15 @@ void onWhois(std::shared_ptr<Server> server, ServerWhois info); }; -namespace js { +namespace duk { template <> -class TypeInfo<PluginInfo> { +class TypeTraits<irccd::PluginInfo> { public: - static void push(Context &ctx, const PluginInfo &info); + static void push(ContextPtr ctx, const irccd::PluginInfo &info); }; -} // !js +} // !duk } // !irccd
--- a/lib/irccd/private/CMakeSources.cmake Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ -set( - PRIVATE_HEADERS - ${CMAKE_CURRENT_LIST_DIR}/connection.h - ${CMAKE_CURRENT_LIST_DIR}/directory.h - ${CMAKE_CURRENT_LIST_DIR}/elapsed-timer.h - ${CMAKE_CURRENT_LIST_DIR}/ini.h - ${CMAKE_CURRENT_LIST_DIR}/filesystem.h - ${CMAKE_CURRENT_LIST_DIR}/sockets.h - ${CMAKE_CURRENT_LIST_DIR}/xdg.h -) - -set( - PRIVATE_SOURCES - ${CMAKE_CURRENT_LIST_DIR}/connection.cpp - ${CMAKE_CURRENT_LIST_DIR}/directory.cpp - ${CMAKE_CURRENT_LIST_DIR}/elapsed-timer.cpp - ${CMAKE_CURRENT_LIST_DIR}/ini.cpp - ${CMAKE_CURRENT_LIST_DIR}/filesystem.cpp - ${CMAKE_CURRENT_LIST_DIR}/sockets.cpp - ${CMAKE_CURRENT_LIST_DIR}/xdg.cpp -)
--- a/lib/irccd/private/connection.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -/* - * connection.cpp -- value wrapper for connecting to irccd - * - * 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/logger.h> - -#include "connection.h" - -namespace irccd { - -json::Value Connection::next(const std::string &name, int timeout) -{ - m_timer.reset(); - - while (isConnected()) { - json::Value object = next(clamp(timeout)); - - if (object.isObject() && object["response"].toString() == name) - return object; - } - - throw std::runtime_error("connection lost"); -} - -void Connection::verify(const std::string &name, int timeout) -{ - auto object = next(name, timeout); - auto value = object.at("status").toString(); - - if (!value.empty() && value != "ok") - throw std::runtime_error(object.at("error").toString()); -} - -} // !irccd
--- a/lib/irccd/private/connection.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,238 +0,0 @@ -/* - * connection.h -- value wrapper for connecting to irccd - * - * 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_CONNECTION_H_ -#define _IRCCD_CONNECTION_H_ - -#include <cassert> -#include <stdexcept> - -#include <irccd/json.h> -#include <irccd/system.h> -#include <irccd/util.h> - -#include "elapsed-timer.h" -#include "sockets.h" - -namespace irccd { - -/** - * @class Connection - * @brief Abstract class for connecting to irccd from Ip or Local addresses. - */ -class Connection { -protected: - ElapsedTimer m_timer; - - /** - * Clamp the time to wait to be sure that it will be never less than 0. - */ - inline int clamp(int timeout) noexcept - { - return timeout < 0 ? -1 : (timeout - (int)m_timer.elapsed() < 0) ? 0 : (timeout - m_timer.elapsed()); - } - -public: - /** - * Default constructor. - */ - Connection() = default; - - /** - * Default destructor. - */ - virtual ~Connection() = default; - - /** - * Wait for the next requested response. - * - * @param name the response name - * @param timeout the optional timeout - * @return the object - * @throw net::Error on errors or on timeout - */ - json::Value next(const std::string &name, int timeout = 30000); - - /** - * Just wait if the operation succeeded. - * - * @param name the response name - * @param timeout the timeout - */ - void verify(const std::string &name, int timeout = 30000); - - /** - * Check if the socket is still connected. - * - * @return true if connected - */ - virtual bool isConnected() const noexcept = 0; - - /** - * Try to connect to the host. - * - * @param timeout the maximum time in milliseconds - * @throw net::Error on errors or timeout - */ - virtual void connect(int timeout = 30000) = 0; - - /** - * Try to send the message in 30 seconds. The message must not end with \r\n\r\n, it is added automatically. - * - * @pre msg must not be empty - * @param msg the message to send - * @param timeout the maximum time in milliseconds - * @throw net::Error on errors - */ - virtual void send(std::string msg, int timeout = 30000) = 0; - - /** - * Get the next event from irccd. - * - * This functions throws if the connection is lost. - * - * @param timeout the maximum time in milliseconds - * @return the next event - * @throw net::Error on errors or disconnection - */ - virtual json::Value next(int timeout = 30000) = 0; -}; - -/** - * @class ConnectionBase - * @brief Implementation for Ip or Local. - */ -template <typename Address> -class ConnectionBase : public Connection { -private: - net::SocketTcp<Address> m_socket; - net::Listener<> m_listener; - Address m_address; - - /* Input buffer */ - std::string m_input; - -public: - /** - * Construct the socket but do not connect immediately. - * - * @param address the address - */ - ConnectionBase(Address address) - : m_address(std::move(address)) - { - m_socket.set(net::option::SockBlockMode{false}); - m_listener.set(m_socket.handle(), net::Condition::Readable); - } - - /** - * @copydoc Connection::isConnected - */ - bool isConnected() const noexcept override - { - return m_socket.state() == net::State::Connected; - } - - /** - * @copydoc Connection::connect - */ - void connect(int timeout) override; - - /** - * @copydoc Connection::send - */ - void send(std::string msg, int timeout) override; - - /** - * @copydoc Connection::next - */ - json::Value next(int timeout) override; -}; - -template <typename Address> -void ConnectionBase<Address>::connect(int timeout) -{ - m_socket.connect(m_address); - - if (m_socket.state() == net::State::Connecting) { - m_listener.set(m_socket.handle(), net::Condition::Writable); - m_listener.wait(timeout); - m_socket.connect(); - m_listener.unset(m_socket.handle(), net::Condition::Writable); - } -} - -template <typename Address> -void ConnectionBase<Address>::send(std::string msg, int timeout) -{ - assert(!msg.empty()); - - /* Add termination */ - msg += "\r\n\r\n"; - - m_listener.remove(m_socket.handle()); - m_listener.set(m_socket.handle(), net::Condition::Writable); - m_timer.reset(); - - while (!msg.empty()) { - /* Do not wait the time that is already passed */ - m_listener.wait(clamp(timeout)); - - /* Try to send at most as possible */ - msg.erase(0, m_socket.send(msg)); - } - - /* Timeout? */ - if (!msg.empty()) - throw std::runtime_error("operation timed out while sending to irccd"); -} - -template <typename Address> -json::Value ConnectionBase<Address>::next(int timeout) -{ - /* Maybe there is already something */ - std::string buffer = util::nextNetwork(m_input); - - m_listener.remove(m_socket.handle()); - m_listener.set(m_socket.handle(), net::Condition::Readable); - m_timer.reset(); - - /* Read if there is nothing */ - while (buffer.empty() && isConnected()) { - /* Wait and read */ - m_listener.wait(clamp(timeout)); - m_input += m_socket.recv(512); - - /* Finally try */ - buffer = util::nextNetwork(m_input); - } - - if (!isConnected()) - throw std::runtime_error("connection lost"); - - json::Value value(json::Buffer{buffer}); - - if (!value.isObject()) - throw std::invalid_argument("invalid message received"); - - return value; -} - -} // !irccd - -#endif // !_IRCCD_CONNECTION_H_
--- a/lib/irccd/private/directory.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,188 +0,0 @@ -/* - * directory.cpp -- open and read directories - * - * 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 <stdexcept> - -#include "directory.h" - -#if defined(_WIN32) -# include <Windows.h> -#else -# include <cstring> -# include <cerrno> - -# include <sys/types.h> -# include <dirent.h> -#endif - -namespace irccd { - -#if defined(_WIN32) - -namespace { - -std::string systemError() -{ - LPSTR error = nullptr; - std::string errmsg = "Unknown error"; - - FormatMessageA( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - GetLastError(), - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)&error, 0, NULL); - - if (error) { - errmsg = std::string(error); - LocalFree(error); - } - - return errmsg; -} - -} // !namespace - -void Directory::systemLoad(const std::string &path, int flags) -{ - std::ostringstream oss; - HANDLE handle; - WIN32_FIND_DATA fdata; - - oss << path << "\\*"; - handle = FindFirstFile(oss.str().c_str(), &fdata); - - if (handle == nullptr) - throw std::runtime_error(systemError()); - - do { - DirectoryEntry entry; - - entry.name = fdata.cFileName; - if (entry.name == "." && !(flags & Directory::Dot)) - continue; - if (entry.name == ".." && !(flags & Directory::DotDot)) - continue; - - switch (fdata.dwFileAttributes) { - case FILE_ATTRIBUTE_DIRECTORY: - entry.type = DirectoryEntry::Dir; - break; - case FILE_ATTRIBUTE_NORMAL: - entry.type = DirectoryEntry::File; - break; - case FILE_ATTRIBUTE_REPARSE_POINT: - entry.type = DirectoryEntry::Link; - break; - default: - break; - } - - m_list.push_back(entry); - } while (FindNextFile(handle, &fdata) != 0); - - FindClose(handle); -} - -#else - -void Directory::systemLoad(const std::string &path, int flags) -{ - DIR *dp; - struct dirent *ent; - - if ((dp = opendir(path.c_str())) == nullptr) - throw std::runtime_error(std::strerror(errno)); - - while ((ent = readdir(dp)) != nullptr) { - DirectoryEntry entry; - - entry.name = ent->d_name; - if (entry.name == "." && !(flags & Directory::Dot)) - continue; - if (entry.name == ".." && !(flags & Directory::DotDot)) - continue; - - switch (ent->d_type) { - case DT_DIR: - entry.type = DirectoryEntry::Dir; - break; - case DT_REG: - entry.type = DirectoryEntry::File; - break; - case DT_LNK: - entry.type = DirectoryEntry::Link; - break; - default: - break; - } - - m_list.push_back(entry); - } - - closedir(dp); -} - -#endif - -bool operator==(const DirectoryEntry &e1, const DirectoryEntry &e2) -{ - return e1.name == e2.name && e1.type == e2.type; -} - -Directory::Directory() -{ -} - -Directory::Directory(const std::string &path, int flags) -{ - systemLoad(path, flags); -} - -Directory::List::iterator Directory::begin() -{ - return m_list.begin(); -} - -Directory::List::const_iterator Directory::cbegin() const -{ - return m_list.cbegin(); -} - -Directory::List::iterator Directory::end() -{ - return m_list.end(); -} - -Directory::List::const_iterator Directory::cend() const -{ - return m_list.cend(); -} - -int Directory::count() const -{ - return m_list.size(); -} - -bool operator==(const Directory &d1, const Directory &d2) -{ - return d1.m_list == d2.m_list; -} - -} // !irccd
--- a/lib/irccd/private/directory.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,139 +0,0 @@ -/* - * directory.h -- open and read directories - * - * 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 _DIRECTORY_H_ -#define _DIRECTORY_H_ - -#include <cstddef> -#include <string> -#include <vector> - -namespace irccd { - -/** - * @class Entry - * @brief entry in the directory list - */ -class DirectoryEntry { -public: - /** - * @enum Type - * @brief Describe the type of an entry - */ - enum Type { - Unknown = 0, - File, - Dir, - Link - }; - - std::string name; //! name of entry (base name) - Type type{Unknown}; //! type of file - - friend bool operator==(const DirectoryEntry &e1, const DirectoryEntry &e2); -}; - -/** - * @class Directory - * @brief class to manipulate directories - * - * This class allow the user to iterate directories in a for range based - * loop using iterators. - */ -class Directory { -public: - /** - * @enum Flags - * @brief optional flags to read directories - */ - enum Flags { - Dot = (1 << 0), //!< If set, lists "." too - DotDot = (1 << 1) //!< If set, lists ".." too - }; - - using List = std::vector<DirectoryEntry>; - - // C++ Container compatibility - using value_type = List::value_type; - using iterator = List::iterator; - using const_iterator = List::const_iterator; - -private: - List m_list; - - void systemLoad(const std::string &path, int flags); - -public: - /** - * Default constructor, does nothing. - */ - Directory(); - - /** - * Open a directory and read all its content. - * @param path the path - * @param flags the optional flags - */ - Directory(const std::string &path, int flags = 0); - - /** - * Virtual destructor defaulted. - */ - virtual ~Directory() = default; - - /** - * Return an iterator the beginning. - * - * @return the iterator - */ - List::iterator begin(); - - /** - * Return a const iterator the beginning. - * - * @return the iterator - */ - List::const_iterator cbegin() const; - - /** - * Return an iterator to past the end. - * - * @return the iterator - */ - List::iterator end(); - - /** - * Return a const iterator to past the end. - * - * @return the iterator - */ - List::const_iterator cend() const; - - /** - * Get the number of entries in the directory. - * - * @return the number - */ - int count() const; - - friend bool operator==(const Directory &d1, const Directory &d2); -}; - -} // !irccd - -#endif // !_DIRECTORY_H_
--- a/lib/irccd/private/elapsed-timer.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -/* - * elapsed-timer.cpp -- measure elapsed time - * - * 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.h" - -using std::chrono::duration_cast; -using std::chrono::high_resolution_clock; -using std::chrono::milliseconds; - -namespace irccd { - -ElapsedTimer::ElapsedTimer() noexcept -{ - m_last = high_resolution_clock::now(); -} - -void ElapsedTimer::pause() noexcept -{ - /* - * When we put the timer on pause, do not forget to set the already - * elapsed time. - */ - (void)elapsed(); - m_paused = true; -} - -void ElapsedTimer::restart() noexcept -{ - m_paused = false; - m_last = high_resolution_clock::now(); -} - -void ElapsedTimer::reset() noexcept -{ - m_elapsed = 0; - m_last = high_resolution_clock::now(); -} - -unsigned ElapsedTimer::elapsed() noexcept -{ - if (!m_paused) { - m_elapsed += duration_cast<milliseconds>(high_resolution_clock::now() - m_last).count(); - m_last = high_resolution_clock::now(); - } - - return m_elapsed; -} - -} // !irccd
--- a/lib/irccd/private/elapsed-timer.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,86 +0,0 @@ -/* - * elapsed-timer.h -- measure elapsed time - * - * 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_ELAPSED_TIMER_H_ -#define _IRCCD_ELAPSED_TIMER_H_ - -/** - * @file ElapsedTimer.h - * @brief Measure elapsed time - */ - -#include <chrono> - -namespace irccd { - -/** - * @class ElapsedTimer - * @brief Measure elapsed time - * - * This class provides an abstraction to measure elapsed time since the - * construction of the object. - * - * It uses std::chrono::high_resolution_clock for more precision and uses - * milliseconds only. - */ -class ElapsedTimer { -public: - using TimePoint = std::chrono::time_point<std::chrono::high_resolution_clock>; - -private: - TimePoint m_last; - bool m_paused{false}; - unsigned m_elapsed{0}; - -public: - /** - * Construct the elapsed timer, start counting. - */ - ElapsedTimer() noexcept; - - /** - * Virtual destructor defaulted. - */ - virtual ~ElapsedTimer() = default; - - /** - * Put the timer on pause, the already elapsed time is stored. - */ - void pause() noexcept; - - /** - * Restart the timer, does not reset it. - */ - void restart() noexcept; - - /** - * Reset the timer to 0. - */ - void reset() noexcept; - - /** - * Get the number of elapsed milliseconds. - * - * @return the milliseconds - */ - unsigned elapsed() noexcept; -}; - -} // !irccd - -#endif // !_IRCCD_ELAPSED_TIMER_H_
--- a/lib/irccd/private/filesystem.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,187 +0,0 @@ -/* - * filesystem.cpp -- some file system operation - * - * 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 <cstdlib> -#include <cstring> -#include <stdexcept> -#include <sstream> - -#include <irccd-config.h> - -#if defined(IRCCD_SYSTEM_WINDOWS) -# include <direct.h> -# include <shlwapi.h> -#else -# include <sys/stat.h> -# include <climits> -# include <unistd.h> -# include <libgen.h> -#endif - -#include "filesystem.h" - -namespace irccd { - -namespace fs { - -#if defined(IRCCD_SYSTEM_WINDOWS) -const char Separator('\\'); -#else -const char Separator('/'); -#endif - -std::string baseName(std::string path) -{ -#if defined(IRCCD_SYSTEM_WINDOWS) - std::size_t pos; - - pos = path.find_last_of('\\'); - if (pos == std::string::npos) - pos = path.find_last_of('/'); - if (pos == std::string::npos) - return path; - - return path.substr(pos + 1); -#else - return basename(&path[0]); -#endif -} - -std::string dirName(std::string path) -{ -#if defined(IRCCD_SYSTEM_WINDOWS) - std::size_t pos; - - pos = path.find_last_of('\\'); - if (pos == std::string::npos) - pos = path.find_last_of('/'); - if (pos == std::string::npos) - return path; - - return path.substr(0, pos); -#else - return dirname(&path[0]); -#endif -} - -bool isAbsolute(const std::string &path) noexcept -{ -#if defined(IRCCD_SYSTEM_WINDOWS) - return !isRelative(path); -#else - return path.size() > 0 && path[0] == '/'; -#endif -} - -bool isRelative(const std::string &path) noexcept -{ -#if defined(IRCCD_SYSTEM_WINDOWS) - return PathIsRelativeA(path.c_str()); -#else - return !isAbsolute(path); -#endif -} - -bool exists(const std::string &path) -{ -#if defined(HAVE_ACCESS) - return access(path.c_str(), F_OK) == 0; -#elif defined(HAVE_STAT) - struct stat st; - - return (stat(path.c_str(), &st) == 0); -#else - // worse fallback - std::FILE *file = std::fopen(path.c_str(), "r"); - bool result; - - if (file != nullptr) { - result = true; - std::fclose(file); - } else { - result = false; - } - - return result; -#endif -} - -void mkdir(const std::string &dir, int mode) -{ - std::ostringstream oss; - - oss << "mkdir: "; - - for (std::size_t i = 0; i < dir.length(); ++i) { - if (dir[i] != '/' && dir[i] != '\\') - continue; - - std::string part = dir.substr(0, i); - if (part.length() <= 0 || exists(part)) - continue; - -#if defined(IRCCD_SYSTEM_WINDOWS) - if (::_mkdir(part.c_str()) < 0) { -#else - if (::mkdir(part.c_str(), mode) < 0) { -#endif - oss << part << ": " << std::strerror(errno); - throw std::runtime_error(oss.str()); - } - } - - // Last part -#if defined(IRCCD_SYSTEM_WINDOWS) - if (::_mkdir(dir.c_str()) < 0) { -#else - if (::mkdir(dir.c_str(), mode) < 0) { -#endif - oss << dir << ": " << std::strerror(errno); - throw std::runtime_error(oss.str()); - } - -#if defined(IRCCD_SYSTEM_WINDOWS) - // Windows's mkdir does not use mode. - (void)mode; -#endif -} - -std::string cwd() -{ -#if defined(IRCCD_SYSTEM_WINDOWS) - char path[MAX_PATH]; - - if (!GetCurrentDirectoryA(sizeof (path), path)) - throw std::runtime_error("failed to get current working directory"); - - return path; -#else - char path[PATH_MAX]; - - if (getcwd(path, sizeof (path)) == nullptr) - throw std::runtime_error(std::strerror(errno)); - - return path; -#endif -} - -} // !fs - -} // !irccd
--- a/lib/irccd/private/filesystem.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -/* - * filesystem.h -- some file system operation - * - * 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_FILESYSTEM_H_ -#define _IRCCD_FILESYSTEM_H_ - -#include <string> - -namespace irccd { - -namespace fs { - -extern const char Separator; - -std::string baseName(std::string path); -std::string dirName(std::string path); -bool isAbsolute(const std::string &path) noexcept; -bool isRelative(const std::string &path) noexcept; -bool exists(const std::string &path); -void mkdir(const std::string &dir, int mode = 0700); -std::string cwd(); - -} // !fs - -} // !irccd - -#endif // !_IRCCD_FILESYSTEM_H_
--- a/lib/irccd/private/ini.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,427 +0,0 @@ -/* - * ini.cpp -- .ini file parsing - * - * 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 <cctype> -#include <cstring> -#include <iostream> -#include <iterator> -#include <fstream> -#include <sstream> -#include <stdexcept> - -#if defined(_WIN32) -# include <Shlwapi.h> // for PathIsRelative -#endif - -#include "ini.h" - -namespace { - -using namespace irccd; -using namespace irccd::ini; - -using StreamIterator = std::istreambuf_iterator<char>; -using TokenIterator = std::vector<Token>::const_iterator; - -inline bool isAbsolute(const std::string &path) noexcept -{ -#if defined(_WIN32) - return !PathIsRelative(path.c_str()); -#else - return path.size() > 0 && path[0] == '/'; -#endif -} - -inline bool isQuote(char c) noexcept -{ - return c == '\'' || c == '"'; -} - -inline bool isSpace(char c) noexcept -{ - /* Custom version because std::isspace includes \n as space */ - return c == ' ' || c == '\t'; -} - -inline bool isList(char c) noexcept -{ - return c == '(' || c == ')' || c == ','; -} - -inline bool isReserved(char c) noexcept -{ - return isList(c) || isQuote(c) || c == '[' || c == ']' || c == '@' || c == '#' || c == '='; -} - -void analyzeLine(int &line, int &column, StreamIterator &it) noexcept -{ - assert(*it == '\n'); - - ++ line; - ++ it; - column = 0; -} - -void analyzeComment(int &column, StreamIterator &it, StreamIterator end) noexcept -{ - assert(*it == '#'); - - while (it != end && *it != '\n') { - ++ column; - ++ it; - } -} - -void analyzeSpaces(int &column, StreamIterator &it, StreamIterator end) noexcept -{ - assert(isSpace(*it)); - - while (it != end && isSpace(*it)) { - ++ column; - ++ it; - } -} - -void analyzeList(Tokens &list, int line, int &column, StreamIterator &it) noexcept -{ - assert(isList(*it)); - - switch (*it++) { - case '(': - list.emplace_back(Token::ListBegin, line, column++); - break; - case ')': - list.emplace_back(Token::ListEnd, line, column++); - break; - case ',': - list.emplace_back(Token::Comma, line, column++); - break; - default: - break; - } -} - -void analyzeSection(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end) -{ - assert(*it == '['); - - std::string value; - int save = column; - - /* Read section name */ - ++ it; - while (it != end && *it != ']') { - if (*it == '\n') - throw Error(line, column, "section not terminated, missing ']'"); - if (isReserved(*it)) - throw Error(line, column, "section name expected after '[', got '" + std::string(1, *it) + "'"); - ++ column; - value += *it++; - } - - if (it == end) - throw Error(line, column, "section name expected after '[', got <EOF>"); - - /* Remove ']' */ - ++ it; - - list.emplace_back(Token::Section, line, save, std::move(value)); -} - -void analyzeAssign(Tokens &list, int &line, int &column, StreamIterator &it) -{ - assert(*it == '='); - - list.push_back({ Token::Assign, line, column++ }); - ++ it; -} - -void analyzeQuotedWord(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end) -{ - std::string value; - int save = column; - char quote = *it++; - - while (it != end && *it != quote) { - // TODO: escape sequence - ++ column; - value += *it++; - } - - if (it == end) - throw Error(line, column, "undisclosed '" + std::string(1, quote) + "', got <EOF>"); - - /* Remove quote */ - ++ it; - - list.push_back({ Token::QuotedWord, line, save, std::move(value) }); -} - -void analyzeWord(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end) -{ - assert(!isReserved(*it)); - - std::string value; - int save = column; - - while (it != end && !std::isspace(*it) && !isReserved(*it)) { - ++ column; - value += *it++; - } - - list.push_back({ Token::Word, line, save, std::move(value) }); -} - -void analyzeInclude(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end) -{ - assert(*it == '@'); - - std::string include; - int save = column; - - /* Read include */ - ++ it; - while (it != end && !isSpace(*it)) { - ++ column; - include += *it++; - } - - if (include != "include") - throw Error(line, column, "expected include after '@' token"); - - list.push_back({ Token::Include, line, save }); -} - -Tokens analyze(StreamIterator &it, StreamIterator end) -{ - Tokens list; - int line = 1; - int column = 0; - - while (it != end) { - if (*it == '\n') - analyzeLine(line, column, it); - else if (*it == '#') - analyzeComment(column, it, end); - else if (*it == '[') - analyzeSection(list, line, column, it, end); - else if (*it == '=') - analyzeAssign(list, line, column, it); - else if (isSpace(*it)) - analyzeSpaces(column, it, end); - else if (*it == '@') - analyzeInclude(list, line, column, it, end); - else if (isQuote(*it)) - analyzeQuotedWord(list, line, column, it, end); - else if (isList(*it)) - analyzeList(list, line, column, it); - else - analyzeWord(list, line, column, it, end); - } - - return list; -} - -void parseOptionValueSimple(Option &option, TokenIterator &it) -{ - assert(it->type() == Token::Word || it->type() == Token::QuotedWord); - - option.push_back((it++)->value()); -} - -void parseOptionValueList(Option &option, TokenIterator &it, TokenIterator end) -{ - assert(it->type() == Token::ListBegin); - - TokenIterator save = it++; - - while (it != end && it->type() != Token::ListEnd) { - switch (it->type()) { - case Token::Comma: - /* Previous must be a word */ - if (it[-1].type() != Token::Word && it[-1].type() != Token::QuotedWord) - throw Error(it->line(), it->column(), "unexpected comma after '" + it[-1].value() + "'"); - - ++ it; - break; - case Token::Word: - case Token::QuotedWord: - option.push_back((it++)->value()); - break; - default: - throw Error(it->line(), it->column(), "unexpected '" + it[-1].value() + "' in list construct"); - break; - } - } - - if (it == end) - throw Error(save->line(), save->column(), "unterminated list construct"); - - /* Remove ) */ - ++ it; -} - -void parseOption(Section &sc, TokenIterator &it, TokenIterator end) -{ - Option option(it->value()); - - TokenIterator save = it; - - /* No '=' or something else? */ - if (++it == end) - throw Error(save->line(), save->column(), "expected '=' assignment, got <EOF>"); - if (it->type() != Token::Assign) - throw Error(it->line(), it->column(), "expected '=' assignment, got " + it->value()); - - /* Empty options are allowed so just test for words */ - if (++it != end) { - if (it->type() == Token::Word || it->type() == Token::QuotedWord) - parseOptionValueSimple(option, it); - else if (it->type() == Token::ListBegin) - parseOptionValueList(option, it, end); - } - - sc.push_back(std::move(option)); -} - -void parseInclude(Document &doc, TokenIterator &it, TokenIterator end) -{ - TokenIterator save = it; - - if (++it == end) - throw Error(save->line(), save->column(), "expected file name after '@include' statement, got <EOF>"); - if (it->type() != Token::Word && it->type() != Token::QuotedWord) - throw Error(it->line(), it->column(), "expected file name after '@include' statement, got " + it->value()); - if (doc.path().empty()) - throw Error(it->line(), it->column(), "'@include' statement invalid with buffer documents"); - - std::string value = (it++)->value(); - std::string file; - - if (!isAbsolute(value)) { -#if defined(_WIN32) - file = doc.path() + "\\" + value; -#else - file = doc.path() + "/" + value; -#endif - } else { - file = value; - } - - Document child(File{file}); - - for (const auto &sc : child) - doc.push_back(sc); -} - -void parseSection(Document &doc, TokenIterator &it, TokenIterator end) -{ - Section sc(it->value()); - - /* Skip [section] */ - ++ it; - - /* Read until next section */ - while (it != end && it->type() != Token::Section) { - if (it->type() != Token::Word) - throw Error(it->line(), it->column(), "unexpected token '" + it->value() + "' in section definition"); - - parseOption(sc, it, end); - } - - doc.push_back(std::move(sc)); -} - -void parse(Document &doc, const Tokens &tokens) -{ - TokenIterator it = tokens.cbegin(); - TokenIterator end = tokens.cend(); - - while (it != end) { - /* Just ignore this */ - switch (it->type()) { - case Token::Include: - parseInclude(doc, it, end); - break; - case Token::Section: - parseSection(doc, it, end); - break; - default: - throw Error(it->line(), it->column(), "unexpected '" + it->value() + "' on root document"); - } - } -} - -} // !namespace - -namespace irccd { - -namespace ini { - -Tokens Document::analyze(const File &file) -{ - std::fstream stream(file.path); - - if (!stream) - throw std::runtime_error(std::strerror(errno)); - - std::istreambuf_iterator<char> it(stream); - std::istreambuf_iterator<char> end; - - return ::analyze(it, end); -} - -Tokens Document::analyze(const Buffer &buffer) -{ - std::istringstream stream(buffer.text); - std::istreambuf_iterator<char> it(stream); - std::istreambuf_iterator<char> end; - - return ::analyze(it, end); -} - -Document::Document(const File &file) - : m_path(file.path) -{ - /* Update path */ - auto pos = m_path.find_last_of("/\\"); - - if (pos != std::string::npos) - m_path.erase(pos); - else - m_path = "."; - - parse(*this, analyze(file)); -} - -Document::Document(const Buffer &buffer) -{ - parse(*this, analyze(buffer)); -} - -void Document::dump(const Tokens &tokens) -{ - for (const Token &token: tokens) - // TODO: add better description - std::cout << token.line() << ":" << token.column() << ": " << token.value() << std::endl; -} - -} // !ini - -} // !irccd
--- a/lib/irccd/private/ini.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,538 +0,0 @@ -/* - * ini.h -- .ini file parsing - * - * 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 _INI_H_ -#define _INI_H_ - -/** - * @file Ini.h - * @brief Configuration file parser. - */ - -#include <algorithm> -#include <exception> -#include <stdexcept> -#include <string> -#include <vector> - -namespace irccd { - -/** - * Namespace for ini related classes. - */ -namespace ini { - -class Document; - -/** - * @class Error - * @brief Error in a file - */ -class Error : public std::exception { -private: - int m_line; //!< line number - int m_column; //!< line column - std::string m_message; //!< error message - -public: - /** - * Constructor. - * - * @param l the line - * @param c the column - * @param m the message - */ - inline Error(int l, int c, std::string m) noexcept - : m_line(l) - , m_column(c) - , m_message(std::move(m)) - { - } - - /** - * Get the line number. - * - * @return the line - */ - inline int line() const noexcept - { - return m_line; - } - - /** - * Get the column number. - * - * @return the column - */ - inline int column() const noexcept - { - return m_column; - } - - /** - * Return the raw error message (no line and column shown). - * - * @return the error message - */ - const char *what() const noexcept override - { - return m_message.c_str(); - } -}; - -/** - * @class Token - * @brief Describe a token read in the .ini source - * - * This class can be used when you want to parse a .ini file yourself. - * - * @see Document::analyze - */ -class Token { -public: - /** - * @brief Token type - */ - enum Type { - Include, //!< include statement - Section, //!< [section] - Word, //!< word without quotes - QuotedWord, //!< word with quotes - Assign, //!< = assignment - ListBegin, //!< begin of list ( - ListEnd, //!< end of list ) - Comma //!< list separation - }; - -private: - Type m_type; - int m_line; - int m_column; - std::string m_value; - -public: - /** - * Construct a token. - * - * @param type the type - * @param line the line - * @param column the column - * @param value the value - */ - Token(Type type, int line, int column, std::string value = "") noexcept - : m_type(type) - , m_line(line) - , m_column(column) - { - switch (type) { - case Include: - m_value = "@include"; - break; - case Section: - case Word: - case QuotedWord: - m_value = value; - break; - case Assign: - m_value = "="; - break; - case ListBegin: - m_value = "("; - break; - case ListEnd: - m_value = ")"; - break; - case Comma: - m_value = ","; - break; - default: - break; - } - } - - /** - * Get the type. - * - * @return the type - */ - inline Type type() const noexcept - { - return m_type; - } - - /** - * Get the line. - * - * @return the line - */ - inline int line() const noexcept - { - return m_line; - } - - /** - * Get the column. - * - * @return the column - */ - inline int column() const noexcept - { - return m_column; - } - - /** - * Get the value. For words, quoted words and section, the value is the content. Otherwise it's the - * characters parsed. - * - * @return the value - */ - inline const std::string &value() const noexcept - { - return m_value; - } -}; - -/** - * List of tokens in order they are analyzed. - */ -using Tokens = std::vector<Token>; - -/** - * @class Option - * @brief Option definition. - */ -class Option : public std::vector<std::string> { -private: - std::string m_key; - -public: - /** - * Construct an empty option. - * - * @param key the key - * @param value the value - */ - inline Option(std::string key) noexcept - : std::vector<std::string>() - , m_key(std::move(key)) - { - } - - /** - * Construct a single option. - * - * @param key the key - * @param value the value - */ - inline Option(std::string key, std::string value) noexcept - : m_key(std::move(key)) - { - push_back(std::move(value)); - } - - /** - * Construct a list option. - * - * @param key the key - * @param values the values - */ - inline Option(std::string key, std::vector<std::string> values) noexcept - : std::vector<std::string>(std::move(values)) - , m_key(std::move(key)) - { - } - - /** - * Get the option key. - * - * @return the key - */ - inline const std::string &key() const noexcept - { - return m_key; - } - - /** - * Get the option value. - * - * @return the value - */ - inline const std::string &value() const noexcept - { - static std::string dummy; - - return empty() ? dummy : (*this)[0]; - } -}; - -/** - * @class Section - * @brief Section that contains one or more options. - */ -class Section : public std::vector<Option> { -private: - std::string m_key; - -public: - /** - * Construct a section with its name. - * - * @param key the key - */ - inline Section(std::string key) noexcept - : m_key(std::move(key)) - { - } - - /** - * Get the section key. - * - * @return the key - */ - inline const std::string &key() const noexcept - { - return m_key; - } - - /** - * Check if the section contains a specific option. - * - * @param key the option key - * @return true if the option exists - */ - inline bool contains(const std::string &key) const noexcept - { - return find(key) != end(); - } - - /** - * Access an option at the specified key. - * - * @param key the key - * @return the option - * @throw std::out_of_range if the key does not exist - */ - inline Option &operator[](const std::string &key) - { - return *find(key); - } - - /** - * Access an option at the specified key. - * - * @param key the key - * @return the option - * @throw std::out_of_range if the key does not exist - */ - inline const Option &operator[](const std::string &key) const - { - return *find(key); - } - - /** - * Find an option by key and return an iterator. - * - * @param key the key - * @return the iterator or end() if not found - */ - inline iterator find(const std::string &key) noexcept - { - return std::find_if(begin(), end(), [&] (const auto &o) { - return o.key() == key; - }); - } - - /** - * Find an option by key and return an iterator. - * - * @param key the key - * @return the iterator or end() if not found - */ - inline const_iterator find(const std::string &key) const noexcept - { - return std::find_if(cbegin(), cend(), [&] (const auto &o) { - return o.key() == key; - }); - } - - /** - * Inherited operators. - */ - using std::vector<Option>::operator[]; -}; - -/** - * @class File - * @brief Source for reading .ini files. - */ -class File { -public: - /** - * Path to the file. - */ - std::string path; -}; - -/** - * @class Buffer - * @brief Source for reading ini from text. - * @note the include statement is not supported with buffers. - */ -class Buffer { -public: - /** - * The ini content. - */ - std::string text; -}; - -/** - * @class Document - * @brief Ini config file loader - */ -class Document : public std::vector<Section> { -private: - std::string m_path; - -public: - /** - * Analyze a file and extract tokens. If the function succeeds, that does not mean the content is valid, - * it just means that there are no syntax error. - * - * For example, this class does not allow adding options under no sections and this function will not - * detect that issue. - * - * @param file the file to read - * @return the list of tokens - * @throws Error on errors - */ - static Tokens analyze(const File &file); - - /** - * Overloaded function for buffers. - * - * @param buffer the buffer to read - * @return the list of tokens - * @throws Error on errors - */ - static Tokens analyze(const Buffer &buffer); - - /** - * Show all tokens and their description. - * - * @param tokens the tokens - */ - static void dump(const Tokens &tokens); - - /** - * Construct a document from a file. - * - * @param file the file to read - * @throws Error on errors - */ - Document(const File &file); - - /** - * Overloaded constructor for buffers. - * - * @param buffer the buffer to read - * @throws Error on errors - */ - Document(const Buffer &buffer); - - /** - * Get the current document path, only useful when constructed from File source. - * - * @return the path - */ - inline const std::string &path() const noexcept - { - return m_path; - } - - /** - * Check if a document has a specific section. - * - * @param key the key - */ - inline bool contains(const std::string &key) const noexcept - { - return std::find_if(begin(), end(), [&] (const auto &sc) { return sc.key() == key; }) != end(); - } - - /** - * Access a section at the specified key. - * - * @param key the key - * @return the section - * @throw std::out_of_range if the key does not exist - */ - inline Section &operator[](const std::string &key) - { - return *find(key); - } - - /** - * Access a section at the specified key. - * - * @param key the key - * @return the section - * @throw std::out_of_range if the key does not exist - */ - inline const Section &operator[](const std::string &key) const - { - return *find(key); - } - - /** - * Find a section by key and return an iterator. - * - * @param key the key - * @return the iterator or end() if not found - */ - inline iterator find(const std::string &key) noexcept - { - return std::find_if(begin(), end(), [&] (const auto &o) { - return o.key() == key; - }); - } - - /** - * Find a section by key and return an iterator. - * - * @param key the key - * @return the iterator or end() if not found - */ - inline const_iterator find(const std::string &key) const noexcept - { - return std::find_if(cbegin(), cend(), [&] (const auto &o) { - return o.key() == key; - }); - } - - /** - * Inherited operators. - */ - using std::vector<Section>::operator[]; -}; - -} // !ini - -} // !irccd - -#endif // !_INI_H_
--- a/lib/irccd/private/signals.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,175 +0,0 @@ -/* - * signals.h -- synchronous observer mechanism - * - * 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_SIGNALS_H_ -#define _IRCCD_SIGNALS_H_ - -#include <functional> -#include <stack> -#include <unordered_map> -#include <vector> - -/** - * @file Signals.h - * @brief Similar Qt signal subsystem for irccd - */ - -namespace irccd { - -/** - * @class SignalConnection - * @brief Stores the reference to the callable - * - * This class can be stored to remove a registered function from a Signal, be - * careful to not mix connections between different signals as they are just - * referenced by ids. - */ -class SignalConnection { -private: - unsigned m_id; - -public: - /** - * Create a signal connection. - * - * @param id the id - */ - inline SignalConnection(unsigned id) noexcept - : m_id(id) - { - } - - /** - * Get the reference object. - * - * @return the id - */ - inline unsigned id() const noexcept - { - return m_id; - } -}; - -/** - * @class Signal - * @brief Stores and call registered functions - * - * This class is intended to be use as a public field in the desired object. - * - * The user just have to call one of connect(), disconnect() or the call - * operator to use this class. - * - * It stores the callable as std::function so type-erasure is complete. - * - * The user is responsible of taking care that the object is still alive - * in case that the function takes a reference to the object. - */ -template <typename... Args> -class Signal { -private: - using Function = std::function<void (Args...)>; - using FunctionMap = std::unordered_map<unsigned, Function>; - using Stack = std::stack<unsigned>; - - FunctionMap m_functions; - Stack m_stack; - unsigned m_max{0}; - -public: - /** - * Register a new function to the signal. - * - * @param function the function - * @return the connection in case you want to remove it - */ - inline SignalConnection connect(Function function) noexcept - { - unsigned id; - - if (!m_stack.empty()) { - id = m_stack.top(); - m_stack.pop(); - } else { - id = m_max ++; - } - - m_functions.emplace(id, std::move(function)); - - return SignalConnection{id}; - } - - /** - * Disconnect a connection. - * - * @param connection the connection - * @warning Be sure that the connection belongs to that signal - */ - inline void disconnect(const SignalConnection &connection) noexcept - { - auto value = m_functions.find(connection.id()); - - if (value != m_functions.end()) { - m_functions.erase(connection.id()); - m_stack.push(connection.id()); - } - } - - /** - * Remove all registered functions. - */ - inline void clear() - { - m_functions.clear(); - m_max = 0; - - while (!m_stack.empty()) - m_stack.pop(); - } - - /** - * Call every functions. - * - * @param args the arguments to pass to the signal - */ - void operator()(Args... args) const - { - /* - * Make a copy of the ids before iterating because the callbacks may eventually remove or modify - * the list. - */ - std::vector<unsigned> ids; - - for (auto &pair : m_functions) - ids.push_back(pair.first); - - /* - * Now iterate while checking if the next id is still available, however if any new signals were - * added while iterating, they will not be called immediately. - */ - for (unsigned i : ids) { - auto it = m_functions.find(i); - - if (it != m_functions.end()) - it->second(args...); - } - } -}; - -} // !irccd - -#endif // !_IRCCD_SIGNALS_H_
--- a/lib/irccd/private/sockets.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,713 +0,0 @@ -/* - * sockets.cpp -- portable C++ socket wrappers - * - * 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. - */ - -#define TIMEOUT_MSG "operation timeout" - -#include <algorithm> -#include <atomic> -#include <cstring> -#include <mutex> - -#include "sockets.h" - -namespace irccd { - -namespace net { - -/* - * Portable constants - * ------------------------------------------------------------------ - */ - -/* {{{ Constants */ - -#if defined(_WIN32) - -const Handle Invalid{INVALID_SOCKET}; -const int Failure{SOCKET_ERROR}; - -#else - -const Handle Invalid{-1}; -const int Failure{-1}; - -#endif - -/* }}} */ - -/* - * Portable functions - * ------------------------------------------------------------------ - */ - -/* {{{ Functions */ - -#if defined(_WIN32) - -namespace { - -static std::mutex s_mutex; -static std::atomic<bool> s_initialized{false}; - -} // !namespace - -#endif // !_WIN32 - -void init() noexcept -{ -#if defined(_WIN32) - std::lock_guard<std::mutex> lock(s_mutex); - - if (!s_initialized) { - s_initialized = true; - - WSADATA wsa; - WSAStartup(MAKEWORD(2, 2), &wsa); - - /* - * If SOCKET_WSA_NO_INIT is not set then the user - * must also call finish himself. - */ -#if !defined(SOCKET_NO_AUTO_INIT) - atexit(finish); -#endif - } -#endif -} - -void finish() noexcept -{ -#if defined(_WIN32) - WSACleanup(); -#endif -} - -std::string error(int errn) -{ -#if defined(_WIN32) - LPSTR str = nullptr; - std::string errmsg = "Unknown error"; - - FormatMessageA( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - errn, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)&str, 0, NULL); - - - if (str) { - errmsg = std::string(str); - LocalFree(str); - } - - return errmsg; -#else - return strerror(errn); -#endif -} - -std::string error() -{ -#if defined(_WIN32) - return error(WSAGetLastError()); -#else - return error(errno); -#endif -} - -/* }}} */ - -/* - * SSL stuff - * ------------------------------------------------------------------ - */ - -/* {{{ SSL initialization */ - -#if !defined(SOCKET_NO_SSL) - -namespace ssl { - -namespace { - -std::mutex mutex; -std::atomic<bool> initialized{false}; - -} // !namespace - -void finish() noexcept -{ - ERR_free_strings(); -} - -void init() noexcept -{ - std::lock_guard<std::mutex> lock{mutex}; - - if (!initialized) { - initialized = true; - - SSL_library_init(); - SSL_load_error_strings(); - OpenSSL_add_all_algorithms(); - -#if !defined(SOCKET_NO_AUTO_SSL_INIT) - atexit(finish); -#endif // SOCKET_NO_AUTO_SSL_INIT - } -} - -} // !ssl - -#endif // SOCKET_NO_SSL - -/* }}} */ - -/* - * Error class - * ------------------------------------------------------------------ - */ - -/* {{{ Error */ - -Error::Error(Code code, std::string function) - : m_code{code} - , m_function{std::move(function)} - , m_error{error()} -{ -} - -Error::Error(Code code, std::string function, int n) - : m_code{code} - , m_function{std::move(function)} - , m_error{error(n)} -{ -} - -Error::Error(Code code, std::string function, std::string error) - : m_code{code} - , m_function{std::move(function)} - , m_error{std::move(error)} -{ -} - -/* }}} */ - -/* - * Predefine addressed to be used - * ------------------------------------------------------------------ - */ - -/* {{{ Addresses */ - -namespace address { - -/* Default domain */ -int Ip::m_default{AF_INET}; - -Ip::Ip(Type domain) noexcept - : m_domain(static_cast<int>(domain)) -{ - assert(m_domain == AF_INET6 || m_domain == AF_INET); - - if (m_domain == AF_INET6) { - std::memset(&m_sin6, 0, sizeof (sockaddr_in6)); - } else { - std::memset(&m_sin, 0, sizeof (sockaddr_in)); - } -} - -Ip::Ip(const std::string &host, int port, Type domain) - : m_domain(static_cast<int>(domain)) -{ - assert(m_domain == AF_INET6 || m_domain == AF_INET); - - if (host == "*") { - if (m_domain == AF_INET6) { - std::memset(&m_sin6, 0, sizeof (sockaddr_in6)); - - m_length = sizeof (sockaddr_in6); - m_sin6.sin6_addr = in6addr_any; - m_sin6.sin6_family = AF_INET6; - m_sin6.sin6_port = htons(port); - } else { - std::memset(&m_sin, 0, sizeof (sockaddr_in)); - - m_length = sizeof (sockaddr_in); - m_sin.sin_addr.s_addr = INADDR_ANY; - m_sin.sin_family = AF_INET; - m_sin.sin_port = htons(port); - } - } else { - addrinfo hints, *res; - - std::memset(&hints, 0, sizeof (addrinfo)); - hints.ai_family = domain; - - auto error = getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &res); - if (error != 0) { - throw Error{Error::System, "getaddrinfo", gai_strerror(error)}; - } - - if (m_domain == AF_INET6) { - std::memcpy(&m_sin6, res->ai_addr, res->ai_addrlen); - } else { - std::memcpy(&m_sin, res->ai_addr, res->ai_addrlen); - } - - m_length = res->ai_addrlen; - freeaddrinfo(res); - } -} - -Ip::Ip(const sockaddr_storage *ss, socklen_t length) noexcept - : m_length{length} - , m_domain{ss->ss_family} -{ - assert(ss->ss_family == AF_INET6 || ss->ss_family == AF_INET); - - if (ss->ss_family == AF_INET6) { - std::memcpy(&m_sin6, ss, length); - } else if (ss->ss_family == AF_INET) { - std::memcpy(&m_sin, ss, length); - } -} - -#if !defined(_WIN32) - -Local::Local() noexcept -{ - std::memset(&m_sun, 0, sizeof (sockaddr_un)); -} - -Local::Local(std::string path, bool rm) noexcept - : m_path{std::move(path)} -{ - /* Silently remove the file even if it fails */ - if (rm) { - ::remove(m_path.c_str()); - } - - /* Copy the path */ - std::memset(m_sun.sun_path, 0, sizeof (m_sun.sun_path)); - std::strncpy(m_sun.sun_path, m_path.c_str(), sizeof (m_sun.sun_path) - 1); - - /* Set the parameters */ - m_sun.sun_family = AF_LOCAL; -} - -Local::Local(const sockaddr_storage *ss, socklen_t length) noexcept -{ - assert(ss->ss_family == AF_LOCAL); - - if (ss->ss_family == AF_LOCAL) { - std::memcpy(&m_sun, ss, length); - m_path = reinterpret_cast<const sockaddr_un &>(m_sun).sun_path; - } -} - -#endif // !_WIN32 - -} // !address - -/* }}} */ - -/* - * Select - * ------------------------------------------------------------------ - */ - -/* {{{ Select */ - -std::vector<ListenerStatus> Select::wait(const ListenerTable &table, int ms) -{ - timeval maxwait, *towait; - fd_set readset; - fd_set writeset; - - FD_ZERO(&readset); - FD_ZERO(&writeset); - - Handle max = 0; - - for (const auto &pair : table) { - if ((pair.second & Condition::Readable) == Condition::Readable) { - FD_SET(pair.first, &readset); - } - if ((pair.second & Condition::Writable) == Condition::Writable) { - FD_SET(pair.first, &writeset); - } - - if (pair.first > max) { - max = pair.first; - } - } - - maxwait.tv_sec = 0; - maxwait.tv_usec = ms * 1000; - - // Set to nullptr for infinite timeout. - towait = (ms < 0) ? nullptr : &maxwait; - - auto error = ::select(max + 1, &readset, &writeset, nullptr, towait); - if (error == Failure) { - throw Error{Error::System, "select"}; - } - if (error == 0) { - throw Error{Error::Timeout, "select", TIMEOUT_MSG}; - } - - std::vector<ListenerStatus> sockets; - - for (const auto &pair : table) { - if (FD_ISSET(pair.first, &readset)) { - sockets.push_back(ListenerStatus{pair.first, Condition::Readable}); - } - if (FD_ISSET(pair.first, &writeset)) { - sockets.push_back(ListenerStatus{pair.first, Condition::Writable}); - } - } - - return sockets; -} - -/* }}} */ - -/* - * Poll - * ------------------------------------------------------------------ - */ - -/* {{{ Poll */ - -/* - * Poll implementation - * ------------------------------------------------------------------ - */ - -#if defined(SOCKET_HAVE_POLL) - -#if defined(_WIN32) -# define poll WSAPoll -#endif - -short Poll::toPoll(Condition condition) const noexcept -{ - short result(0); - - if ((condition & Condition::Readable) == Condition::Readable) { - result |= POLLIN; - } - if ((condition & Condition::Writable) == Condition::Writable) { - result |= POLLOUT; - } - - return result; -} - -Condition Poll::toCondition(short &event) const noexcept -{ - Condition condition{Condition::None}; - - /* - * Poll implementations mark the socket differently regarding - * the disconnection of a socket. - * - * At least, even if POLLHUP or POLLIN is set, recv() always - * return 0 so we mark the socket as readable. - */ - if ((event & POLLIN) || (event & POLLHUP)) { - condition |= Condition::Readable; - } - if (event & POLLOUT) { - condition |= Condition::Writable; - } - - /* Reset event for safety */ - event = 0; - - return condition; -} - -void Poll::set(const ListenerTable &, Handle h, Condition condition, bool add) -{ - if (add) { - m_fds.push_back(pollfd{h, toPoll(condition), 0}); - } else { - auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const pollfd &pfd) { - return pfd.fd == h; - }); - - it->events |= toPoll(condition); - } -} - -void Poll::unset(const ListenerTable &, Handle h, Condition condition, bool remove) -{ - auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const pollfd &pfd) { - return pfd.fd == h; - }); - - if (remove) { - m_fds.erase(it); - } else { - it->events &= ~(toPoll(condition)); - } -} - -std::vector<ListenerStatus> Poll::wait(const ListenerTable &, int ms) -{ - auto result = poll(m_fds.data(), m_fds.size(), ms); - if (result == 0) { - throw Error{Error::Timeout, "select", TIMEOUT_MSG}; - } - if (result < 0) { - throw Error{Error::System, "poll"}; - } - - std::vector<ListenerStatus> sockets; - for (auto &fd : m_fds) { - if (fd.revents != 0) { - sockets.push_back(ListenerStatus{fd.fd, toCondition(fd.revents)}); - } - } - - return sockets; -} - -#endif // !SOCKET_HAVE_POLL - -/* }}} */ - -/* - * Epoll implementation - * ------------------------------------------------------------------ - */ - -/* {{{ Epoll */ - -#if defined(SOCKET_HAVE_EPOLL) - -uint32_t Epoll::toEpoll(Condition condition) const noexcept -{ - uint32_t events = 0; - - if ((condition & Condition::Readable) == Condition::Readable) { - events |= EPOLLIN; - } - if ((condition & Condition::Writable) == Condition::Writable) { - events |= EPOLLOUT; - } - - return events; -} - -Condition Epoll::toCondition(uint32_t events) const noexcept -{ - Condition condition{Condition::None}; - - if ((events & EPOLLIN) || (events & EPOLLHUP)) { - condition |= Condition::Readable; - } - if (events & EPOLLOUT) { - condition |= Condition::Writable; - } - - return condition; -} - -void Epoll::update(Handle h, int op, int eflags) -{ - epoll_event ev; - - std::memset(&ev, 0, sizeof (epoll_event)); - - ev.events = eflags; - ev.data.fd = h; - - if (epoll_ctl(m_handle, op, h, &ev) < 0) { - throw Error{Error::System, "epoll_ctl"}; - } -} - -Epoll::Epoll() - : m_handle{epoll_create1(0)} -{ - if (m_handle < 0) { - throw Error{Error::System, "epoll_create"}; - } -} - -Epoll::~Epoll() -{ - close(m_handle); -} - -/* - * For set and unset, we need to apply the whole flags required, so if the socket - * was set to Connection::Readable and user add Connection::Writable, we must - * place both. - */ -void Epoll::set(const ListenerTable &table, Handle sc, Condition condition, bool add) -{ - if (add) { - update(sc, EPOLL_CTL_ADD, toEpoll(condition)); - m_events.resize(m_events.size() + 1); - } else { - update(sc, EPOLL_CTL_MOD, toEpoll(table.at(sc) | condition)); - } -} - -/* - * Unset is a bit complicated case because Listener tells us which - * flag to remove but to update epoll descriptor we need to pass - * the effective flags that we want to be applied. - * - * So we put the same flags that are currently effective and remove the - * requested one. - */ -void Epoll::unset(const ListenerTable &table, Handle sc, Condition condition, bool remove) -{ - if (remove) { - update(sc, EPOLL_CTL_DEL, 0); - m_events.resize(m_events.size() - 1); - } else { - update(sc, EPOLL_CTL_MOD, toEpoll(table.at(sc) & ~(condition))); - } -} - -std::vector<ListenerStatus> Epoll::wait(const ListenerTable &, int ms) -{ - int ret = epoll_wait(m_handle, m_events.data(), m_events.size(), ms); - std::vector<ListenerStatus> result; - - if (ret == 0) { - throw Error{Error::Timeout, "epoll_wait", TIMEOUT_MSG}; - } - if (ret < 0) { - throw Error{Error::System, "epoll_wait"}; - } - - for (int i = 0; i < ret; ++i) { - result.push_back(ListenerStatus{m_events[i].data.fd, toCondition(m_events[i].events)}); - } - - return result; -} - -#endif // !SOCKET_HAVE_EPOLL - -/* }}} */ - -/* - * Kqueue implementation - * ------------------------------------------------------------------ - */ - -/* {{{ Kqueue */ - -#if defined(SOCKET_HAVE_KQUEUE) - -Kqueue::Kqueue() - : m_handle(kqueue()) -{ - if (m_handle < 0) { - throw Error{Error::System, "kqueue"}; - } -} - -Kqueue::~Kqueue() -{ - close(m_handle); -} - -void Kqueue::update(Handle h, int filter, int kflags) -{ - struct kevent ev; - - EV_SET(&ev, h, filter, kflags, 0, 0, nullptr); - - if (kevent(m_handle, &ev, 1, nullptr, 0, nullptr) < 0) { - throw Error{Error::System, "kevent"}; - } -} - -void Kqueue::set(const ListenerTable &, Handle h, Condition condition, bool add) -{ - if ((condition & Condition::Readable) == Condition::Readable) { - update(h, EVFILT_READ, EV_ADD | EV_ENABLE); - } - if ((condition & Condition::Writable) == Condition::Writable) { - update(h, EVFILT_WRITE, EV_ADD | EV_ENABLE); - } - - if (add) { - m_result.resize(m_result.size() + 1); - } -} - -void Kqueue::unset(const ListenerTable &, Handle h, Condition condition, bool remove) -{ - if ((condition & Condition::Readable) == Condition::Readable) { - update(h, EVFILT_READ, EV_DELETE); - } - if ((condition & Condition::Writable) == Condition::Writable) { - update(h, EVFILT_WRITE, EV_DELETE); - } - - if (remove) { - m_result.resize(m_result.size() - 1); - } -} - -std::vector<ListenerStatus> Kqueue::wait(const ListenerTable &, int ms) -{ - std::vector<ListenerStatus> sockets; - timespec ts = { 0, 0 }; - timespec *pts = (ms <= 0) ? nullptr : &ts; - - ts.tv_sec = ms / 1000; - ts.tv_nsec = (ms % 1000) * 1000000; - - int nevents = kevent(m_handle, nullptr, 0, &m_result[0], m_result.capacity(), pts); - - if (nevents == 0) { - throw Error{Error::Timeout, "kevent", TIMEOUT_MSG}; - } - if (nevents < 0) { - throw Error{Error::System, "kevent"}; - } - - for (int i = 0; i < nevents; ++i) { - sockets.push_back(ListenerStatus{ - static_cast<Handle>(m_result[i].ident), - m_result[i].filter == EVFILT_READ ? Condition::Readable : Condition::Writable - }); - } - - return sockets; -} - -#endif // !SOCKET_HAVE_KQUEUE - -/* }}} */ - -} // !net - -} // !irccd
--- a/lib/irccd/private/sockets.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4078 +0,0 @@ -/* - * sockets.h -- portable C++ socket wrappers - * - * 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 _SOCKETS_H_ -#define _SOCKETS_H_ - -/** - * @file Sockets.h - * @brief Portable socket abstraction - * - * # Introduction - * - * This file is a portable networking library. - * - * ## Options - * - * The user may set the following variables before compiling these files: - * - * ### General options - * - * - **SOCKET_NO_AUTO_INIT**: (bool) Set to 0 if you don't want Socket class to - * automatically calls net::init function and net::finish functions. - * - **SOCKET_NO_SSL**: (bool) Set to 0 if you don't have access to OpenSSL library. - * - **SOCKET_NO_AUTO_SSL_INIT**: (bool) Set to 0 if you don't want Socket class with Tls to automatically init - * the OpenSSL library. You will need to call net::ssl::init and net::ssl::finish. - * - * ### Options for Listener class - * - * Feature detection, multiple implementations may be avaible, for example, Linux has poll, select and epoll. - * - * We assume that `select(2)` is always available. - * - * Of course, you can set the variables yourself if you test it with your build system. - * - * - **SOCKET_HAVE_POLL**: Defined on all BSD, Linux. Also defined on Windows - * if _WIN32_WINNT is set to 0x0600 or greater. - * - **SOCKET_HAVE_KQUEUE**: Defined on all BSD and Apple. - * - **SOCKET_HAVE_EPOLL**: Defined on Linux only. - * - **SOCKET_DEFAULT_BACKEND**: Which backend to use (e.g. `Select`). - * - * The preference priority is ordered from left to right. - * - * | System | Backend | Class name | - * |---------------|-------------------------|--------------| - * | Linux | epoll(7) | Epoll | - * | *BSD | kqueue(2) | Kqueue | - * | Windows | poll(2), select(2) | Poll, Select | - * | Mac OS X | kqueue(2) | Kqueue | - */ - -#if defined(_WIN32) -# if _WIN32_WINNT >= 0x0600 && !defined(SOCKET_HAVE_POLL) -# define SOCKET_HAVE_POLL -# endif -#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) -# if !defined(SOCKET_HAVE_KQUEUE) -# define SOCKET_HAVE_KQUEUE -# endif -# if !defined(SOCKET_HAVE_POLL) -# define SOCKET_HAVE_POLL -# endif -#elif defined(__linux__) -# if !defined(SOCKET_HAVE_EPOLL) -# define SOCKET_HAVE_EPOLL -# endif -# if !defined(SOCKET_HAVE_POLL) -# define SOCKET_HAVE_POLL -# endif -#endif - -/* - * Define SOCKET_DEFAULT_BACKEND - * ------------------------------------------------------------------ - */ - -/** - * Defines the default Listener backend to use. - * - * @note Do not rely on the value shown in doxygen. - */ -#if defined(_WIN32) -# if !defined(SOCKET_DEFAULT_BACKEND) -# if defined(SOCKET_HAVE_POLL) -# define SOCKET_DEFAULT_BACKEND Poll -# else -# define SOCKET_DEFAULT_BACKEND Select -# endif -# endif -#elif defined(__linux__) -# include <sys/epoll.h> - -# if !defined(SOCKET_DEFAULT_BACKEND) -# define SOCKET_DEFAULT_BACKEND Epoll -# endif -#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__APPLE__) -# include <sys/types.h> -# include <sys/event.h> -# include <sys/time.h> - -# if !defined(SOCKET_DEFAULT_BACKEND) -# define SOCKET_DEFAULT_BACKEND Kqueue -# endif -#else -# if !defined(SOCKET_DEFAULT_BACKEND) -# define SOCKET_DEFAULT_BACKEND Select -# endif -#endif - -#if defined(SOCKET_HAVE_POLL) && !defined(_WIN32) -# include <poll.h> -#endif - -/* - * Headers to include - * ------------------------------------------------------------------ - */ - -#if defined(_WIN32) -# include <cstdlib> - -# include <WinSock2.h> -# include <WS2tcpip.h> -#else -# include <cerrno> - -# include <sys/ioctl.h> -# include <sys/types.h> -# include <sys/socket.h> -# include <sys/un.h> - -# include <arpa/inet.h> - -# include <netinet/in.h> -# include <netinet/tcp.h> - -# include <fcntl.h> -# include <netdb.h> -# include <unistd.h> -#endif - -#if !defined(SOCKET_NO_SSL) -# include <openssl/err.h> -# include <openssl/evp.h> -# include <openssl/ssl.h> -#endif - -#include <cassert> -#include <chrono> -#include <cstdlib> -#include <cstring> -#include <exception> -#include <functional> -#include <map> -#include <memory> -#include <string> -#include <vector> - -namespace irccd { - -/** - * General network namespace. - */ -namespace net { - -/* - * Portables types - * ------------------------------------------------------------------ - * - * The following types are defined differently between Unix and Windows. - */ - -/* {{{ Protocols */ - -#if defined(_WIN32) - -/** - * Socket type, SOCKET. - */ -using Handle = SOCKET; - -/** - * Argument to pass to set. - */ -using ConstArg = const char *; - -/** - * Argument to pass to get. - */ -using Arg = char *; - -#else - -/** - * Socket type, int. - */ -using Handle = int; - -/** - * Argument to pass to set. - */ -using ConstArg = const void *; - -/** - * Argument to pass to get. - */ -using Arg = void *; - -#endif - -/* }}} */ - -/* - * Portable constants - * ------------------------------------------------------------------ - * - * These constants are needed to check functions return codes, they are rarely needed in end user code. - */ - -/* {{{ Constants */ - -/* - * The following constants are defined differently from Unix - * to Windows. - */ -#if defined(_WIN32) - -/** - * Socket creation failure or invalidation. - */ -extern const Handle Invalid; - -/** - * Socket operation failure. - */ -extern const int Failure; - -#else - -/** - * Socket creation failure or invalidation. - */ -extern const int Invalid; - -/** - * Socket operation failure. - */ -extern const int Failure; - -#endif - -#if !defined(SOCKET_NO_SSL) - -namespace ssl { - -/** - * @enum Method - * @brief Which OpenSSL method to use. - */ -enum Method { - Tlsv1, //!< TLS v1.2 (recommended) - Sslv3 //!< SSLv3 -}; - -} // !ssl - -#endif - -/* }}} */ - -/* - * Portable functions - * ------------------------------------------------------------------ - * - * The following free functions can be used to initialize the library or to get the last system error. - */ - -/* {{{ Functions */ - -/** - * Initialize the socket library. Except if you defined SOCKET_NO_AUTO_INIT, you don't need to call this - * function manually. - */ -void init() noexcept; - -/** - * Close the socket library. - */ -void finish() noexcept; - -#if !defined(SOCKET_NO_SSL) - -/** - * OpenSSL namespace. - */ -namespace ssl { - -/** - * Initialize the OpenSSL library. Except if you defined SOCKET_NO_AUTO_SSL_INIT, you don't need to call this function - * manually. - */ -void init() noexcept; - -/** - * Close the OpenSSL library. - */ -void finish() noexcept; - -} // !ssl - -#endif // SOCKET_NO_SSL - -/** - * Get the last socket system error. The error is set from errno or from - * WSAGetLastError on Windows. - * - * @return a string message - */ -std::string error(); - -/** - * Get the last system error. - * - * @param errn the error number (errno or WSAGetLastError) - * @return the error - */ -std::string error(int errn); - -/* }}} */ - -/* - * Error class - * ------------------------------------------------------------------ - * - * This is the main exception thrown on socket operations. - */ - -/* {{{ Error */ - -/** - * @class Error - * @brief Base class for sockets error - */ -class Error : public std::exception { -public: - /** - * @enum Code - * @brief Which kind of error - */ - enum Code { - Timeout, ///!< The action did timeout - System, ///!< There is a system error - Other ///!< Other custom error - }; - -private: - Code m_code; - std::string m_function; - std::string m_error; - -public: - /** - * Constructor that use the last system error. - * - * @param code which kind of error - * @param function the function name - */ - Error(Code code, std::string function); - - /** - * Constructor that use the system error set by the user. - * - * @param code which kind of error - * @param function the function name - * @param error the error - */ - Error(Code code, std::string function, int error); - - /** - * Constructor that set the error specified by the user. - * - * @param code which kind of error - * @param function the function name - * @param error the error - */ - Error(Code code, std::string function, std::string error); - - /** - * Get which function has triggered the error. - * - * @return the function name (e.g connect) - */ - inline const std::string &function() const noexcept - { - return m_function; - } - - /** - * The error code. - * - * @return the code - */ - inline Code code() const noexcept - { - return m_code; - } - - /** - * Get the error (only the error content). - * - * @return the error - */ - const char *what() const noexcept - { - return m_error.c_str(); - } -}; - -/* }}} */ - -/* - * State class - * ------------------------------------------------------------------ - * - * To facilitate higher-level stuff, the socket has a state. - */ - -/* {{{ State */ - -/** - * @enum State - * @brief Current socket state. - */ -enum class State { - Open, //!< Socket is open - Bound, //!< Socket is bound to an address - Connecting, //!< The connection is in progress - Connected, //!< Connection is complete - Accepted, //!< Socket has been accepted (client) - Accepting, //!< The client acceptation is in progress - Closed, //!< The socket has been closed - Disconnected, //!< The connection was lost -}; - -/* }}} */ - -/* - * Action enum - * ------------------------------------------------------------------ - * - * Defines the pending operation. - */ - -/* {{{ Action */ - -/** - * @enum Action - * @brief Define the current operation that must complete. - * - * Some operations like accept, connect, recv or send must sometimes do several round-trips to complete and the socket - * action is set with that enumeration. The user is responsible of calling accept, connect send or recv until the - * operation is complete. - * - * Note: the user must wait for the appropriate condition in Socket::condition to check if the required condition is - * met. - * - * It is important to complete the operation in the correct order because protocols like Tls may require to continue - * re-negociating when calling Socket::send or Socket::Recv. - */ -enum class Action { - None, //!< No action is required, socket is ready - Accept, //!< The socket is not yet accepted, caller must call accept() again - Connect, //!< The socket is not yet connected, caller must call connect() again - Receive, //!< The received operation has not succeeded yet, caller must call recv() or recvfrom() again - Send //!< The send operation has not succeded yet, caller must call send() or sendto() again -}; - -/* }}} */ - -/* - * Condition enum - * ------------------------------------------------------------------ - * - * Defines if we must wait for reading or writing. - */ - -/* {{{ Condition */ - -/** - * @enum Condition - * @brief Define the required condition for the socket. - * - * As explained in Action enumeration, some operations required to be called several times, before calling these - * operations, the user must wait the socket to be readable or writable. This can be checked with Socket::condition. - */ -enum class Condition { - None, //!< No condition is required - Readable = (1 << 0), //!< The socket must be readable - Writable = (1 << 1) //!< The socket must be writable -}; - -/** - * Apply bitwise XOR. - * - * @param v1 the first value - * @param v2 the second value - * @return the new value - */ -constexpr Condition operator^(Condition v1, Condition v2) noexcept -{ - return static_cast<Condition>(static_cast<int>(v1) ^ static_cast<int>(v2)); -} - -/** - * Apply bitwise AND. - * - * @param v1 the first value - * @param v2 the second value - * @return the new value - */ -constexpr Condition operator&(Condition v1, Condition v2) noexcept -{ - return static_cast<Condition>(static_cast<int>(v1) & static_cast<int>(v2)); -} - -/** - * Apply bitwise OR. - * - * @param v1 the first value - * @param v2 the second value - * @return the new value - */ -constexpr Condition operator|(Condition v1, Condition v2) noexcept -{ - return static_cast<Condition>(static_cast<int>(v1) | static_cast<int>(v2)); -} - -/** - * Apply bitwise NOT. - * - * @param v the value - * @return the complement - */ -constexpr Condition operator~(Condition v) noexcept -{ - return static_cast<Condition>(~static_cast<int>(v)); -} - -/** - * Assign bitwise OR. - * - * @param v1 the first value - * @param v2 the second value - * @return the new value - */ -inline Condition &operator|=(Condition &v1, Condition v2) noexcept -{ - v1 = static_cast<Condition>(static_cast<int>(v1) | static_cast<int>(v2)); - - return v1; -} - -/** - * Assign bitwise AND. - * - * @param v1 the first value - * @param v2 the second value - * @return the new value - */ -inline Condition &operator&=(Condition &v1, Condition v2) noexcept -{ - v1 = static_cast<Condition>(static_cast<int>(v1) & static_cast<int>(v2)); - - return v1; -} - -/** - * Assign bitwise XOR. - * - * @param v1 the first value - * @param v2 the second value - * @return the new value - */ -inline Condition &operator^=(Condition &v1, Condition v2) noexcept -{ - v1 = static_cast<Condition>(static_cast<int>(v1) ^ static_cast<int>(v2)); - - return v1; -} - -/* }}} */ - -/* - * Base Socket class - * ------------------------------------------------------------------ - * - * This base class has operations that are common to all types of sockets but you usually instanciate - * a SocketTcp or SocketUdp - */ - -/* {{{ Socket */ - -/** - * @class Socket - * @brief Base socket class for socket operations. - * - * **Important:** When using non-blocking sockets, some considerations must be taken. See the implementation of the - * underlying protocol for more details. - * - * @see protocol::Tls - * @see protocol::Tcp - * @see protocol::Udp - */ -template <typename Address, typename Protocol> -class Socket { -private: - Protocol m_proto; - State m_state{State::Closed}; - Action m_action{Action::None}; - Condition m_condition{Condition::None}; - -protected: - /** - * The native handle. - */ - Handle m_handle{Invalid}; - -public: - /** - * Create a socket handle. - * - * This is the primary function and the only one that creates the socket handle, all other constructors - * are just overloaded functions. - * - * @param domain the domain AF_* - * @param type the type SOCK_* - * @param protocol the protocol - * @param iface the implementation - * @throw net::Error on errors - * @post state is set to Open - * @post handle is not set to Invalid - */ - Socket(int domain, int type, int protocol, Protocol iface = {}) - : m_proto(std::move(iface)) - { -#if !defined(SOCKET_NO_AUTO_INIT) - init(); -#endif - m_handle = ::socket(domain, type, protocol); - - if (m_handle == Invalid) { - throw Error{Error::System, "socket"}; - } - - m_proto.create(*this); - m_state = State::Open; - - assert(m_handle != Invalid); - } - - /** - * This tries to create a socket. - * - * Domain and type are determined by the Address and Protocol object. - * - * @param protocol the protocol - * @param address which type of address - * @throw net::Error on errors - */ - explicit inline Socket(Protocol protocol = {}, const Address &address = {}) - : Socket{address.domain(), protocol.type(), 0, std::move(protocol)} - { - } - - /** - * Construct a socket with an already created descriptor. - * - * @param handle the native descriptor - * @param state specify the socket state - * @param protocol the type of socket implementation - * @post action is set to None - * @post condition is set to None - */ - explicit inline Socket(Handle handle, State state = State::Closed, Protocol protocol = {}) noexcept - : m_proto(std::move(protocol)) - , m_state{state} - , m_handle{handle} - { - assert(m_action == Action::None); - assert(m_condition == Condition::None); - } - - /** - * Create an invalid socket. Can be used when you cannot instanciate the socket immediately. - */ - explicit inline Socket(std::nullptr_t) noexcept - : m_handle{Invalid} - { - } - - /** - * Copy constructor deleted. - */ - Socket(const Socket &) = delete; - - /** - * Transfer ownership from other to this. - * - * @param other the other socket - */ - inline Socket(Socket &&other) noexcept - : m_proto(std::move(other.m_proto)) - , m_state{other.m_state} - , m_action{other.m_action} - , m_condition{other.m_condition} - , m_handle{other.m_handle} - { - /* Invalidate other */ - other.m_handle = Invalid; - other.m_state = State::Closed; - other.m_action = Action::None; - other.m_condition = Condition::None; - } - - /** - * Default destructor. - */ - virtual ~Socket() - { - close(); - } - - /** - * Access the implementation. - * - * @return the implementation - * @warning use this function with care - */ - inline const Protocol &protocol() const noexcept - { - return m_proto; - } - - /** - * Overloaded function. - * - * @return the implementation - */ - inline Protocol &protocol() noexcept - { - return m_proto; - } - - /** - * Get the current socket state. - * - * @return the state - */ - inline State state() const noexcept - { - return m_state; - } - - /** - * Change the current socket state. - * - * @param state the new state - * @warning only implementations should call this function - */ - inline void setState(State state) noexcept - { - m_state = state; - } - - /** - * Get the pending operation. - * - * @return the action to complete before continuing - * @note usually only needed in non-blocking sockets - */ - inline Action action() const noexcept - { - return m_action; - } - - /** - * Change the pending operation. - * - * @param action the action - * @warning you should not call this function yourself - */ - inline void setAction(Action action) noexcept - { - m_action = action; - } - - /** - * Get the condition to wait for. - * - * @return the condition - */ - inline Condition condition() const noexcept - { - return m_condition; - } - - /** - * Change the condition required. - * - * @param condition the condition - * @warning you should not call this function yourself - */ - inline void setCondition(Condition condition) noexcept - { - m_condition = condition; - } - - /** - * Set an option for the socket. Wrapper of setsockopt(2). - * - * @param level the setting level - * @param name the name - * @param arg the value - * @throw net::Error on errors - */ - template <typename Argument> - void set(int level, int name, const Argument &arg) - { - if (setsockopt(m_handle, level, name, (ConstArg)&arg, sizeof (arg)) == Failure) { - throw Error{Error::System, "set"}; - } - } - - /** - * Object-oriented option setter. - * - * The object must have `set(Socket<Address, Protocol> &) const`. - * - * @param option the option - * @throw net::Error on errors - */ - template <typename Option> - inline void set(const Option &option) - { - option.set(*this); - } - - /** - * Get an option for the socket. Wrapper of getsockopt(2). - * - * @param level the setting level - * @param name the name - * @throw net::Error on errors - */ - template <typename Argument> - Argument get(int level, int name) - { - Argument desired, result{}; - socklen_t size = sizeof (result); - - if (getsockopt(m_handle, level, name, (Arg)&desired, &size) == Failure) { - throw Error{Error::System, "get"}; - } - - std::memcpy(&result, &desired, size); - - return result; - } - - /** - * Object-oriented option getter. - * - * The object must have `T get(Socket<Address, Protocol> &) const`, T can be any type and it is the value - * returned from this function. - * - * @return the same value as get() in the option - * @throw net::Error on errors - */ - template <typename Option> - inline auto get() -> decltype(std::declval<Option>().get(*this)) - { - return Option{}.get(*this); - } - - /** - * Get the native handle. - * - * @return the handle - * @warning Not portable - */ - inline Handle handle() const noexcept - { - return m_handle; - } - - /** - * Bind using a native address. - * - * @param address the address - * @param length the size - * @pre state must not be Bound - * @throw net::Error on errors - */ - void bind(const sockaddr *address, socklen_t length) - { - assert(m_state != State::Bound); - - if (::bind(m_handle, address, length) == Failure) { - throw Error{Error::System, "bind"}; - } - - m_state = State::Bound; - } - - /** - * Overload that takes an address. - * - * @param address the address - * @throw net::Error on errors - */ - inline void bind(const Address &address) - { - bind(address.address(), address.length()); - } - - /** - * Listen for pending connection. - * - * @param max the maximum number - * @pre state must be Bound - * @throw net::Error on errors - */ - inline void listen(int max = 128) - { - assert(m_state == State::Bound); - - if (::listen(this->m_handle, max) == Failure) { - throw Error{Error::System, "listen"}; - } - } - - /** - * Connect to the address. - * - * If connection cannot be established immediately, connect with no argument must be called again. See - * the underlying protocol for more information. - * - * @pre state must be State::Open - * @param address the address - * @param length the the address length - * @throw net::Error on errors - * @post state is set to State::Connecting or State::Connected - * @note For non-blocking sockets, see the underlying protocol function for more details - */ - void connect(const sockaddr *address, socklen_t length) - { - assert(m_state == State::Open); - - m_action = Action::None; - m_condition = Condition::None; - - m_proto.connect(*this, address, length); - - assert((m_state == State::Connected && m_action == Action::None && m_condition == Condition::None) || - (m_state == State::Connecting && m_action == Action::Connect && m_condition != Condition::None)); - } - - /** - * Overloaded function. - * - * Effectively call connect(address.address(), address.length()); - * - * @param address the address - */ - inline void connect(const Address &address) - { - connect(address.address(), address.length()); - } - - /** - * Continue the connection, only required with non-blocking sockets. - * - * @pre state must be State::Connecting - * @throw net::Error on errors - */ - void connect() - { - assert(m_state == State::Connecting); - - m_action = Action::None; - m_condition = Condition::None; - - m_proto.connect(*this); - - assert((m_state == State::Connected && m_action == Action::None && m_condition == Condition::None) || - (m_state == State::Connecting && m_action == Action::Connect && m_condition != Condition::None)); - } - - /** - * Accept a new client. If there are no pending connection, throws an error. - * - * If the client cannot be accepted immediately, the client is returned and accept with no arguments - * must be called on it. See the underlying protocol for more information. - * - * @pre state must be State::Bound - * @param info the address where to store client's information (optional) - * @return the new socket - * @throw Error on errors - * @post returned client's state is set to State::Accepting or State::Accepted - * @note For non-blocking sockets, see the underlying protocol function for more details - */ - Socket<Address, Protocol> accept(Address *info) - { - assert(m_state == State::Bound); - - m_action = Action::None; - m_condition = Condition::None; - - sockaddr_storage storage; - socklen_t length = sizeof (storage); - - Socket<Address, Protocol> sc = m_proto.accept(*this, reinterpret_cast<sockaddr *>(&storage), &length); - - if (info) { - *info = Address{&storage, length}; - } - - /* Master do not change */ - assert(m_state == State::Bound); - assert(m_action == Action::None); - assert(m_condition == Condition::None); - - /* Client */ - assert( - (sc.state() == State::Accepting && sc.action() == Action::Accept && sc.condition() != Condition::None) || - (sc.state() == State::Accepted && sc.action() == Action::None && sc.condition() == Condition::None) - ); - - return sc; - } - - /** - * Continue the accept process on this client. This function must be called only when the socket is - * ready to be readable or writable! (see condition). - * - * @pre state must be State::Accepting - * @throw Error on errors - * @post if connection is complete, state is changed to State::Accepted, action and condition are unset - * @post if connection is still in progress, condition is set - */ - void accept() - { - assert(m_state == State::Accepting); - - m_action = Action::None; - m_condition = Condition::None; - - m_proto.accept(*this); - - assert( - (m_state == State::Accepting && m_action == Action::Accept && m_condition != Condition::None) || - (m_state == State::Accepted && m_action == Action::None && m_condition == Condition::None) - ); - } - - /** - * Get the local name. This is a wrapper of getsockname(). - * - * @return the address - * @throw Error on failures - * @pre state() must not be State::Closed - */ - Address address() const - { - assert(m_state != State::Closed); - - sockaddr_storage ss; - socklen_t length = sizeof (sockaddr_storage); - - if (::getsockname(m_handle, (sockaddr *)&ss, &length) == Failure) { - throw Error{Error::System, "getsockname"}; - } - - return Address(&ss, length); - } - - /** - * Receive some data. - * - * If the operation cannot be complete immediately, 0 is returned and user must call the function - * again when ready. See the underlying protocol for more information. - * - * If action is set to Action::None and result is set to 0, disconnection occured. - * - * @param data the destination buffer - * @param length the buffer length - * @pre action must not be Action::Send - * @return the number of bytes received or 0 - * @throw Error on error - * @note For non-blocking sockets, see the underlying protocol function for more details - */ - unsigned recv(void *data, unsigned length) - { - assert(m_action != Action::Send); - - m_action = Action::None; - m_condition = Condition::None; - - unsigned nbread = m_proto.recv(*this, data, length); - - return nbread; - } - - /** - * Overloaded function. - * - * @param count the number of bytes to receive - * @return the string - * @throw Error on error - */ - inline std::string recv(unsigned count) - { - std::string result; - - result.resize(count); - auto n = recv(const_cast<char *>(result.data()), count); - result.resize(n); - - return result; - } - - /** - * Send some data. - * - * If the operation cannot be complete immediately, 0 is returned and user must call the function - * again when ready. See the underlying protocol for more information. - * - * @param data the data buffer - * @param length the buffer length - * @return the number of bytes sent or 0 - * @pre action() must not be Flag::Receive - * @throw Error on error - * @note For non-blocking sockets, see the underlying protocol function for more details - */ - unsigned send(const void *data, unsigned length) - { - assert(m_action != Action::Receive); - - m_action = Action::None; - m_condition = Condition::None; - - unsigned nbsent = m_proto.send(*this, data, length); - - assert((m_action == Action::None && m_condition == Condition::None) || - (m_action == Action::Send && m_condition != Condition::None)); - - return nbsent; - } - - /** - * Overloaded function. - * - * @param data the string to send - * @return the number of bytes sent - * @throw Error on error - */ - inline unsigned send(const std::string &data) - { - return send(data.c_str(), data.size()); - } - - /** - * Send data to an end point. - * - * If the operation cannot be complete immediately, 0 is returned and user must call the function - * again when ready. See the underlying protocol for more information. - * - * @param data the buffer - * @param length the buffer length - * @param address the client address - * @param addrlen the address length - * @return the number of bytes sent - * @throw net::Error on errors - * @note For non-blocking sockets, see the underlying protocol function for more details - */ - inline unsigned sendto(const void *data, unsigned length, const sockaddr *address, socklen_t addrlen) - { - return m_proto.sendto(*this, data, length, address, addrlen); - } - - /** - * Overloaded function. - * - * @param data the buffer - * @param length the buffer length - * @param address the destination - * @return the number of bytes sent - * @throw net::Error on errors - */ - inline unsigned sendto(const void *data, unsigned length, const Address &address) - { - return sendto(data, length, address.address(), address.length()); - } - - /** - * Overloaded function. - * - * @param data the data - * @param address the address - * @return the number of bytes sent - * @throw net:;Error on errors - */ - inline unsigned sendto(const std::string &data, const Address &address) - { - return sendto(data.c_str(), data.length(), address); - } - - /** - * Receive data from an end point. - * - * If the operation cannot be complete immediately, 0 is returned and user must call the function - * again when ready. See the underlying protocol for more information. - * - * @param data the destination buffer - * @param length the buffer length - * @param address the address destination - * @param addrlen the address length (in/out) - * @return the number of bytes received - * @throw net::Error on errors - * @note For non-blocking sockets, see the underlying protocol function for more details - */ - inline unsigned recvfrom(void *data, unsigned length, sockaddr *address, socklen_t *addrlen) - { - return m_proto.recvfrom(*this, data, length, address, addrlen); - } - - /** - * Overloaded function. - * - * @param data the destination buffer - * @param length the buffer length - * @param info the address destination - * @return the number of bytes received - * @throw net::Error on errors - */ - inline unsigned recvfrom(void *data, unsigned length, Address *info = nullptr) - { - sockaddr_storage storage; - socklen_t addrlen = sizeof (sockaddr_storage); - - auto n = recvfrom(data, length, reinterpret_cast<sockaddr *>(&storage), &addrlen); - - if (info && n != 0) { - *info = Address{&storage, addrlen}; - } - - return n; - } - - /** - * Overloaded function. - * - * @param count the maximum number of bytes to receive - * @param info the client information - * @return the string - * @throw net::Error on errors - */ - std::string recvfrom(unsigned count, Address *info = nullptr) - { - std::string result; - - result.resize(count); - auto n = recvfrom(const_cast<char *>(result.data()), count, info); - result.resize(n); - - return result; - } - - /** - * Close the socket. - * - * Automatically called from the destructor. - */ - void close() - { - if (m_handle != Invalid) { -#if defined(_WIN32) - ::closesocket(m_handle); -#else - ::close(m_handle); -#endif - m_handle = Invalid; - } - - m_state = State::Closed; - m_action = Action::None; - m_condition = Condition::None; - } - - /** - * Assignment operator forbidden. - * - * @return *this - */ - Socket &operator=(const Socket &) = delete; - - /** - * Transfer ownership from other to this. The other socket is left - * invalid and will not be closed. - * - * @param other the other socket - * @return this - */ - Socket &operator=(Socket &&other) noexcept - { - m_handle = other.m_handle; - m_proto = std::move(other.m_proto); - m_state = other.m_state; - m_action = other.m_action; - m_condition = other.m_condition; - - /* Invalidate other */ - other.m_handle = Invalid; - other.m_state = State::Closed; - other.m_action = Action::None; - other.m_condition = Condition::None; - - return *this; - } -}; - -/** - * Compare two sockets. - * - * @param s1 the first socket - * @param s2 the second socket - * @return true if they equals - */ -template <typename Address, typename Protocol> -bool operator==(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2) -{ - return s1.handle() == s2.handle(); -} - -/** - * Compare two sockets. - * - * @param s1 the first socket - * @param s2 the second socket - * @return true if they are different - */ -template <typename Address, typename Protocol> -bool operator!=(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2) -{ - return s1.handle() != s2.handle(); -} - -/** - * Compare two sockets. - * - * @param s1 the first socket - * @param s2 the second socket - * @return true if s1 < s2 - */ -template <typename Address, typename Protocol> -bool operator<(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2) -{ - return s1.handle() < s2.handle(); -} - -/** - * Compare two sockets. - * - * @param s1 the first socket - * @param s2 the second socket - * @return true if s1 > s2 - */ -template <typename Address, typename Protocol> -bool operator>(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2) -{ - return s1.handle() > s2.handle(); -} - -/** - * Compare two sockets. - * - * @param s1 the first socket - * @param s2 the second socket - * @return true if s1 <= s2 - */ -template <typename Address, typename Protocol> -bool operator<=(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2) -{ - return s1.handle() <= s2.handle(); -} - -/** - * Compare two sockets. - * - * @param s1 the first socket - * @param s2 the second socket - * @return true if s1 >= s2 - */ -template <typename Address, typename Protocol> -bool operator>=(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2) -{ - return s1.handle() >= s2.handle(); -} - -/* }}} */ - -/* - * Predefined options - * ------------------------------------------------------------------ - */ - -/* {{{ Options */ - -/** - * Namespace of predefined options. - */ -namespace option { - -/* - * Options for socket - * ------------------------------------------------------------------ - */ - -/* {{{ Options for socket */ - -/** - * @class SockBlockMode - * @brief Set or get the blocking-mode for a socket. - * @warning On Windows, it's not possible to check if the socket is blocking or not. - */ -class SockBlockMode { -public: - /** - * Set to false if you want non-blocking socket. - */ - bool value{false}; - - /** - * Set the option. - * - * @param sc the socket - * @throw Error on errors - */ - template <typename Address, typename Protocol> - void set(Socket<Address, Protocol> &sc) const - { -#if defined(O_NONBLOCK) && !defined(_WIN32) - int flags; - - if ((flags = fcntl(sc.handle(), F_GETFL, 0)) < 0) { - flags = 0; - } - - if (value) { - flags &= ~(O_NONBLOCK); - } else { - flags |= O_NONBLOCK; - } - - if (fcntl(sc.handle(), F_SETFL, flags) < 0) { - throw Error{Error::System, "fcntl"}; - } -#else - unsigned long flags = (value) ? 0 : 1; - - if (ioctlsocket(sc.handle(), FIONBIO, &flags) == Failure) { - throw Error{Error::System, "fcntl"}; - } -#endif - } - - /** - * Get the option. - * - * @return the value - * @throw Error on errors - */ - template <typename Address, typename Protocol> - bool get(Socket<Address, Protocol> &sc) const - { -#if defined(O_NONBLOCK) && !defined(_WIN32) - int flags = fcntl(sc.handle(), F_GETFL, 0); - - if (flags < 0) { - throw Error{Error::System, "fcntl"}; - } - - return !(flags & O_NONBLOCK); -#else - throw Error{Error::Other, "get", "Windows API cannot let you get the blocking status of a socket"}; -#endif - } -}; - -/** - * @class SockReuseAddress - * @brief Reuse address, must be used before calling Socket::bind - */ -class SockReuseAddress { -public: - /** - * Set to true if you want to set the SOL_SOCKET/SO_REUSEADDR option. - */ - bool value{true}; - - /** - * Set the option. - * - * @param sc the socket - * @throw Error on errors - */ - template <typename Address, typename Protocol> - inline void set(Socket<Address, Protocol> &sc) const - { - sc.set(SOL_SOCKET, SO_REUSEADDR, value ? 1 : 0); - } - - /** - * Get the option. - * - * @return the value - * @throw Error on errors - */ - template <typename Address, typename Protocol> - inline bool get(Socket<Address, Protocol> &sc) const - { - return static_cast<bool>(sc.template get<int>(SOL_SOCKET, SO_REUSEADDR)); - } -}; - -/** - * @class SockSendBuffer - * @brief Set or get the output buffer. - */ -class SockSendBuffer { -public: - /** - * Set to the buffer size. - */ - int value{2048}; - - /** - * Set the option. - * - * @param sc the socket - * @throw Error on errors - */ - template <typename Address, typename Protocol> - inline void set(Socket<Address, Protocol> &sc) const - { - sc.set(SOL_SOCKET, SO_SNDBUF, value); - } - - /** - * Get the option. - * - * @return the value - * @throw Error on errors - */ - template <typename Address, typename Protocol> - inline int get(Socket<Address, Protocol> &sc) const - { - return sc.template get<int>(SOL_SOCKET, SO_SNDBUF); - } -}; - -/** - * @class SockReceiveBuffer - * @brief Set or get the input buffer. - */ -class SockReceiveBuffer { -public: - /** - * Set to the buffer size. - */ - int value{2048}; - - /** - * Set the option. - * - * @param sc the socket - * @throw Error on errors - */ - template <typename Address, typename Protocol> - inline void set(Socket<Address, Protocol> &sc) const - { - sc.set(SOL_SOCKET, SO_RCVBUF, value); - } - - /** - * Get the option. - * - * @return the value - * @throw Error on errors - */ - template <typename Address, typename Protocol> - inline int get(Socket<Address, Protocol> &sc) const - { - return sc.template get<int>(SOL_SOCKET, SO_RCVBUF); - } -}; - -/* }}} */ - -/** - * @class TcpNoDelay - * @brief Set this option if you want to disable nagle's algorithm. - */ -class TcpNoDelay { -public: - /** - * Set to true to set TCP_NODELAY option. - */ - bool value{true}; - - /** - * Set the option. - * - * @param sc the socket - * @throw Error on errors - */ - template <typename Address, typename Protocol> - inline void set(Socket<Address, Protocol> &sc) const - { - sc.set(IPPROTO_TCP, TCP_NODELAY, value ? 1 : 0); - } - - /** - * Get the option. - * - * @return the value - * @throw Error on errors - */ - template <typename Address, typename Protocol> - inline bool get(Socket<Address, Protocol> &sc) const - { - return static_cast<bool>(sc.template get<int>(IPPROTO_TCP, TCP_NODELAY)); - } -}; - -/** - * @class Ipv6Only - * @brief Control IPPROTO_IPV6/IPV6_V6ONLY - * - * Note: some systems may or not set this option by default so it's a good idea to set it in any case to either - * false or true if portability is a concern. - */ -class Ipv6Only { -public: - /** - * Set this to use only IPv6. - */ - bool value{true}; - - /** - * Set the option. - * - * @param sc the socket - * @throw Error on errors - */ - template <typename Address, typename Protocol> - inline void set(Socket<Address, Protocol> &sc) const - { - sc.set(IPPROTO_IPV6, IPV6_V6ONLY, value ? 1 : 0); - } - - /** - * Get the option. - * - * @return the value - * @throw Error on errors - */ - template <typename Address, typename Protocol> - inline bool get(Socket<Address, Protocol> &sc) const - { - return static_cast<bool>(sc.template get<int>(IPPROTO_IPV6, IPV6_V6ONLY)); - } -}; - -} // !option - -/* }}} */ - -/* - * Predefined addressed to be used - * ------------------------------------------------------------------ - * - * - Ip, - * - Local. - */ - -/* {{{ Addresses */ - -/** - * Set of predefined addresses. - */ -namespace address { - -/** - * @class Ip - * @brief Base class for IPv6 and IPv4, you can use it if you don't know in advance if you'll use IPv6 or IPv4. - */ -class Ip { -public: - /** - * @enum Type - * @brief Type of ip address. - */ - enum Type { - v4 = AF_INET, //!< AF_INET - v6 = AF_INET6 //!< AF_INET6 - }; - -private: - /* - * Default domain when using default constructors. - * - * Note: AF_INET or AF_INET6, not - */ - static int m_default; - - union { - sockaddr_in m_sin; - sockaddr_in6 m_sin6; - }; - - socklen_t m_length{0}; - int m_domain{AF_INET}; - -public: - /** - * Set the default domain to use when using default Ip constructor. By default, AF_INET is used. - * - * @pre domain must be Type::v4 or Type::v6 - */ - static inline void setDefault(Type domain) noexcept - { - assert(domain == Type::v4 || domain == Type::v6); - - m_default = static_cast<int>(domain); - } - - /** - * Construct using the default domain. - */ - inline Ip() noexcept - : Ip(static_cast<Type>(m_default)) - { - } - - /** - * Default initialize the Ip domain. - * - * @pre domain must be AF_INET or AF_INET6 only - * @param domain the domain (AF_INET or AF_INET6) - */ - Ip(Type domain) noexcept; - - /** - * Construct an address suitable for bind() or connect(). - * - * @pre domain must be Type::v4 or Type::v6 - * @param domain the domain (AF_INET or AF_INET6) - * @param host the host (* for any) - * @param port the port number - * @throw Error on errors - */ - Ip(const std::string &host, int port, Type domain = v4); - - /** - * Construct an address from a storage. - * - * @pre storage's domain must be AF_INET or AF_INET6 only - * @param ss the storage - * @param length the length - */ - Ip(const sockaddr_storage *ss, socklen_t length) noexcept; - - /** - * Get the domain (AF_INET or AF_INET6). - * - * @return the domain - */ - inline int domain() const noexcept - { - return m_domain; - } - - /** - * Return the underlying address, either sockaddr_in6 or sockaddr_in. - * - * @return the address - */ - inline const sockaddr *address() const noexcept - { - if (m_domain == AF_INET6) { - return reinterpret_cast<const sockaddr *>(&m_sin6); - } - - return reinterpret_cast<const sockaddr *>(&m_sin); - } - - /** - * Return the underlying address length. - * - * @return the length - */ - inline socklen_t length() const noexcept - { - return m_length; - } - - /** - * Get the port. - * - * @return the port - */ - inline int port() const noexcept - { - if (m_domain == AF_INET6) { - return ntohs(m_sin6.sin6_port); - } - - return ntohs(m_sin.sin_port); - } -}; - -#if !defined(_WIN32) - -/** - * @class Local - * @brief unix family sockets - * - * Create an address to a specific path. Only available on Unix. - */ -class Local { -private: - sockaddr_un m_sun; - std::string m_path; - -public: - /** - * Get the domain AF_LOCAL. - * - * @return AF_LOCAL - */ - inline int domain() const noexcept - { - return AF_LOCAL; - } - - /** - * Default constructor. - */ - Local() noexcept; - - /** - * Construct an address to a path. - * - * @param path the path - * @param rm remove the file before (default: false) - */ - Local(std::string path, bool rm = false) noexcept; - - /** - * Construct an unix address from a storage address. - * - * @pre storage's domain must be AF_LOCAL - * @param ss the storage - * @param length the length - */ - Local(const sockaddr_storage *ss, socklen_t length) noexcept; - - /** - * Get the sockaddr_un. - * - * @return the address - */ - inline const sockaddr *address() const noexcept - { - return reinterpret_cast<const sockaddr *>(&m_sun); - } - - /** - * Get the address length. - * - * @return the length - */ - inline socklen_t length() const noexcept - { -#if defined(SOCKET_HAVE_SUN_LEN) - return SUN_LEN(&m_sun); -#else - return sizeof (m_sun); -#endif - } -}; - -#endif // !_WIN32 - -} // !address - -/* }}} */ - -/* - * Predefined protocols - * ------------------------------------------------------------------ - * - * - Tcp, for standard stream connections, - * - Udp, for standard datagram connections, - * - Tls, for secure stream connections. - */ - -/* {{{ Protocols */ - -/** - * Set of predefined protocols. - */ -namespace protocol { - -/* {{{ Tcp */ - -/** - * @class Tcp - * @brief Clear TCP implementation. - * - * This is the basic TCP protocol that implements recv, send, connect and accept as wrappers of the usual - * C functions. - */ -class Tcp { -public: - /** - * Socket type. - * - * @return SOCK_STREAM - */ - inline int type() const noexcept - { - return SOCK_STREAM; - } - - /** - * Do nothing. - * - * This function is just present for compatibility, it should never be called. - */ - template <typename Address> - inline void create(Socket<Address, Tcp> &) const noexcept - { - /* No-op */ - } - - /** - * Standard connect. - * - * If the socket is marked non-blocking and the connection cannot be established immediately, then the - * following is true: - * - * - state is set to State::Connecting, - * - action is set to Action::Connect, - * - condition is set to Condition::Writable. - * - * Then the user must wait until the socket is writable and call connect() with 0 arguments. - * - * If the socket is blocking, this function blocks until the connection is complete or an error occurs, in - * that case state is either set to State::Connected or State::Disconnected but action and condition are - * not set. - * - * @param sc the socket - * @param address the address - * @param length the length - * @throw net::Error on errors - * @note Wrapper of connect(2) - */ - template <typename Address, typename Protocol> - void connect(Socket<Address, Protocol> &sc, const sockaddr *address, socklen_t length) - { - if (::connect(sc.handle(), address, length) == Failure) { - /* - * Determine if the error comes from a non-blocking connect that cannot be - * accomplished yet. - */ -#if defined(_WIN32) - int error = WSAGetLastError(); - - if (error == WSAEWOULDBLOCK) { - sc.setState(State::Connecting); - sc.setAction(Action::Connect); - sc.setCondition(Condition::Writable); - } else { - sc.setState(State::Disconnected); - throw Error{Error::System, "connect", error}; - } -#else - if (errno == EINPROGRESS) { - sc.setState(State::Connecting); - sc.setAction(Action::Connect); - sc.setCondition(Condition::Writable); - } else { - sc.setState(State::Disconnected); - throw Error{Error::System, "connect"}; - } -#endif - } else { - sc.setState(State::Connected); - } - } - - /** - * Continue the connection. This function must only be called when the socket is ready for writing, - * the user is responsible of waiting for that condition. - * - * This function check for SOL_SOCKET/SO_ERROR status. - * - * If the connection is complete, status is set to State::Connected, otherwise it is set to - * State::Disconnected. In both cases, action and condition are not set. - * - * @param sc the socket - * @throw net::Error on errors - */ - template <typename Address, typename Protocol> - void connect(Socket<Address, Protocol> &sc) - { - int error = sc.template get<int>(SOL_SOCKET, SO_ERROR); - - if (error == Failure) { - sc.setState(State::Disconnected); - throw Error{Error::System, "connect", error}; - } - - sc.setState(State::Connected); - } - - /** - * Accept a clear client. - * - * If the socket is marked non-blocking and there are no pending connection, this function throws an - * error. The user must wait that the socket is readable before calling this function. - * - * If the socket is blocking, this function blocks until a new client is connected or throws an error on - * errors. - * - * If the socket is correctly returned, its state is set to State::Accepted and its action and condition - * are not set. - * - * In any case, action and condition of this socket are not set. - * - * @param sc the socket - * @param address the address destination - * @param length the address length - * @return the socket - * @throw net::Error on errors - * @note Wrapper of accept(2) - */ - template <typename Address, typename Protocol> - Socket<Address, Protocol> accept(Socket<Address, Protocol> &sc, sockaddr *address, socklen_t *length) - { - Handle handle = ::accept(sc.handle(), address, length); - - if (handle == Invalid) { - throw Error{Error::System, "accept"}; - } - - return Socket<Address, Protocol>{handle, State::Accepted}; - } - - /** - * Continue accept. - * - * This function is just present for compatibility, it should never be called. - */ - template <typename Address, typename Protocol> - inline void accept(Socket<Address, Protocol> &) const noexcept - { - /* no-op */ - } - - /** - * Receive data. - * - * If the socket is marked non-blocking and no data is available, 0 is returned and condition is set to - * Condition::Readable. If 0 is returned and condition is not set, then the state is set to - * State::Disconnected. - * - * If the socket is blocking, this function blocks until some data is available or if an error occurs. - * - * In any case, action is never set. - * - * @param sc the socket - * @param data the destination - * @param length the destination length - * @return the number of bytes read - * @throw Error on errors - * @note Wrapper of recv(2) - */ - template <typename Address> - unsigned recv(Socket<Address, Tcp> &sc, void *data, unsigned length) - { - int nbread = ::recv(sc.handle(), (Arg)data, length, 0); - - if (nbread == Failure) { -#if defined(_WIN32) - int error = WSAGetLastError(); - - if (error == WSAEWOULDBLOCK) { - nbread = 0; - sc.setCondition(Condition::Readable); - } else { - sc.setState(State::Disconnected); - throw Error{Error::System, "recv", error}; - } -#else - if (errno == EAGAIN || errno == EWOULDBLOCK) { - sc.setCondition(Condition::Readable); - } else { - sc.setState(State::Disconnected); - throw Error{Error::System, "recv"}; - } -#endif - } else if (nbread == 0) { - sc.setState(State::Disconnected); - } - - return static_cast<unsigned>(nbread); - } - - /** - * Send some data. - * - * If the socket is marked non-blocking and the operation would block, then 0 is returned and condition is set to - * Condition::Writable. - * - * If the socket is blocking, this function blocks until the data has been sent. - * - * On any other errors, this function throw net::Error. - * - * @param sc the socket - * @param data the buffer to send - * @param length the buffer length - * @return the number of bytes sent - * @throw net::Error on errors - * @note Wrapper of send(2) - */ - template <typename Address> - unsigned send(Socket<Address, Tcp> &sc, const void *data, unsigned length) - { - int nbsent = ::send(sc.handle(), (ConstArg)data, length, 0); - - if (nbsent == Failure) { -#if defined(_WIN32) - int error = WSAGetLastError(); - - if (error == WSAEWOULDBLOCK) { - nbsent = 0; - sc.setCondition(Condition::Writable); - } else { - sc.setState(State::Disconnected); - throw Error{Error::System, "send", error}; - } -#else - if (errno == EAGAIN || errno == EWOULDBLOCK) { - nbsent = 0; - sc.setCondition(Condition::Writable); - } else { - sc.setState(State::Disconnected); - throw Error{Error::System, "send"}; - } -#endif - } - - return static_cast<unsigned>(nbsent); - } -}; - -/* }}} */ - -/* {{{ Udp */ - -/** - * @class Udp - * @brief Clear UDP type. - * - * This class is the basic implementation of UDP sockets. - */ -class Udp { -public: - /** - * Socket type. - * - * @return SOCK_DGRAM - */ - inline int type() const noexcept - { - return SOCK_DGRAM; - } - - /** - * Do nothing. - */ - template <typename Address> - inline void create(Socket<Address, Udp> &) noexcept - { - /* No-op */ - } - - /** - * Receive data from an end point. - * - * If the socket is marked non-blocking and no data is available, 0 is returned and condition is set to - * Condition::Readable. - * - * If the socket is blocking, this functions blocks until some data is available or if an error occurs. - * - * @param sc the socket - * @param data the destination buffer - * @param length the buffer length - * @param address the address - * @param addrlen the initial address length - * @return the number of bytes received - * @throw Error on error - */ - template <typename Address> - unsigned recvfrom(Socket<Address, Udp> &sc, void *data, unsigned length, sockaddr *address, socklen_t *addrlen) - { - int nbread; - - nbread = ::recvfrom(sc.handle(), (Arg)data, length, 0, address, addrlen); - - if (nbread == Failure) { -#if defined(_WIN32) - int error = WSAGetLastError(); - - if (error == WSAEWOULDBLOCK) { - nbread = 0; - sc.setCondition(Condition::Readable); - } else { - throw Error{Error::System, "recvfrom"}; - } -#else - if (errno == EAGAIN || errno == EWOULDBLOCK) { - nbread = 0; - sc.setCondition(Condition::Readable); - } else { - throw Error{Error::System, "recvfrom"}; - } -#endif - } - - return static_cast<unsigned>(nbread); - } - - /** - * Send data to an end point. - * - * If the socket is marked non-blocking and the operation would block, then 0 is returned and condition is set to - * Condition::Writable. - * - * If the socket is blocking, this functions blocks until the data has been sent. - * - * @param sc the socket - * @param data the buffer - * @param length the buffer length - * @param address the client address - * @param addrlen the adderss length - * @return the number of bytes sent - * @throw Error on error - */ - template <typename Address> - unsigned sendto(Socket<Address, Udp> &sc, const void *data, unsigned length, const sockaddr *address, socklen_t addrlen) - { - int nbsent; - - nbsent = ::sendto(sc.handle(), (ConstArg)data, length, 0, address, addrlen); - if (nbsent == Failure) { -#if defined(_WIN32) - int error = WSAGetLastError(); - - if (error == WSAEWOULDBLOCK) { - nbsent = 0; - sc.setCondition(Condition::Writable); - } else { - throw Error{Error::System, "sendto", error}; - } -#else - if (errno == EAGAIN || errno == EWOULDBLOCK) { - nbsent = 0; - sc.setCondition(Condition::Writable); - } else { - throw Error{Error::System, "sendto"}; - } -#endif - } - - return static_cast<unsigned>(nbsent); - } -}; - -/* }}} */ - -/* {{{ Tls */ - -#if !defined(SOCKET_NO_SSL) - -/** - * @class Tls - * @brief OpenSSL secure layer for TCP. - * - * **Note:** This protocol is much more difficult to use with non-blocking sockets, if some operations would block, the - * user is responsible of calling the function again by waiting for the appropriate condition. See the functions for - * more details. - * - * @see Tls::accept - * @see Tls::connect - * @see Tls::recv - * @see Tls::send - */ -class Tls : private Tcp { -private: - using Context = std::shared_ptr<SSL_CTX>; - using Ssl = std::unique_ptr<SSL, void (*)(SSL *)>; - - /* OpenSSL objects */ - Context m_context; - Ssl m_ssl{nullptr, nullptr}; - - /* Status */ - bool m_tcpconnected{false}; - - /* - * User definable parameters - */ - ssl::Method m_method{ssl::Tlsv1}; - std::string m_key; - std::string m_certificate; - bool m_verify{false}; - - /* - * Construct with a context and ssl, for Tls::accept. - */ - Tls(Context context, Ssl ssl) - : m_context{std::move(context)} - , m_ssl{std::move(ssl)} - { - } - - /* - * Get the OpenSSL error message. - */ - inline std::string error(int error) - { - auto msg = ERR_reason_error_string(error); - - return msg == nullptr ? "" : msg; - } - - /* - * Update the states after an uncompleted operation. - */ - template <typename Address, typename Protocol> - inline void updateStates(Socket<Address, Protocol> &sc, State state, Action action, int code) - { - assert(code == SSL_ERROR_WANT_READ || code == SSL_ERROR_WANT_WRITE); - - sc.setState(state); - sc.setAction(action); - - if (code == SSL_ERROR_WANT_READ) { - sc.setCondition(Condition::Readable); - } else { - sc.setCondition(Condition::Writable); - } - } - - /* - * Continue the connect operation. - */ - template <typename Address, typename Protocol> - void processConnect(Socket<Address, Protocol> &sc) - { - int ret = SSL_connect(m_ssl.get()); - - if (ret <= 0) { - int no = SSL_get_error(m_ssl.get(), ret); - - if (no == SSL_ERROR_WANT_READ || no == SSL_ERROR_WANT_WRITE) { - updateStates(sc, State::Connecting, Action::Connect, no); - } else { - sc.setState(State::Disconnected); - throw Error{Error::System, "connect", error(no)}; - } - } else { - sc.setState(State::Connected); - } - } - - /* - * Continue accept. - */ - template <typename Address, typename Protocol> - void processAccept(Socket<Address, Protocol> &sc) - { - int ret = SSL_accept(m_ssl.get()); - - if (ret <= 0) { - int no = SSL_get_error(m_ssl.get(), ret); - - if (no == SSL_ERROR_WANT_READ || no == SSL_ERROR_WANT_WRITE) { - updateStates(sc, State::Accepting, Action::Accept, no); - } else { - sc.setState(State::Disconnected); - throw Error(Error::System, "accept", error(no)); - } - } else { - sc.setState(State::Accepted); - } - } - -public: - /** - * @copydoc Tcp::type - */ - inline int type() const noexcept - { - return SOCK_STREAM; - } - - /** - * Empty TLS constructor. - */ - Tls() - { -#if !defined(SOCKET_NO_SSL_AUTO_INIT) - net::ssl::init(); -#endif - } - - /** - * Set the method. - * - * @param method the method - * @pre the socket must not be already created - */ - inline void setMethod(ssl::Method method) noexcept - { - assert(!m_context); - assert(!m_ssl); - - m_method = method; - } - - /** - * Use the specified private key file. - * - * @param file the path to the private key - */ - inline void setPrivateKey(std::string file) noexcept - { - m_key = std::move(file); - } - - /** - * Use the specified certificate file. - * - * @param file the path to the file - */ - inline void setCertificate(std::string file) noexcept - { - m_certificate = std::move(file); - } - - /** - * Set to true if we must verify the certificate and private key. - * - * @param verify the mode - */ - inline void setVerify(bool verify = true) noexcept - { - m_verify = verify; - } - - /** - * Initialize the SSL objects after have created. - * - * @param sc the socket - * @throw net::Error on errors - */ - template <typename Address> - inline void create(Socket<Address, Tls> &sc) - { - auto method = (m_method == ssl::Tlsv1) ? TLSv1_method() : SSLv23_method(); - - m_context = {SSL_CTX_new(method), SSL_CTX_free}; - m_ssl = {SSL_new(m_context.get()), SSL_free}; - - SSL_set_fd(m_ssl.get(), sc.handle()); - - /* Load certificates */ - if (m_certificate.size() > 0) { - SSL_CTX_use_certificate_file(m_context.get(), m_certificate.c_str(), SSL_FILETYPE_PEM); - } - if (m_key.size() > 0) { - SSL_CTX_use_PrivateKey_file(m_context.get(), m_key.c_str(), SSL_FILETYPE_PEM); - } - if (m_verify && !SSL_CTX_check_private_key(m_context.get())) { - throw Error{Error::System, "(openssl)", "unable to verify key"}; - } - } - - /** - * Connect to a secure host. - * - * If the socket is marked non-blocking and the connection cannot be established yet, then the state is set - * to State::Connecting, the condition is set to Condition::Readable or Condition::Writable, the user must - * wait for the appropriate condition before calling the overload connect which takes 0 argument. - * - * If the socket is blocking, this functions blocks until the connection is complete. - * - * If the connection was completed correctly the state is set to State::Connected. - * - * @param sc the socket - * @param address the address - * @param length the address length - * @throw net::Error on errors - */ - template <typename Address, typename Protocol> - void connect(Socket<Address, Protocol> &sc, const sockaddr *address, socklen_t length) - { - /* 1. Connect using raw TCP */ - Tcp::connect(sc, address, length); - - /* 2. If the connection is complete (e.g. non-blocking), try handshake */ - if (sc.state() == State::Connected) { - m_tcpconnected = true; - processConnect(sc); - } - } - - /** - * Continue the connection. - * - * This function must be called when the socket is ready for reading or writing (check with Socket::condition), - * the state may change exactly like the initial connect call. - * - * @param sc the socket - * @throw net::Error on errors - */ - template <typename Address, typename Protocol> - void connect(Socket<Address, Protocol> &sc) - { - /* 1. Be sure to complete standard connect before */ - if (!m_tcpconnected) { - Tcp::connect(sc); - m_tcpconnected = sc.state() == State::Connected; - } - - if (m_tcpconnected) { - processConnect(sc); - } - } - - /** - * Accept a secure client. - * - * Because SSL needs several round-trips, if the socket is marked non-blocking and the connection is not - * completed yet, a new socket is returned but with the State::Accepting state. Its condition is set to - * Condition::Readable or Condition::Writable, the user is responsible of calling accept overload which takes - * 0 arguments on the returned socket when the condition is met. - * - * If the socket is blocking, this function blocks until the client is accepted and returned. - * - * If the client is accepted correctly, its state is set to State::Accepted. This instance does not change. - * - * @param sc the socket - * @param address the address destination - * @param length the address length - * @return the client - * @throw net::Error on errors - */ - template <typename Address> - Socket<Address, Tls> accept(Socket<Address, Tls> &sc, sockaddr *address, socklen_t *length) - { - Socket<Address, Tls> client = Tcp::accept(sc, address, length); - Tls &proto = client.protocol(); - - /* 1. Share the context */ - proto.m_context = m_context; - - /* 2. Create new SSL instance */ - proto.m_ssl = Ssl{SSL_new(m_context.get()), SSL_free}; - SSL_set_fd(proto.m_ssl.get(), client.handle()); - - /* 3. Try accept process on the **new** client */ - proto.processAccept(client); - - return client; - } - - /** - * Continue accept. - * - * This function must be called on the client that is being accepted. - * - * Like accept or connect, user is responsible of calling this function until the connection is complete. - * - * @param sc the socket - * @throw net::Error on errors - */ - template <typename Address, typename Protocol> - inline void accept(Socket<Address, Protocol> &sc) - { - processAccept(sc); - } - - /** - * Receive some secure data. - * - * If the socket is marked non-blocking, 0 is returned if no data is available yet or if the connection - * needs renegociation. If renegociation is required case, the action is set to Action::Receive and condition - * is set to Condition::Readable or Condition::Writable. The user must wait that the condition is met and - * call this function again. - * - * @param sc the socket - * @param data the destination - * @param len the buffer length - * @return the number of bytes read - * @throw net::Error on errors - */ - template <typename Address> - unsigned recv(Socket<Address, Tls> &sc, void *data, unsigned len) - { - auto nbread = SSL_read(m_ssl.get(), data, len); - - if (nbread <= 0) { - auto no = SSL_get_error(m_ssl.get(), nbread); - - if (no == SSL_ERROR_WANT_READ || no == SSL_ERROR_WANT_WRITE) { - nbread = 0; - updateStates(sc, sc.state(), Action::Receive, no); - } else { - throw Error{Error::System, "recv", error(no)}; - } - } - - return nbread; - } - - /** - * Send some data. - * - * Like recv, if the socket is marked non-blocking and no data can be sent or a negociation is required, - * condition and action are set. See receive for more details - * - * @param sc the socket - * @param data the data to send - * @param len the buffer length - * @return the number of bytes sent - * @throw net::Error on errors - */ - template <typename Address> - unsigned send(Socket<Address, Tls> &sc, const void *data, unsigned len) - { - auto nbsent = SSL_write(m_ssl.get(), data, len); - - if (nbsent <= 0) { - auto no = SSL_get_error(m_ssl.get(), nbsent); - - if (no == SSL_ERROR_WANT_READ || no == SSL_ERROR_WANT_WRITE) { - nbsent = 0; - updateStates(sc, sc.state(), Action::Send, no); - } else { - throw Error{Error::System, "send", error(no)}; - } - } - - return nbsent; - } -}; - -#endif // !SOCKET_NO_SSL - -/* }}} */ - -} // !protocol - -/* }}} */ - -/* - * Convenient helpers - * ------------------------------------------------------------------ - * - * - SocketTcp<Address>, for TCP sockets, - * - SocketUdp<Address>, for UDP sockets, - * - SocketTls<Address>, for secure TCP sockets. - */ - -/* {{{ Helpers */ - -/** - * Helper to create TCP sockets. - */ -template <typename Address> -using SocketTcp = Socket<Address, protocol::Tcp>; - -/** - * Helper to create TCP/IP sockets. - */ -using SocketTcpIp = Socket<address::Ip, protocol::Tcp>; - -#if !defined(_WIN32) - -/** - * Helper to create TCP/Local sockets. - */ -using SocketTcpLocal = Socket<address::Local, protocol::Tcp>; - -#endif - -/** - * Helper to create UDP sockets. - */ -template <typename Address> -using SocketUdp = Socket<Address, protocol::Udp>; - -/** - * Helper to create UDP/IP sockets. - */ -using SocketUdpIp = Socket<address::Ip, protocol::Udp>; - -#if !defined(SOCKET_NO_SSL) - -/** - * Helper to create OpenSSL TCP sockets. - */ -template <typename Address> -using SocketTls = Socket<Address, protocol::Tls>; - -/** - * Helper to create OpenSSL TCP/Ip sockets. - */ -using SocketTlsIp = Socket<address::Ip, protocol::Tls>; - -#endif // !SOCKET_NO_SSL - -/* }}} */ - -/* - * Select wrapper - * ------------------------------------------------------------------ - * - * Wrapper for select(2) and other various implementations. - */ - -/* {{{ Listener */ - -/** - * @class ListenerStatus - * @brief Result of polling - * - * Result of a select call, returns the first ready socket found with its - * flags. - */ -class ListenerStatus { -public: - Handle socket; //!< which socket is ready - Condition flags; //!< the flags -}; - -/** - * Table used in the socket listener to store which sockets have been - * set in which directions. - */ -using ListenerTable = std::map<Handle, Condition>; - -/** - * @class Select - * @brief Implements select(2) - * - * This class is the fallback of any other method, it is not preferred at all for many reasons. - */ -class Select { -public: - /** - * No-op, uses the ListenerTable directly. - */ - inline void set(const ListenerTable &, Handle, Condition, bool) noexcept {} - - /** - * No-op, uses the ListenerTable directly. - */ - inline void unset(const ListenerTable &, Handle, Condition, bool) noexcept {} - - /** - * Return the sockets - */ - std::vector<ListenerStatus> wait(const ListenerTable &table, int ms); - - /** - * Backend identifier - */ - inline const char *name() const noexcept - { - return "select"; - } -}; - -#if defined(SOCKET_HAVE_POLL) - -/** - * @class Poll - * @brief Implements poll(2). - * - * Poll is widely supported and is better than select(2). It is still not the - * best option as selecting the sockets is O(n). - */ -class Poll { -private: - std::vector<pollfd> m_fds; - - short toPoll(Condition flags) const noexcept; - Condition toCondition(short &event) const noexcept; - -public: - /** - * Set the handle. - */ - void set(const ListenerTable &, Handle, Condition, bool); - - /** - * Unset the handle. - */ - void unset(const ListenerTable &, Handle, Condition, bool); - - /** - * Wait for events. - */ - std::vector<ListenerStatus> wait(const ListenerTable &, int ms); - - /** - * Backend identifier - */ - inline const char *name() const noexcept - { - return "poll"; - } -}; - -#endif - -#if defined(SOCKET_HAVE_EPOLL) - -/** - * @class Epoll - * @brief Linux's epoll. - */ -class Epoll { -private: - int m_handle; - std::vector<epoll_event> m_events; - - Epoll(const Epoll &) = delete; - Epoll &operator=(const Epoll &) = delete; - Epoll(const Epoll &&) = delete; - Epoll &operator=(const Epoll &&) = delete; - - uint32_t toEpoll(Condition flags) const noexcept; - Condition toCondition(uint32_t events) const noexcept; - void update(Handle sc, int op, int eflags); - -public: - /** - * Construct the epoll instance. - */ - Epoll(); - - /** - * Close the epoll instance. - */ - ~Epoll(); - - /** - * Set the handle. - */ - void set(const ListenerTable &, Handle, Condition, bool); - - /** - * Unset the handle. - */ - void unset(const ListenerTable &, Handle, Condition, bool); - - /** - * Wait for events. - */ - std::vector<ListenerStatus> wait(const ListenerTable &, int); - - /** - * Backend identifier - */ - inline const char *name() const noexcept - { - return "epoll"; - } -}; - -#endif - -#if defined(SOCKET_HAVE_KQUEUE) - -/** - * @class Kqueue - * @brief Implements kqueue(2). - * - * This implementation is available on all BSD and Mac OS X. It is better than - * poll(2) because it's O(1), however it's a bit more memory consuming. - */ -class Kqueue { -private: - std::vector<struct kevent> m_result; - int m_handle; - - Kqueue(const Kqueue &) = delete; - Kqueue &operator=(const Kqueue &) = delete; - Kqueue(Kqueue &&) = delete; - Kqueue &operator=(Kqueue &&) = delete; - - void update(Handle sc, int filter, int kflags); - -public: - /** - * Construct the kqueue instance. - */ - Kqueue(); - - /** - * Destroy the kqueue instance. - */ - ~Kqueue(); - - /** - * Set the handle. - */ - void set(const ListenerTable &, Handle, Condition, bool); - - /** - * Unset the handle. - */ - void unset(const ListenerTable &, Handle, Condition, bool); - - /** - * Wait for events. - */ - std::vector<ListenerStatus> wait(const ListenerTable &, int); - - /** - * Backend identifier - */ - inline const char *name() const noexcept - { - return "kqueue"; - } -}; - -#endif - -/** - * @class Listener - * @brief Synchronous multiplexing - * - * Convenient wrapper around the select() system call. - * - * This class is implemented using a bridge pattern to allow different uses - * of listener implementation. - * - * You should not reinstanciate a new Listener at each iteartion of your - * main loop as it can be extremely costly. Instead use the same listener that - * you can safely modify on the fly. - * - * Currently, poll, epoll, select and kqueue are available. - * - * To implement the backend, the following functions must be available: - * - * ### Set - * - * @code - * void set(const ListenerTable &, Handle sc, Condition condition, bool add); - * @endcode - * - * This function, takes the socket to be added and the flags. The condition is - * always guaranteed to be correct and the function will never be called twice - * even if the user tries to set the same flag again. - * - * An optional add argument is added for backends which needs to do different - * operation depending if the socket was already set before or if it is the - * first time (e.g EPOLL_CTL_ADD vs EPOLL_CTL_MOD for epoll(7). - * - * ### Unset - * - * @code - * void unset(const ListenerTable &, Handle sc, Condition condition, bool remove); - * @endcode - * - * Like set, this function is only called if the condition is actually set and will - * not be called multiple times. - * - * Also like set, an optional remove argument is set if the socket is being - * completely removed (e.g no more flags are set for this socket). - * - * ### Wait - * - * @code - * std::vector<ListenerStatus> wait(const ListenerTable &, int ms); - * @endcode - * - * Wait for the sockets to be ready with the specified milliseconds. Must return a list of ListenerStatus, - * may throw any exceptions. - * - * ### Name - * - * @code - * inline const char *name() const noexcept - * @endcode - * - * Returns the backend name. Usually the class in lower case. - */ -template <typename Backend = SOCKET_DEFAULT_BACKEND> -class Listener { -private: - Backend m_backend; - ListenerTable m_table; - -public: - /** - * Construct an empty listener. - */ - Listener() = default; - - /** - * Get the backend. - * - * @return the backend - */ - inline const Backend &backend() const noexcept - { - return m_backend; - } - - /** - * Get the non-modifiable table. - * - * @return the table - */ - inline const ListenerTable &table() const noexcept - { - return m_table; - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline ListenerTable::const_iterator begin() const noexcept - { - return m_table.begin(); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline ListenerTable::const_iterator cbegin() const noexcept - { - return m_table.cbegin(); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline ListenerTable::const_iterator end() const noexcept - { - return m_table.end(); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline ListenerTable::const_iterator cend() const noexcept - { - return m_table.cend(); - } - - /** - * Add or update a socket to the listener. - * - * If the socket is already placed with the appropriate flags, the - * function is a no-op. - * - * If incorrect flags are passed, the function does nothing. - * - * @param sc the socket - * @param condition the condition (may be OR'ed) - * @throw Error if the backend failed to set - */ - void set(Handle sc, Condition condition) - { - /* Invalid or useless flags */ - if (condition == Condition::None || static_cast<int>(condition) > 0x3) - return; - - auto it = m_table.find(sc); - - /* - * Do not update the table if the backend failed to add - * or update. - */ - if (it == m_table.end()) { - m_backend.set(m_table, sc, condition, true); - m_table.emplace(sc, condition); - } else { - /* Remove flag if already present */ - if ((condition & Condition::Readable) == Condition::Readable && - (it->second & Condition::Readable) == Condition::Readable) { - condition &= ~(Condition::Readable); - } - if ((condition & Condition::Writable) == Condition::Writable && - (it->second & Condition::Writable) == Condition::Writable) { - condition &= ~(Condition::Writable); - } - - /* Still need a call? */ - if (condition != Condition::None) { - m_backend.set(m_table, sc, condition, false); - it->second |= condition; - } - } - } - - /** - * Unset a socket from the listener, only the flags is removed - * unless the two flagss are requested. - * - * For example, if you added a socket for both reading and writing, - * unsetting the write flags will keep the socket for reading. - * - * @param sc the socket - * @param condition the condition (may be OR'ed) - * @see remove - */ - void unset(Handle sc, Condition condition) - { - auto it = m_table.find(sc); - - /* Invalid or useless flags */ - if (condition == Condition::None || static_cast<int>(condition) > 0x3 || it == m_table.end()) - return; - - /* - * Like set, do not update if the socket is already at the appropriate - * state. - */ - if ((condition & Condition::Readable) == Condition::Readable && - (it->second & Condition::Readable) != Condition::Readable) { - condition &= ~(Condition::Readable); - } - if ((condition & Condition::Writable) == Condition::Writable && - (it->second & Condition::Writable) != Condition::Writable) { - condition &= ~(Condition::Writable); - } - - if (condition != Condition::None) { - /* Determine if it's a complete removal */ - bool removal = ((it->second) & ~(condition)) == Condition::None; - - m_backend.unset(m_table, sc, condition, removal); - - if (removal) { - m_table.erase(it); - } else { - it->second &= ~(condition); - } - } - } - - /** - * Remove completely the socket from the listener. - * - * It is a shorthand for unset(sc, Condition::Readable | Condition::Writable); - * - * @param sc the socket - */ - inline void remove(Handle sc) - { - unset(sc, Condition::Readable | Condition::Writable); - } - - /** - * Remove all sockets. - */ - inline void clear() - { - while (!m_table.empty()) { - remove(m_table.begin()->first); - } - } - - /** - * Get the number of sockets in the listener. - */ - inline ListenerTable::size_type size() const noexcept - { - return m_table.size(); - } - - /** - * Select a socket. Waits for a specific amount of time specified as the duration. - * - * @param duration the duration - * @return the socket ready - */ - template <typename Rep, typename Ratio> - inline ListenerStatus wait(const std::chrono::duration<Rep, Ratio> &duration) - { - assert(!m_table.empty()); - - auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration); - - return m_backend.wait(m_table, cvt.count())[0]; - } - - /** - * Overload with milliseconds. - * - * @param timeout the optional timeout in milliseconds - * @return the socket ready - */ - inline ListenerStatus wait(int timeout = -1) - { - return wait(std::chrono::milliseconds(timeout)); - } - - /** - * Select multiple sockets. - * - * @param duration the duration - * @return the socket ready - */ - template <typename Rep, typename Ratio> - inline std::vector<ListenerStatus> waitMultiple(const std::chrono::duration<Rep, Ratio> &duration) - { - assert(!m_table.empty()); - - auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration); - - return m_backend.wait(m_table, cvt.count()); - } - - /** - * Overload with milliseconds. - * - * @return the socket ready - */ - inline std::vector<ListenerStatus> waitMultiple(int timeout = -1) - { - return waitMultiple(std::chrono::milliseconds(timeout)); - } -}; - -/* }}} */ - -/* - * Callback - * ------------------------------------------------------------------ - * - * Function owner with tests. - */ - -/* {{{ Callback */ - -/** - * @class Callback - * @brief Convenient signal owner that checks if the target is valid. - * - * This class also catch all errors thrown from signals to avoid interfering with our process. - */ -template <typename... Args> -class Callback : public std::function<void (Args...)> { -public: - /** - * Inherited constructors. - */ - using std::function<void (Args...)>::function; - - /** - * Execute the callback only if a target is set. - */ - void operator()(Args... args) const - { - if (*this) { - try { - std::function<void (Args...)>::operator()(args...); - } catch (...) { - } - } - } -}; - -/* }}} */ - -/* - * StreamConnection - * ------------------------------------------------------------------ - * - * Client connected on the server side. - */ - -/* {{{ StreamConnection */ - -/** - * @class StreamConnection - * @brief Connected client on the server side. - * - * This object is created from StreamServer when a new client is connected, it is the higher - * level object of sockets and completely asynchronous. - */ -template <typename Address, typename Protocol> -class StreamConnection { -public: - /** - * Called when the output has changed. - */ - using WriteHandler = Callback<>; - -private: - /* Signals */ - WriteHandler m_onWrite; - - /* Sockets and output buffer */ - Socket<Address, Protocol> m_socket; - std::string m_output; - -public: - /** - * Create the connection. - * - * @param s the socket - */ - StreamConnection(Socket<Address, Protocol> s) - : m_socket{std::move(s)} - { - m_socket.set(net::option::SockBlockMode{false}); - } - - /** - * Access the underlying socket. - * - * @return the socket - * @warning use with care - */ - inline Socket<Address, Protocol> &socket() noexcept - { - return m_socket; - } - - /** - * Access the current output. - * - * @return the output - */ - inline const std::string &output() const noexcept - { - return m_output; - } - - /** - * Overloaded function - * - * @return the output - * @warning use with care, avoid modifying the output if you don't know what you're doing - */ - inline std::string &output() noexcept - { - return m_output; - } - - /** - * Post some data to be sent asynchronously. - * - * @param str the data to append - */ - inline void send(std::string str) - { - m_output += str; - m_onWrite(); - } - - /** - * Kill the client. - */ - inline void close() - { - m_socket.close(); - } - - /** - * Set the write handler, the signal is emitted when the output has changed so that the StreamServer owner - * knows that there are some data to send. - * - * @param handler the handler - * @warning you usually never need to set this yourself - */ - inline void setWriteHandler(WriteHandler handler) - { - m_onWrite = std::move(handler); - } -}; - -/* }}} */ - -/* - * StreamServer - * ------------------------------------------------------------------ - * - * Convenient stream oriented server. - */ - -/* {{{ StreamServer */ - -/** - * @class StreamServer - * @brief Convenient stream server for TCP and TLS. - * - * This class does all the things for you as accepting new clients, listening for it and sending data. It works - * asynchronously without blocking to let you control your process workflow. - * - * This class is not thread safe and you must not call any of the functions from different threads. - */ -template <typename Address, typename Protocol> -class StreamServer { -public: - /** - * Handler when a new client is connected. - */ - using ConnectionHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &>; - - /** - * Handler when a client is disconnected. - */ - using DisconnectionHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &>; - - /** - * Handler when data has been received from a client. - */ - using ReadHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &, const std::string &>; - - /** - * Handler when data has been correctly sent to a client. - */ - using WriteHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &, const std::string &>; - - /** - * Handler when an error occured. - */ - using ErrorHandler = Callback<const Error &>; - - /** - * Handler when there was a timeout. - */ - using TimeoutHandler = Callback<>; - -private: - using ClientMap = std::map<Handle, std::shared_ptr<StreamConnection<Address, Protocol>>>; - - /* Signals */ - ConnectionHandler m_onConnection; - DisconnectionHandler m_onDisconnection; - ReadHandler m_onRead; - WriteHandler m_onWrite; - ErrorHandler m_onError; - TimeoutHandler m_onTimeout; - - /* Sockets */ - Socket<Address, Protocol> m_master; - Listener<> m_listener; - ClientMap m_clients; - - /* - * Update flags depending on the required condition. - */ - void updateFlags(std::shared_ptr<StreamConnection<Address, Protocol>> &client) - { - assert(client->socket().action() != Action::None); - - m_listener.remove(client->socket().handle()); - m_listener.set(client->socket().handle(), client->socket().condition()); - } - - /* - * Continue accept process. - */ - template <typename AcceptCall> - void processAccept(std::shared_ptr<StreamConnection<Address, Protocol>> &client, const AcceptCall &acceptFunc) - { - try { - /* Do the accept */ - acceptFunc(); - - /* 1. First remove completely the client */ - m_listener.remove(client->socket().handle()); - - /* 2. If accept is not finished, wait for the appropriate condition */ - if (client->socket().state() == State::Accepted) { - /* 3. Client is accepted, notify the user */ - m_listener.set(client->socket().handle(), Condition::Readable); - m_onConnection(client); - } else { - /* Operation still in progress */ - updateFlags(client); - } - } catch (const Error &error) { - m_clients.erase(client->socket().handle()); - m_listener.remove(client->socket().handle()); - m_onError(error); - } - } - - /* - * Process initial accept of master socket, this is the initial accepting process. Except on errors, the - * socket is stored but the user will be notified only once the socket is completely accepted. - */ - void processInitialAccept() - { - // TODO: store address too. - std::shared_ptr<StreamConnection<Address, Protocol>> client = std::make_shared<StreamConnection<Address, Protocol>>(m_master.accept(nullptr)); - std::weak_ptr<StreamConnection<Address, Protocol>> ptr{client}; - - /* 1. Register output changed to update listener */ - client->setWriteHandler([this, ptr] () { - auto client = ptr.lock(); - - /* Do not update the listener immediately if an action is pending */ - if (client && client->socket().action() == Action::None && !client->output().empty()) { - m_listener.set(client->socket().handle(), Condition::Writable); - } - }); - - /* 2. Add the client */ - m_clients.insert(std::make_pair(client->socket().handle(), client)); - - /* - * 2. Do an initial check to set the listener flags, at this moment the socket may or not be - * completely accepted. - */ - processAccept(client, [&] () {}); - } - - /* - * Read or complete the read operation. - */ - void processRead(std::shared_ptr<StreamConnection<Address, Protocol>> &client) - { - /* - * Read because there is something to read or because the pending operation is - * read and must complete. - */ - auto buffer = client->socket().recv(512); - - /* - * Now the receive operation may be completed, in that case, two possibilities: - * - * 1. The action is set to None (completed) - * 2. The action is still not complete, update the flags - */ - if (client->socket().action() == Action::None) { - /* Empty mean normal disconnection */ - if (buffer.empty()) { - m_listener.remove(client->socket().handle()); - m_clients.erase(client->socket().handle()); - m_onDisconnection(client); - } else { - /* - * At this step, it is possible that we were completing a receive operation, in this - * case the write flag may be removed, add it if required. - */ - if (!client->output().empty()) { - m_listener.set(client->socket().handle(), Condition::Writable); - } - - m_onRead(client, buffer); - } - } else { - /* Operation in progress */ - updateFlags(client); - } - } - - /* - * Flush the output buffer. - */ - void processWrite(std::shared_ptr<StreamConnection<Address, Protocol>> &client) - { - auto &output = client->output(); - auto nsent = client->socket().send(output); - - if (client->socket().action() == Action::None) { - /* 1. Create a copy of content that has been sent */ - auto sent = output.substr(0, nsent); - - /* 2. Erase the content sent */ - output.erase(0, nsent); - - /* 3. Update listener */ - if (output.empty()) { - m_listener.unset(client->socket().handle(), Condition::Writable); - } - - /* 4. Notify user */ - m_onWrite(client, sent); - } else { - updateFlags(client); - } - } - - void processSync(std::shared_ptr<StreamConnection<Address, Protocol>> &client, Condition flags) - { - try { - auto action = client->socket().action(); - - if (action == Action::Receive || - (action == Action::None && (flags & Condition::Readable) == Condition::Readable)) { - processRead(client); - } else if ((flags & Condition::Writable) == Condition::Writable) { - processWrite(client); - } - } catch (const Error &error) { - m_onDisconnection(client); - m_listener.remove(client->socket().handle()); - m_clients.erase(client->socket().handle()); - } - } - -public: - /** - * Create a stream server with the specified address to bind. - * - * @param protocol the protocol (Tcp or Tls) - * @param address the address to bind - * @param max the max number to listen - * @throw Error on errors - */ - StreamServer(Protocol protocol, const Address &address, int max = 128) - : m_master{std::move(protocol), address} - { - // TODO: m_onError - m_master.set(SOL_SOCKET, SO_REUSEADDR, 1); - m_master.bind(address); - m_master.listen(max); - m_listener.set(m_master.handle(), Condition::Readable); - } - - /** - * Set the connection handler, called when a new client is connected. - * - * @param handler the handler - */ - inline void setConnectionHandler(ConnectionHandler handler) - { - m_onConnection = std::move(handler); - } - - /** - * Set the disconnection handler, called when a client died. - * - * @param handler the handler - */ - inline void setDisconnectionHandler(DisconnectionHandler handler) - { - m_onDisconnection = std::move(handler); - } - - /** - * Set the receive handler, called when a client has sent something. - * - * @param handler the handler - */ - inline void setReadHandler(ReadHandler handler) - { - m_onRead = std::move(handler); - } - - /** - * Set the writing handler, called when some data has been sent to a client. - * - * @param handler the handler - */ - inline void setWriteHandler(WriteHandler handler) - { - m_onWrite = std::move(handler); - } - - /** - * Set the error handler, called when unrecoverable error has occured. - * - * @param handler the handler - */ - inline void setErrorHandler(ErrorHandler handler) - { - m_onError = std::move(handler); - } - - /** - * Set the timeout handler, called when the selection has timeout. - * - * @param handler the handler - */ - inline void setTimeoutHandler(TimeoutHandler handler) - { - m_onTimeout = std::move(handler); - } - - /** - * Poll for the next event. - * - * @param timeout the timeout (-1 for indefinitely) - * @throw Error on errors - */ - void poll(int timeout = -1) - { - try { - auto st = m_listener.wait(timeout); - - if (st.socket == m_master.handle()) { - /* New client */ - processInitialAccept(); - } else { - /* Recv / Send / Accept on a client */ - auto client = m_clients[st.socket]; - - if (client->socket().state() == State::Accepted) { - processSync(client, st.flags); - } else { - processAccept(client, [&] () { client->socket().accept(); }); - } - } - } catch (const Error &error) { - if (error.code() == Error::Timeout) { - m_onTimeout(); - } else { - m_onError(error); - } - } - } -}; - -/* }}} */ - -/* - * StreamClient - * ------------------------------------------------------------------ - */ - -/* {{{ StreamClient */ - -/** - * @class StreamClient - * @brief Client side connection to a server. - * - * This class is not thread safe and you must not call any of the functions from different threads. - */ -template <typename Address, typename Protocol> -class StreamClient { -public: - /** - * Handler when connection is complete. - */ - using ConnectionHandler = Callback<>; - - /** - * Handler when data has been received. - */ - using ReadHandler = Callback<const std::string &>; - - /** - * Handler when data has been sent correctly. - */ - using WriteHandler = Callback<const std::string &>; - - /** - * Handler when disconnected. - */ - using DisconnectionHandler = Callback<>; - - /** - * Handler on unrecoverable error. - */ - using ErrorHandler = Callback<const Error &>; - - /** - * Handler when timeout occured. - */ - using TimeoutHandler = Callback<>; - -private: - /* Signals */ - ConnectionHandler m_onConnection; - ReadHandler m_onRead; - WriteHandler m_onWrite; - DisconnectionHandler m_onDisconnection; - ErrorHandler m_onError; - TimeoutHandler m_onTimeout; - - /* Socket */ - Socket<Address, Protocol> m_socket; - Listener<> m_listener; - - /* Output buffer */ - std::string m_output; - - /* - * Update the flags after an uncompleted operation. This function must only be called when the operation - * has not complete (e.g. connect, recv, send). - */ - void updateFlags() - { - assert(m_socket.action() != Action::None); - - m_listener.remove(m_socket.handle()); - m_listener.set(m_socket.handle(), m_socket.condition()); - } - - /* - * This is the generic connect helper, it will be used to both initiate the connection or to continue the - * connection process if needed. - * - * Thus the template parameter is the appropriate function to call either, m_socket.connect(address) or - * m_socket.connect(). - * - * See poll() and connect() to understand. - */ - template <typename ConnectCall> - void processConnect(const ConnectCall &connectFunc) - { - /* Call m_socket.connect() or m_socket.connect(address) */ - connectFunc(); - - /* Remove entirely */ - m_listener.remove(m_socket.handle()); - - if (m_socket.state() == State::Connected) { - m_onConnection(); - m_listener.set(m_socket.handle(), Condition::Readable); - } else { - /* Connection still in progress */ - updateFlags(); - } - } - - /* - * Receive or complete the receive command, if the command is not complete, the listener is updated - * accordingly. - */ - void processRead() - { - auto received = m_socket.recv(512); - - if (m_socket.action() == Action::None) { - /* 0 means disconnection */ - if (received.empty()) { - m_onDisconnection(); - } else { - /* - * At this step, it is possible that we were completing a receive operation, in this - * case the write flag may be removed, add it if required. - */ - if (m_output.empty()) { - m_listener.unset(m_socket.handle(), Condition::Writable); - } - - m_onRead(received); - } - } else { - /* Receive operation in progress */ - updateFlags(); - } - } - - /* - * Send or complete the send command, if the command is not complete, the listener is updated - * accordingly. - */ - void processWrite() - { - auto nsent = m_socket.send(m_output); - - if (m_socket.action() == Action::None) { - /* 1. Make a copy of what has been sent */ - auto sent = m_output.substr(0, nsent); - - /* 2. Erase sent content */ - m_output.erase(0, nsent); - - /* 3. Update flags if needed */ - if (m_output.empty()) { - m_listener.unset(m_socket.handle(), Condition::Writable); - } - - /* 4. Notify user */ - m_onWrite(sent); - } else { - /* Send operation in progress */ - updateFlags(); - } - } - - /* - * Receive or send. - */ - void processSync(Condition condition) - { - if ((m_socket.action() == Action::Receive) || - (m_socket.action() == Action::None && (condition & Condition::Readable) == Condition::Readable)) { - processRead(); - } else { - processWrite(); - } - } - -public: - /** - * Create a client. The client is automatically marked as non-blocking. - * - * @param protocol the protocol (Tcp or Tls) - * @param address the optional address - * @throw net::Error on failures - */ - StreamClient(Protocol protocol = {}, const Address &address = {}) - : m_socket{std::move(protocol), address} - { - m_socket.set(net::option::SockBlockMode{false}); - m_listener.set(m_socket.handle(), Condition::Readable); - } - - /** - * Set the connection handler, called when the connection succeed. - * - * @param handler the handler - */ - inline void setConnectionHandler(ConnectionHandler handler) - { - m_onConnection = std::move(handler); - } - - /** - * Set the disconnection handler, called when the server closed the connection. - * - * @param handler the handler - */ - inline void setDisconnectionHandler(DisconnectionHandler handler) - { - m_onDisconnection = std::move(handler); - } - - /** - * Set the read handler, called when we received something. - * - * @param handler the handler - */ - inline void setReadHandler(ReadHandler handler) - { - m_onRead = std::move(handler); - } - - /** - * Set the write handler, called when we successfully sent data. - * - * @param handler the handler - */ - inline void setWriteHandler(WriteHandler handler) - { - m_onWrite = std::move(handler); - } - - /** - * Set the error handler, called when unexpected error occurs. - * - * @param handler the handler - */ - inline void setErrorHandler(ErrorHandler handler) - { - m_onError = std::move(handler); - } - - /** - * Connect to a server, this function may connect immediately or not in any case the connection handler - * will be called when the connection completed. - * - * @param address the address to connect to - */ - void connect(const Address &address) noexcept - { - assert(m_socket.state() == State::Open); - - processConnect([&] () { m_socket.connect(address); }); - } - - /** - * Asynchronously send data to the server. - * - * @param str the data to append - */ - void send(std::string str) - { - m_output += str; - - /* Don't update the listener if there is a pending operation */ - if (m_socket.state() == State::Connected && m_socket.action() == Action::None && !m_output.empty()) { - m_listener.set(m_socket.handle(), Condition::Writable); - } - } - - /** - * Wait for the next event. - * - * @param timeout the time to wait in milliseconds - * @throw Error on errors - */ - void poll(int timeout = -1) noexcept - { - try { - auto st = m_listener.wait(timeout); - - if (m_socket.state() != State::Connected) { - /* Continue the connection */ - processConnect([&] () { m_socket.connect(); }); - } else { - /* Read / Write */ - processSync(st.flags); - } - } catch (const Error &error) { - if (error.code() == Error::Timeout) { - m_onTimeout(); - } else { - m_listener.remove(m_socket.handle()); - m_onError(error); - } - } - } -}; - -/* }}} */ - -} // !net - -} // !irccd - -#endif // !_SOCKETS_H_
--- a/lib/irccd/private/xdg.cpp Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,129 +0,0 @@ -/* - * Xdg.cpp -- XDG directory specifications - * - * Copyright (c) 2013, 2014, 2015 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 <cstdlib> -#include <stdexcept> -#include <sstream> - -#include "xdg.h" - -namespace irccd { - -namespace { - -bool isabsolute(const std::string &path) -{ - return path.length() > 0 && path[0] == '/'; -} - -std::vector<std::string> split(const std::string &arg) -{ - std::stringstream iss(arg); - std::string item; - std::vector<std::string> elems; - - while (std::getline(iss, item, ':')) - if (isabsolute(item)) - elems.push_back(item); - - return elems; -} - -std::string envOrHome(const std::string &var, const std::string &repl) -{ - auto value = getenv(var.c_str()); - - if (value == nullptr || !isabsolute(value)) { - auto home = getenv("HOME"); - - if (home == nullptr) - throw std::runtime_error("could not get home directory"); - - return std::string(home) + "/" + repl; - } - - return value; -} - -std::vector<std::string> listOrDefaults(const std::string &var, const std::vector<std::string> &list) -{ - auto value = getenv(var.c_str()); - - if (!value) - return list; - - // No valid item at all? Use defaults - auto result = split(value); - - return (result.size() == 0) ? list : result; -} - -} // !namespace - -Xdg::Xdg() -{ - m_configHome = envOrHome("XDG_CONFIG_HOME", ".config"); - m_dataHome = envOrHome("XDG_DATA_HOME", ".local/share"); - m_cacheHome = envOrHome("XDG_CACHE_HOME", ".cache"); - - m_configDirs = listOrDefaults("XDG_CONFIG_DIRS", { "/etc/xdg" }); - m_dataDirs = listOrDefaults("XDG_DATA_DIRS", { "/usr/local/share", "/usr/share" }); - - /* - * Runtime directory is a special case and does not have a replacement, the - * application should manage this by itself. - */ - auto runtime = getenv("XDG_RUNTIME_DIR"); - if (runtime && isabsolute(runtime)) - m_runtimeDir = runtime; -} - -const std::string &Xdg::configHome() const noexcept -{ - return m_configHome; -} - -const std::string &Xdg::dataHome() const noexcept -{ - return m_dataHome; -} - -const std::string &Xdg::cacheHome() const noexcept -{ - return m_cacheHome; -} - -const std::string &Xdg::runtimeDir() const -{ - if (m_runtimeDir.size() == 0) - throw std::runtime_error("XDG_RUNTIME_DIR is not set"); - - return m_runtimeDir; -} - -const Xdg::List &Xdg::configDirs() const noexcept -{ - return m_configDirs; -} - -const Xdg::List &Xdg::dataDirs() const noexcept -{ - return m_dataDirs; -} - -} // !irccd
--- a/lib/irccd/private/xdg.h Sat Mar 26 14:41:53 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,112 +0,0 @@ -/* - * Xdg.h -- XDG directory specifications - * - * Copyright (c) 2013, 2014, 2015 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_XDG_H_ -#define _IRCCD_XDG_H_ - -/** - * @file Xdg.h - * @brief Read XDG standard specifications - */ - -#include <vector> -#include <string> - -namespace irccd { - -/** - * @class Xdg - * @brief XDG specifications - * - * Read and get XDG directories. This file contains exports thingies so it can - * compiles successfully on Windows but its usage is discouraged. - */ -class Xdg { -public: - /** - * list of directories. - */ - using List = std::vector<std::string>; - -private: - std::string m_configHome; - std::string m_dataHome; - std::string m_cacheHome; - std::string m_runtimeDir; - List m_configDirs; - List m_dataDirs; - -public: - /** - * Open an xdg instance and load directories. - * - * @throw std::runtime_error on failures - */ - Xdg(); - - /** - * Get the config directory. ${XDG_CONFIG_HOME} or ${HOME}/.config - * - * @return the config directory - */ - const std::string &configHome() const noexcept; - - /** - * Get the data directory. ${XDG_DATA_HOME} or ${HOME}/.local/share - * - * @return the data directory - */ - const std::string &dataHome() const noexcept; - - /** - * Get the cache directory. ${XDG_CACHE_HOME} or ${HOME}/.cache - * - * @return the cache directory - */ - const std::string &cacheHome() const noexcept; - - /** - * Get the runtime directory. ${XDG_RUNTIME_DIR} must be set, - * if not, it throws an exception. - * - * The XDG standard says that application should handle XDG_RUNTIME_DIR by - * themselves. - * - * @return the runtime directory - * @throw std::runtime_error on error - */ - const std::string &runtimeDir() const; - - /** - * Get the standard config directories. ${XDG_CONFIG_DIRS} or { "/etc/xdg" } - * - * @return the list of config directories - */ - const List &configDirs() const noexcept; - - /** - * Get the data directories. ${XDG_DATA_DIRS} or { "/usr/local/share", "/usr/share" } - * - * @return the list of data directories - */ - const List &dataDirs() const noexcept; -}; - -} // !irccd - -#endif // !_IRCCD_XDG_H_
--- a/lib/irccd/server-state.h Sat Mar 26 14:41:53 2016 +0100 +++ b/lib/irccd/server-state.h Wed Mar 30 13:52:47 2016 +0200 @@ -21,8 +21,8 @@ #include <irccd-config.h> -#include <irccd/private/elapsed-timer.h> -#include <irccd/private/sockets.h> +#include "elapsed-timer.h" +#include "sockets.h" namespace irccd {
--- a/lib/irccd/server.cpp Sat Mar 26 14:41:53 2016 +0100 +++ b/lib/irccd/server.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -24,14 +24,9 @@ #include <libirc_rfcnumeric.h> #include "logger.h" +#include "server.h" #include "util.h" -#include "server.h" - -#if defined(WITH_JS) -# include "js/js.h" -#endif - namespace irccd { bool Server::isSelf(const std::string &nick) const noexcept @@ -436,13 +431,4 @@ irc_process_select_descriptors(m_session.get(), &setinput, &setoutput); } -#if defined(WITH_JS) - -void Server::prototype(js::Context &ctx) -{ - ctx.getGlobal<void>("\xff""\xff""Server-proto"); -} - -#endif - } // !irccd
--- a/lib/irccd/server.h Sat Mar 26 14:41:53 2016 +0100 +++ b/lib/irccd/server.h Wed Mar 30 13:52:47 2016 +0200 @@ -34,10 +34,9 @@ #include <irccd-config.h> -#include <irccd/private/signals.h> - #include "logger.h" #include "server-state.h" +#include "signals.h" namespace irccd {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/signals.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,175 @@ +/* + * signals.h -- synchronous observer mechanism + * + * 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_SIGNALS_H_ +#define _IRCCD_SIGNALS_H_ + +#include <functional> +#include <stack> +#include <unordered_map> +#include <vector> + +/** + * @file Signals.h + * @brief Similar Qt signal subsystem for irccd + */ + +namespace irccd { + +/** + * @class SignalConnection + * @brief Stores the reference to the callable + * + * This class can be stored to remove a registered function from a Signal, be + * careful to not mix connections between different signals as they are just + * referenced by ids. + */ +class SignalConnection { +private: + unsigned m_id; + +public: + /** + * Create a signal connection. + * + * @param id the id + */ + inline SignalConnection(unsigned id) noexcept + : m_id(id) + { + } + + /** + * Get the reference object. + * + * @return the id + */ + inline unsigned id() const noexcept + { + return m_id; + } +}; + +/** + * @class Signal + * @brief Stores and call registered functions + * + * This class is intended to be use as a public field in the desired object. + * + * The user just have to call one of connect(), disconnect() or the call + * operator to use this class. + * + * It stores the callable as std::function so type-erasure is complete. + * + * The user is responsible of taking care that the object is still alive + * in case that the function takes a reference to the object. + */ +template <typename... Args> +class Signal { +private: + using Function = std::function<void (Args...)>; + using FunctionMap = std::unordered_map<unsigned, Function>; + using Stack = std::stack<unsigned>; + + FunctionMap m_functions; + Stack m_stack; + unsigned m_max{0}; + +public: + /** + * Register a new function to the signal. + * + * @param function the function + * @return the connection in case you want to remove it + */ + inline SignalConnection connect(Function function) noexcept + { + unsigned id; + + if (!m_stack.empty()) { + id = m_stack.top(); + m_stack.pop(); + } else { + id = m_max ++; + } + + m_functions.emplace(id, std::move(function)); + + return SignalConnection{id}; + } + + /** + * Disconnect a connection. + * + * @param connection the connection + * @warning Be sure that the connection belongs to that signal + */ + inline void disconnect(const SignalConnection &connection) noexcept + { + auto value = m_functions.find(connection.id()); + + if (value != m_functions.end()) { + m_functions.erase(connection.id()); + m_stack.push(connection.id()); + } + } + + /** + * Remove all registered functions. + */ + inline void clear() + { + m_functions.clear(); + m_max = 0; + + while (!m_stack.empty()) + m_stack.pop(); + } + + /** + * Call every functions. + * + * @param args the arguments to pass to the signal + */ + void operator()(Args... args) const + { + /* + * Make a copy of the ids before iterating because the callbacks may eventually remove or modify + * the list. + */ + std::vector<unsigned> ids; + + for (auto &pair : m_functions) + ids.push_back(pair.first); + + /* + * Now iterate while checking if the next id is still available, however if any new signals were + * added while iterating, they will not be called immediately. + */ + for (unsigned i : ids) { + auto it = m_functions.find(i); + + if (it != m_functions.end()) + it->second(args...); + } + } +}; + +} // !irccd + +#endif // !_IRCCD_SIGNALS_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/sockets.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,713 @@ +/* + * sockets.cpp -- portable C++ socket wrappers + * + * 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. + */ + +#define TIMEOUT_MSG "operation timeout" + +#include <algorithm> +#include <atomic> +#include <cstring> +#include <mutex> + +#include "sockets.h" + +namespace irccd { + +namespace net { + +/* + * Portable constants + * ------------------------------------------------------------------ + */ + +/* {{{ Constants */ + +#if defined(_WIN32) + +const Handle Invalid{INVALID_SOCKET}; +const int Failure{SOCKET_ERROR}; + +#else + +const Handle Invalid{-1}; +const int Failure{-1}; + +#endif + +/* }}} */ + +/* + * Portable functions + * ------------------------------------------------------------------ + */ + +/* {{{ Functions */ + +#if defined(_WIN32) + +namespace { + +static std::mutex s_mutex; +static std::atomic<bool> s_initialized{false}; + +} // !namespace + +#endif // !_WIN32 + +void init() noexcept +{ +#if defined(_WIN32) + std::lock_guard<std::mutex> lock(s_mutex); + + if (!s_initialized) { + s_initialized = true; + + WSADATA wsa; + WSAStartup(MAKEWORD(2, 2), &wsa); + + /* + * If SOCKET_WSA_NO_INIT is not set then the user + * must also call finish himself. + */ +#if !defined(SOCKET_NO_AUTO_INIT) + atexit(finish); +#endif + } +#endif +} + +void finish() noexcept +{ +#if defined(_WIN32) + WSACleanup(); +#endif +} + +std::string error(int errn) +{ +#if defined(_WIN32) + LPSTR str = nullptr; + std::string errmsg = "Unknown error"; + + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + errn, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&str, 0, NULL); + + + if (str) { + errmsg = std::string(str); + LocalFree(str); + } + + return errmsg; +#else + return strerror(errn); +#endif +} + +std::string error() +{ +#if defined(_WIN32) + return error(WSAGetLastError()); +#else + return error(errno); +#endif +} + +/* }}} */ + +/* + * SSL stuff + * ------------------------------------------------------------------ + */ + +/* {{{ SSL initialization */ + +#if !defined(SOCKET_NO_SSL) + +namespace ssl { + +namespace { + +std::mutex mutex; +std::atomic<bool> initialized{false}; + +} // !namespace + +void finish() noexcept +{ + ERR_free_strings(); +} + +void init() noexcept +{ + std::lock_guard<std::mutex> lock{mutex}; + + if (!initialized) { + initialized = true; + + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + +#if !defined(SOCKET_NO_AUTO_SSL_INIT) + atexit(finish); +#endif // SOCKET_NO_AUTO_SSL_INIT + } +} + +} // !ssl + +#endif // SOCKET_NO_SSL + +/* }}} */ + +/* + * Error class + * ------------------------------------------------------------------ + */ + +/* {{{ Error */ + +Error::Error(Code code, std::string function) + : m_code{code} + , m_function{std::move(function)} + , m_error{error()} +{ +} + +Error::Error(Code code, std::string function, int n) + : m_code{code} + , m_function{std::move(function)} + , m_error{error(n)} +{ +} + +Error::Error(Code code, std::string function, std::string error) + : m_code{code} + , m_function{std::move(function)} + , m_error{std::move(error)} +{ +} + +/* }}} */ + +/* + * Predefine addressed to be used + * ------------------------------------------------------------------ + */ + +/* {{{ Addresses */ + +namespace address { + +/* Default domain */ +int Ip::m_default{AF_INET}; + +Ip::Ip(Type domain) noexcept + : m_domain(static_cast<int>(domain)) +{ + assert(m_domain == AF_INET6 || m_domain == AF_INET); + + if (m_domain == AF_INET6) { + std::memset(&m_sin6, 0, sizeof (sockaddr_in6)); + } else { + std::memset(&m_sin, 0, sizeof (sockaddr_in)); + } +} + +Ip::Ip(const std::string &host, int port, Type domain) + : m_domain(static_cast<int>(domain)) +{ + assert(m_domain == AF_INET6 || m_domain == AF_INET); + + if (host == "*") { + if (m_domain == AF_INET6) { + std::memset(&m_sin6, 0, sizeof (sockaddr_in6)); + + m_length = sizeof (sockaddr_in6); + m_sin6.sin6_addr = in6addr_any; + m_sin6.sin6_family = AF_INET6; + m_sin6.sin6_port = htons(port); + } else { + std::memset(&m_sin, 0, sizeof (sockaddr_in)); + + m_length = sizeof (sockaddr_in); + m_sin.sin_addr.s_addr = INADDR_ANY; + m_sin.sin_family = AF_INET; + m_sin.sin_port = htons(port); + } + } else { + addrinfo hints, *res; + + std::memset(&hints, 0, sizeof (addrinfo)); + hints.ai_family = domain; + + auto error = getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &res); + if (error != 0) { + throw Error{Error::System, "getaddrinfo", gai_strerror(error)}; + } + + if (m_domain == AF_INET6) { + std::memcpy(&m_sin6, res->ai_addr, res->ai_addrlen); + } else { + std::memcpy(&m_sin, res->ai_addr, res->ai_addrlen); + } + + m_length = res->ai_addrlen; + freeaddrinfo(res); + } +} + +Ip::Ip(const sockaddr_storage *ss, socklen_t length) noexcept + : m_length{length} + , m_domain{ss->ss_family} +{ + assert(ss->ss_family == AF_INET6 || ss->ss_family == AF_INET); + + if (ss->ss_family == AF_INET6) { + std::memcpy(&m_sin6, ss, length); + } else if (ss->ss_family == AF_INET) { + std::memcpy(&m_sin, ss, length); + } +} + +#if !defined(_WIN32) + +Local::Local() noexcept +{ + std::memset(&m_sun, 0, sizeof (sockaddr_un)); +} + +Local::Local(std::string path, bool rm) noexcept + : m_path{std::move(path)} +{ + /* Silently remove the file even if it fails */ + if (rm) { + ::remove(m_path.c_str()); + } + + /* Copy the path */ + std::memset(m_sun.sun_path, 0, sizeof (m_sun.sun_path)); + std::strncpy(m_sun.sun_path, m_path.c_str(), sizeof (m_sun.sun_path) - 1); + + /* Set the parameters */ + m_sun.sun_family = AF_LOCAL; +} + +Local::Local(const sockaddr_storage *ss, socklen_t length) noexcept +{ + assert(ss->ss_family == AF_LOCAL); + + if (ss->ss_family == AF_LOCAL) { + std::memcpy(&m_sun, ss, length); + m_path = reinterpret_cast<const sockaddr_un &>(m_sun).sun_path; + } +} + +#endif // !_WIN32 + +} // !address + +/* }}} */ + +/* + * Select + * ------------------------------------------------------------------ + */ + +/* {{{ Select */ + +std::vector<ListenerStatus> Select::wait(const ListenerTable &table, int ms) +{ + timeval maxwait, *towait; + fd_set readset; + fd_set writeset; + + FD_ZERO(&readset); + FD_ZERO(&writeset); + + Handle max = 0; + + for (const auto &pair : table) { + if ((pair.second & Condition::Readable) == Condition::Readable) { + FD_SET(pair.first, &readset); + } + if ((pair.second & Condition::Writable) == Condition::Writable) { + FD_SET(pair.first, &writeset); + } + + if (pair.first > max) { + max = pair.first; + } + } + + maxwait.tv_sec = 0; + maxwait.tv_usec = ms * 1000; + + // Set to nullptr for infinite timeout. + towait = (ms < 0) ? nullptr : &maxwait; + + auto error = ::select(max + 1, &readset, &writeset, nullptr, towait); + if (error == Failure) { + throw Error{Error::System, "select"}; + } + if (error == 0) { + throw Error{Error::Timeout, "select", TIMEOUT_MSG}; + } + + std::vector<ListenerStatus> sockets; + + for (const auto &pair : table) { + if (FD_ISSET(pair.first, &readset)) { + sockets.push_back(ListenerStatus{pair.first, Condition::Readable}); + } + if (FD_ISSET(pair.first, &writeset)) { + sockets.push_back(ListenerStatus{pair.first, Condition::Writable}); + } + } + + return sockets; +} + +/* }}} */ + +/* + * Poll + * ------------------------------------------------------------------ + */ + +/* {{{ Poll */ + +/* + * Poll implementation + * ------------------------------------------------------------------ + */ + +#if defined(SOCKET_HAVE_POLL) + +#if defined(_WIN32) +# define poll WSAPoll +#endif + +short Poll::toPoll(Condition condition) const noexcept +{ + short result(0); + + if ((condition & Condition::Readable) == Condition::Readable) { + result |= POLLIN; + } + if ((condition & Condition::Writable) == Condition::Writable) { + result |= POLLOUT; + } + + return result; +} + +Condition Poll::toCondition(short &event) const noexcept +{ + Condition condition{Condition::None}; + + /* + * Poll implementations mark the socket differently regarding + * the disconnection of a socket. + * + * At least, even if POLLHUP or POLLIN is set, recv() always + * return 0 so we mark the socket as readable. + */ + if ((event & POLLIN) || (event & POLLHUP)) { + condition |= Condition::Readable; + } + if (event & POLLOUT) { + condition |= Condition::Writable; + } + + /* Reset event for safety */ + event = 0; + + return condition; +} + +void Poll::set(const ListenerTable &, Handle h, Condition condition, bool add) +{ + if (add) { + m_fds.push_back(pollfd{h, toPoll(condition), 0}); + } else { + auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const pollfd &pfd) { + return pfd.fd == h; + }); + + it->events |= toPoll(condition); + } +} + +void Poll::unset(const ListenerTable &, Handle h, Condition condition, bool remove) +{ + auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const pollfd &pfd) { + return pfd.fd == h; + }); + + if (remove) { + m_fds.erase(it); + } else { + it->events &= ~(toPoll(condition)); + } +} + +std::vector<ListenerStatus> Poll::wait(const ListenerTable &, int ms) +{ + auto result = poll(m_fds.data(), m_fds.size(), ms); + if (result == 0) { + throw Error{Error::Timeout, "select", TIMEOUT_MSG}; + } + if (result < 0) { + throw Error{Error::System, "poll"}; + } + + std::vector<ListenerStatus> sockets; + for (auto &fd : m_fds) { + if (fd.revents != 0) { + sockets.push_back(ListenerStatus{fd.fd, toCondition(fd.revents)}); + } + } + + return sockets; +} + +#endif // !SOCKET_HAVE_POLL + +/* }}} */ + +/* + * Epoll implementation + * ------------------------------------------------------------------ + */ + +/* {{{ Epoll */ + +#if defined(SOCKET_HAVE_EPOLL) + +uint32_t Epoll::toEpoll(Condition condition) const noexcept +{ + uint32_t events = 0; + + if ((condition & Condition::Readable) == Condition::Readable) { + events |= EPOLLIN; + } + if ((condition & Condition::Writable) == Condition::Writable) { + events |= EPOLLOUT; + } + + return events; +} + +Condition Epoll::toCondition(uint32_t events) const noexcept +{ + Condition condition{Condition::None}; + + if ((events & EPOLLIN) || (events & EPOLLHUP)) { + condition |= Condition::Readable; + } + if (events & EPOLLOUT) { + condition |= Condition::Writable; + } + + return condition; +} + +void Epoll::update(Handle h, int op, int eflags) +{ + epoll_event ev; + + std::memset(&ev, 0, sizeof (epoll_event)); + + ev.events = eflags; + ev.data.fd = h; + + if (epoll_ctl(m_handle, op, h, &ev) < 0) { + throw Error{Error::System, "epoll_ctl"}; + } +} + +Epoll::Epoll() + : m_handle{epoll_create1(0)} +{ + if (m_handle < 0) { + throw Error{Error::System, "epoll_create"}; + } +} + +Epoll::~Epoll() +{ + close(m_handle); +} + +/* + * For set and unset, we need to apply the whole flags required, so if the socket + * was set to Connection::Readable and user add Connection::Writable, we must + * place both. + */ +void Epoll::set(const ListenerTable &table, Handle sc, Condition condition, bool add) +{ + if (add) { + update(sc, EPOLL_CTL_ADD, toEpoll(condition)); + m_events.resize(m_events.size() + 1); + } else { + update(sc, EPOLL_CTL_MOD, toEpoll(table.at(sc) | condition)); + } +} + +/* + * Unset is a bit complicated case because Listener tells us which + * flag to remove but to update epoll descriptor we need to pass + * the effective flags that we want to be applied. + * + * So we put the same flags that are currently effective and remove the + * requested one. + */ +void Epoll::unset(const ListenerTable &table, Handle sc, Condition condition, bool remove) +{ + if (remove) { + update(sc, EPOLL_CTL_DEL, 0); + m_events.resize(m_events.size() - 1); + } else { + update(sc, EPOLL_CTL_MOD, toEpoll(table.at(sc) & ~(condition))); + } +} + +std::vector<ListenerStatus> Epoll::wait(const ListenerTable &, int ms) +{ + int ret = epoll_wait(m_handle, m_events.data(), m_events.size(), ms); + std::vector<ListenerStatus> result; + + if (ret == 0) { + throw Error{Error::Timeout, "epoll_wait", TIMEOUT_MSG}; + } + if (ret < 0) { + throw Error{Error::System, "epoll_wait"}; + } + + for (int i = 0; i < ret; ++i) { + result.push_back(ListenerStatus{m_events[i].data.fd, toCondition(m_events[i].events)}); + } + + return result; +} + +#endif // !SOCKET_HAVE_EPOLL + +/* }}} */ + +/* + * Kqueue implementation + * ------------------------------------------------------------------ + */ + +/* {{{ Kqueue */ + +#if defined(SOCKET_HAVE_KQUEUE) + +Kqueue::Kqueue() + : m_handle(kqueue()) +{ + if (m_handle < 0) { + throw Error{Error::System, "kqueue"}; + } +} + +Kqueue::~Kqueue() +{ + close(m_handle); +} + +void Kqueue::update(Handle h, int filter, int kflags) +{ + struct kevent ev; + + EV_SET(&ev, h, filter, kflags, 0, 0, nullptr); + + if (kevent(m_handle, &ev, 1, nullptr, 0, nullptr) < 0) { + throw Error{Error::System, "kevent"}; + } +} + +void Kqueue::set(const ListenerTable &, Handle h, Condition condition, bool add) +{ + if ((condition & Condition::Readable) == Condition::Readable) { + update(h, EVFILT_READ, EV_ADD | EV_ENABLE); + } + if ((condition & Condition::Writable) == Condition::Writable) { + update(h, EVFILT_WRITE, EV_ADD | EV_ENABLE); + } + + if (add) { + m_result.resize(m_result.size() + 1); + } +} + +void Kqueue::unset(const ListenerTable &, Handle h, Condition condition, bool remove) +{ + if ((condition & Condition::Readable) == Condition::Readable) { + update(h, EVFILT_READ, EV_DELETE); + } + if ((condition & Condition::Writable) == Condition::Writable) { + update(h, EVFILT_WRITE, EV_DELETE); + } + + if (remove) { + m_result.resize(m_result.size() - 1); + } +} + +std::vector<ListenerStatus> Kqueue::wait(const ListenerTable &, int ms) +{ + std::vector<ListenerStatus> sockets; + timespec ts = { 0, 0 }; + timespec *pts = (ms <= 0) ? nullptr : &ts; + + ts.tv_sec = ms / 1000; + ts.tv_nsec = (ms % 1000) * 1000000; + + int nevents = kevent(m_handle, nullptr, 0, &m_result[0], m_result.capacity(), pts); + + if (nevents == 0) { + throw Error{Error::Timeout, "kevent", TIMEOUT_MSG}; + } + if (nevents < 0) { + throw Error{Error::System, "kevent"}; + } + + for (int i = 0; i < nevents; ++i) { + sockets.push_back(ListenerStatus{ + static_cast<Handle>(m_result[i].ident), + m_result[i].filter == EVFILT_READ ? Condition::Readable : Condition::Writable + }); + } + + return sockets; +} + +#endif // !SOCKET_HAVE_KQUEUE + +/* }}} */ + +} // !net + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/sockets.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,4078 @@ +/* + * sockets.h -- portable C++ socket wrappers + * + * 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 _SOCKETS_H_ +#define _SOCKETS_H_ + +/** + * @file Sockets.h + * @brief Portable socket abstraction + * + * # Introduction + * + * This file is a portable networking library. + * + * ## Options + * + * The user may set the following variables before compiling these files: + * + * ### General options + * + * - **SOCKET_NO_AUTO_INIT**: (bool) Set to 0 if you don't want Socket class to + * automatically calls net::init function and net::finish functions. + * - **SOCKET_NO_SSL**: (bool) Set to 0 if you don't have access to OpenSSL library. + * - **SOCKET_NO_AUTO_SSL_INIT**: (bool) Set to 0 if you don't want Socket class with Tls to automatically init + * the OpenSSL library. You will need to call net::ssl::init and net::ssl::finish. + * + * ### Options for Listener class + * + * Feature detection, multiple implementations may be avaible, for example, Linux has poll, select and epoll. + * + * We assume that `select(2)` is always available. + * + * Of course, you can set the variables yourself if you test it with your build system. + * + * - **SOCKET_HAVE_POLL**: Defined on all BSD, Linux. Also defined on Windows + * if _WIN32_WINNT is set to 0x0600 or greater. + * - **SOCKET_HAVE_KQUEUE**: Defined on all BSD and Apple. + * - **SOCKET_HAVE_EPOLL**: Defined on Linux only. + * - **SOCKET_DEFAULT_BACKEND**: Which backend to use (e.g. `Select`). + * + * The preference priority is ordered from left to right. + * + * | System | Backend | Class name | + * |---------------|-------------------------|--------------| + * | Linux | epoll(7) | Epoll | + * | *BSD | kqueue(2) | Kqueue | + * | Windows | poll(2), select(2) | Poll, Select | + * | Mac OS X | kqueue(2) | Kqueue | + */ + +#if defined(_WIN32) +# if _WIN32_WINNT >= 0x0600 && !defined(SOCKET_HAVE_POLL) +# define SOCKET_HAVE_POLL +# endif +#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) +# if !defined(SOCKET_HAVE_KQUEUE) +# define SOCKET_HAVE_KQUEUE +# endif +# if !defined(SOCKET_HAVE_POLL) +# define SOCKET_HAVE_POLL +# endif +#elif defined(__linux__) +# if !defined(SOCKET_HAVE_EPOLL) +# define SOCKET_HAVE_EPOLL +# endif +# if !defined(SOCKET_HAVE_POLL) +# define SOCKET_HAVE_POLL +# endif +#endif + +/* + * Define SOCKET_DEFAULT_BACKEND + * ------------------------------------------------------------------ + */ + +/** + * Defines the default Listener backend to use. + * + * @note Do not rely on the value shown in doxygen. + */ +#if defined(_WIN32) +# if !defined(SOCKET_DEFAULT_BACKEND) +# if defined(SOCKET_HAVE_POLL) +# define SOCKET_DEFAULT_BACKEND Poll +# else +# define SOCKET_DEFAULT_BACKEND Select +# endif +# endif +#elif defined(__linux__) +# include <sys/epoll.h> + +# if !defined(SOCKET_DEFAULT_BACKEND) +# define SOCKET_DEFAULT_BACKEND Epoll +# endif +#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__APPLE__) +# include <sys/types.h> +# include <sys/event.h> +# include <sys/time.h> + +# if !defined(SOCKET_DEFAULT_BACKEND) +# define SOCKET_DEFAULT_BACKEND Kqueue +# endif +#else +# if !defined(SOCKET_DEFAULT_BACKEND) +# define SOCKET_DEFAULT_BACKEND Select +# endif +#endif + +#if defined(SOCKET_HAVE_POLL) && !defined(_WIN32) +# include <poll.h> +#endif + +/* + * Headers to include + * ------------------------------------------------------------------ + */ + +#if defined(_WIN32) +# include <cstdlib> + +# include <WinSock2.h> +# include <WS2tcpip.h> +#else +# include <cerrno> + +# include <sys/ioctl.h> +# include <sys/types.h> +# include <sys/socket.h> +# include <sys/un.h> + +# include <arpa/inet.h> + +# include <netinet/in.h> +# include <netinet/tcp.h> + +# include <fcntl.h> +# include <netdb.h> +# include <unistd.h> +#endif + +#if !defined(SOCKET_NO_SSL) +# include <openssl/err.h> +# include <openssl/evp.h> +# include <openssl/ssl.h> +#endif + +#include <cassert> +#include <chrono> +#include <cstdlib> +#include <cstring> +#include <exception> +#include <functional> +#include <map> +#include <memory> +#include <string> +#include <vector> + +namespace irccd { + +/** + * General network namespace. + */ +namespace net { + +/* + * Portables types + * ------------------------------------------------------------------ + * + * The following types are defined differently between Unix and Windows. + */ + +/* {{{ Protocols */ + +#if defined(_WIN32) + +/** + * Socket type, SOCKET. + */ +using Handle = SOCKET; + +/** + * Argument to pass to set. + */ +using ConstArg = const char *; + +/** + * Argument to pass to get. + */ +using Arg = char *; + +#else + +/** + * Socket type, int. + */ +using Handle = int; + +/** + * Argument to pass to set. + */ +using ConstArg = const void *; + +/** + * Argument to pass to get. + */ +using Arg = void *; + +#endif + +/* }}} */ + +/* + * Portable constants + * ------------------------------------------------------------------ + * + * These constants are needed to check functions return codes, they are rarely needed in end user code. + */ + +/* {{{ Constants */ + +/* + * The following constants are defined differently from Unix + * to Windows. + */ +#if defined(_WIN32) + +/** + * Socket creation failure or invalidation. + */ +extern const Handle Invalid; + +/** + * Socket operation failure. + */ +extern const int Failure; + +#else + +/** + * Socket creation failure or invalidation. + */ +extern const int Invalid; + +/** + * Socket operation failure. + */ +extern const int Failure; + +#endif + +#if !defined(SOCKET_NO_SSL) + +namespace ssl { + +/** + * @enum Method + * @brief Which OpenSSL method to use. + */ +enum Method { + Tlsv1, //!< TLS v1.2 (recommended) + Sslv3 //!< SSLv3 +}; + +} // !ssl + +#endif + +/* }}} */ + +/* + * Portable functions + * ------------------------------------------------------------------ + * + * The following free functions can be used to initialize the library or to get the last system error. + */ + +/* {{{ Functions */ + +/** + * Initialize the socket library. Except if you defined SOCKET_NO_AUTO_INIT, you don't need to call this + * function manually. + */ +void init() noexcept; + +/** + * Close the socket library. + */ +void finish() noexcept; + +#if !defined(SOCKET_NO_SSL) + +/** + * OpenSSL namespace. + */ +namespace ssl { + +/** + * Initialize the OpenSSL library. Except if you defined SOCKET_NO_AUTO_SSL_INIT, you don't need to call this function + * manually. + */ +void init() noexcept; + +/** + * Close the OpenSSL library. + */ +void finish() noexcept; + +} // !ssl + +#endif // SOCKET_NO_SSL + +/** + * Get the last socket system error. The error is set from errno or from + * WSAGetLastError on Windows. + * + * @return a string message + */ +std::string error(); + +/** + * Get the last system error. + * + * @param errn the error number (errno or WSAGetLastError) + * @return the error + */ +std::string error(int errn); + +/* }}} */ + +/* + * Error class + * ------------------------------------------------------------------ + * + * This is the main exception thrown on socket operations. + */ + +/* {{{ Error */ + +/** + * @class Error + * @brief Base class for sockets error + */ +class Error : public std::exception { +public: + /** + * @enum Code + * @brief Which kind of error + */ + enum Code { + Timeout, ///!< The action did timeout + System, ///!< There is a system error + Other ///!< Other custom error + }; + +private: + Code m_code; + std::string m_function; + std::string m_error; + +public: + /** + * Constructor that use the last system error. + * + * @param code which kind of error + * @param function the function name + */ + Error(Code code, std::string function); + + /** + * Constructor that use the system error set by the user. + * + * @param code which kind of error + * @param function the function name + * @param error the error + */ + Error(Code code, std::string function, int error); + + /** + * Constructor that set the error specified by the user. + * + * @param code which kind of error + * @param function the function name + * @param error the error + */ + Error(Code code, std::string function, std::string error); + + /** + * Get which function has triggered the error. + * + * @return the function name (e.g connect) + */ + inline const std::string &function() const noexcept + { + return m_function; + } + + /** + * The error code. + * + * @return the code + */ + inline Code code() const noexcept + { + return m_code; + } + + /** + * Get the error (only the error content). + * + * @return the error + */ + const char *what() const noexcept + { + return m_error.c_str(); + } +}; + +/* }}} */ + +/* + * State class + * ------------------------------------------------------------------ + * + * To facilitate higher-level stuff, the socket has a state. + */ + +/* {{{ State */ + +/** + * @enum State + * @brief Current socket state. + */ +enum class State { + Open, //!< Socket is open + Bound, //!< Socket is bound to an address + Connecting, //!< The connection is in progress + Connected, //!< Connection is complete + Accepted, //!< Socket has been accepted (client) + Accepting, //!< The client acceptation is in progress + Closed, //!< The socket has been closed + Disconnected, //!< The connection was lost +}; + +/* }}} */ + +/* + * Action enum + * ------------------------------------------------------------------ + * + * Defines the pending operation. + */ + +/* {{{ Action */ + +/** + * @enum Action + * @brief Define the current operation that must complete. + * + * Some operations like accept, connect, recv or send must sometimes do several round-trips to complete and the socket + * action is set with that enumeration. The user is responsible of calling accept, connect send or recv until the + * operation is complete. + * + * Note: the user must wait for the appropriate condition in Socket::condition to check if the required condition is + * met. + * + * It is important to complete the operation in the correct order because protocols like Tls may require to continue + * re-negociating when calling Socket::send or Socket::Recv. + */ +enum class Action { + None, //!< No action is required, socket is ready + Accept, //!< The socket is not yet accepted, caller must call accept() again + Connect, //!< The socket is not yet connected, caller must call connect() again + Receive, //!< The received operation has not succeeded yet, caller must call recv() or recvfrom() again + Send //!< The send operation has not succeded yet, caller must call send() or sendto() again +}; + +/* }}} */ + +/* + * Condition enum + * ------------------------------------------------------------------ + * + * Defines if we must wait for reading or writing. + */ + +/* {{{ Condition */ + +/** + * @enum Condition + * @brief Define the required condition for the socket. + * + * As explained in Action enumeration, some operations required to be called several times, before calling these + * operations, the user must wait the socket to be readable or writable. This can be checked with Socket::condition. + */ +enum class Condition { + None, //!< No condition is required + Readable = (1 << 0), //!< The socket must be readable + Writable = (1 << 1) //!< The socket must be writable +}; + +/** + * Apply bitwise XOR. + * + * @param v1 the first value + * @param v2 the second value + * @return the new value + */ +constexpr Condition operator^(Condition v1, Condition v2) noexcept +{ + return static_cast<Condition>(static_cast<int>(v1) ^ static_cast<int>(v2)); +} + +/** + * Apply bitwise AND. + * + * @param v1 the first value + * @param v2 the second value + * @return the new value + */ +constexpr Condition operator&(Condition v1, Condition v2) noexcept +{ + return static_cast<Condition>(static_cast<int>(v1) & static_cast<int>(v2)); +} + +/** + * Apply bitwise OR. + * + * @param v1 the first value + * @param v2 the second value + * @return the new value + */ +constexpr Condition operator|(Condition v1, Condition v2) noexcept +{ + return static_cast<Condition>(static_cast<int>(v1) | static_cast<int>(v2)); +} + +/** + * Apply bitwise NOT. + * + * @param v the value + * @return the complement + */ +constexpr Condition operator~(Condition v) noexcept +{ + return static_cast<Condition>(~static_cast<int>(v)); +} + +/** + * Assign bitwise OR. + * + * @param v1 the first value + * @param v2 the second value + * @return the new value + */ +inline Condition &operator|=(Condition &v1, Condition v2) noexcept +{ + v1 = static_cast<Condition>(static_cast<int>(v1) | static_cast<int>(v2)); + + return v1; +} + +/** + * Assign bitwise AND. + * + * @param v1 the first value + * @param v2 the second value + * @return the new value + */ +inline Condition &operator&=(Condition &v1, Condition v2) noexcept +{ + v1 = static_cast<Condition>(static_cast<int>(v1) & static_cast<int>(v2)); + + return v1; +} + +/** + * Assign bitwise XOR. + * + * @param v1 the first value + * @param v2 the second value + * @return the new value + */ +inline Condition &operator^=(Condition &v1, Condition v2) noexcept +{ + v1 = static_cast<Condition>(static_cast<int>(v1) ^ static_cast<int>(v2)); + + return v1; +} + +/* }}} */ + +/* + * Base Socket class + * ------------------------------------------------------------------ + * + * This base class has operations that are common to all types of sockets but you usually instanciate + * a SocketTcp or SocketUdp + */ + +/* {{{ Socket */ + +/** + * @class Socket + * @brief Base socket class for socket operations. + * + * **Important:** When using non-blocking sockets, some considerations must be taken. See the implementation of the + * underlying protocol for more details. + * + * @see protocol::Tls + * @see protocol::Tcp + * @see protocol::Udp + */ +template <typename Address, typename Protocol> +class Socket { +private: + Protocol m_proto; + State m_state{State::Closed}; + Action m_action{Action::None}; + Condition m_condition{Condition::None}; + +protected: + /** + * The native handle. + */ + Handle m_handle{Invalid}; + +public: + /** + * Create a socket handle. + * + * This is the primary function and the only one that creates the socket handle, all other constructors + * are just overloaded functions. + * + * @param domain the domain AF_* + * @param type the type SOCK_* + * @param protocol the protocol + * @param iface the implementation + * @throw net::Error on errors + * @post state is set to Open + * @post handle is not set to Invalid + */ + Socket(int domain, int type, int protocol, Protocol iface = {}) + : m_proto(std::move(iface)) + { +#if !defined(SOCKET_NO_AUTO_INIT) + init(); +#endif + m_handle = ::socket(domain, type, protocol); + + if (m_handle == Invalid) { + throw Error{Error::System, "socket"}; + } + + m_proto.create(*this); + m_state = State::Open; + + assert(m_handle != Invalid); + } + + /** + * This tries to create a socket. + * + * Domain and type are determined by the Address and Protocol object. + * + * @param protocol the protocol + * @param address which type of address + * @throw net::Error on errors + */ + explicit inline Socket(Protocol protocol = {}, const Address &address = {}) + : Socket{address.domain(), protocol.type(), 0, std::move(protocol)} + { + } + + /** + * Construct a socket with an already created descriptor. + * + * @param handle the native descriptor + * @param state specify the socket state + * @param protocol the type of socket implementation + * @post action is set to None + * @post condition is set to None + */ + explicit inline Socket(Handle handle, State state = State::Closed, Protocol protocol = {}) noexcept + : m_proto(std::move(protocol)) + , m_state{state} + , m_handle{handle} + { + assert(m_action == Action::None); + assert(m_condition == Condition::None); + } + + /** + * Create an invalid socket. Can be used when you cannot instanciate the socket immediately. + */ + explicit inline Socket(std::nullptr_t) noexcept + : m_handle{Invalid} + { + } + + /** + * Copy constructor deleted. + */ + Socket(const Socket &) = delete; + + /** + * Transfer ownership from other to this. + * + * @param other the other socket + */ + inline Socket(Socket &&other) noexcept + : m_proto(std::move(other.m_proto)) + , m_state{other.m_state} + , m_action{other.m_action} + , m_condition{other.m_condition} + , m_handle{other.m_handle} + { + /* Invalidate other */ + other.m_handle = Invalid; + other.m_state = State::Closed; + other.m_action = Action::None; + other.m_condition = Condition::None; + } + + /** + * Default destructor. + */ + virtual ~Socket() + { + close(); + } + + /** + * Access the implementation. + * + * @return the implementation + * @warning use this function with care + */ + inline const Protocol &protocol() const noexcept + { + return m_proto; + } + + /** + * Overloaded function. + * + * @return the implementation + */ + inline Protocol &protocol() noexcept + { + return m_proto; + } + + /** + * Get the current socket state. + * + * @return the state + */ + inline State state() const noexcept + { + return m_state; + } + + /** + * Change the current socket state. + * + * @param state the new state + * @warning only implementations should call this function + */ + inline void setState(State state) noexcept + { + m_state = state; + } + + /** + * Get the pending operation. + * + * @return the action to complete before continuing + * @note usually only needed in non-blocking sockets + */ + inline Action action() const noexcept + { + return m_action; + } + + /** + * Change the pending operation. + * + * @param action the action + * @warning you should not call this function yourself + */ + inline void setAction(Action action) noexcept + { + m_action = action; + } + + /** + * Get the condition to wait for. + * + * @return the condition + */ + inline Condition condition() const noexcept + { + return m_condition; + } + + /** + * Change the condition required. + * + * @param condition the condition + * @warning you should not call this function yourself + */ + inline void setCondition(Condition condition) noexcept + { + m_condition = condition; + } + + /** + * Set an option for the socket. Wrapper of setsockopt(2). + * + * @param level the setting level + * @param name the name + * @param arg the value + * @throw net::Error on errors + */ + template <typename Argument> + void set(int level, int name, const Argument &arg) + { + if (setsockopt(m_handle, level, name, (ConstArg)&arg, sizeof (arg)) == Failure) { + throw Error{Error::System, "set"}; + } + } + + /** + * Object-oriented option setter. + * + * The object must have `set(Socket<Address, Protocol> &) const`. + * + * @param option the option + * @throw net::Error on errors + */ + template <typename Option> + inline void set(const Option &option) + { + option.set(*this); + } + + /** + * Get an option for the socket. Wrapper of getsockopt(2). + * + * @param level the setting level + * @param name the name + * @throw net::Error on errors + */ + template <typename Argument> + Argument get(int level, int name) + { + Argument desired, result{}; + socklen_t size = sizeof (result); + + if (getsockopt(m_handle, level, name, (Arg)&desired, &size) == Failure) { + throw Error{Error::System, "get"}; + } + + std::memcpy(&result, &desired, size); + + return result; + } + + /** + * Object-oriented option getter. + * + * The object must have `T get(Socket<Address, Protocol> &) const`, T can be any type and it is the value + * returned from this function. + * + * @return the same value as get() in the option + * @throw net::Error on errors + */ + template <typename Option> + inline auto get() -> decltype(std::declval<Option>().get(*this)) + { + return Option{}.get(*this); + } + + /** + * Get the native handle. + * + * @return the handle + * @warning Not portable + */ + inline Handle handle() const noexcept + { + return m_handle; + } + + /** + * Bind using a native address. + * + * @param address the address + * @param length the size + * @pre state must not be Bound + * @throw net::Error on errors + */ + void bind(const sockaddr *address, socklen_t length) + { + assert(m_state != State::Bound); + + if (::bind(m_handle, address, length) == Failure) { + throw Error{Error::System, "bind"}; + } + + m_state = State::Bound; + } + + /** + * Overload that takes an address. + * + * @param address the address + * @throw net::Error on errors + */ + inline void bind(const Address &address) + { + bind(address.address(), address.length()); + } + + /** + * Listen for pending connection. + * + * @param max the maximum number + * @pre state must be Bound + * @throw net::Error on errors + */ + inline void listen(int max = 128) + { + assert(m_state == State::Bound); + + if (::listen(this->m_handle, max) == Failure) { + throw Error{Error::System, "listen"}; + } + } + + /** + * Connect to the address. + * + * If connection cannot be established immediately, connect with no argument must be called again. See + * the underlying protocol for more information. + * + * @pre state must be State::Open + * @param address the address + * @param length the the address length + * @throw net::Error on errors + * @post state is set to State::Connecting or State::Connected + * @note For non-blocking sockets, see the underlying protocol function for more details + */ + void connect(const sockaddr *address, socklen_t length) + { + assert(m_state == State::Open); + + m_action = Action::None; + m_condition = Condition::None; + + m_proto.connect(*this, address, length); + + assert((m_state == State::Connected && m_action == Action::None && m_condition == Condition::None) || + (m_state == State::Connecting && m_action == Action::Connect && m_condition != Condition::None)); + } + + /** + * Overloaded function. + * + * Effectively call connect(address.address(), address.length()); + * + * @param address the address + */ + inline void connect(const Address &address) + { + connect(address.address(), address.length()); + } + + /** + * Continue the connection, only required with non-blocking sockets. + * + * @pre state must be State::Connecting + * @throw net::Error on errors + */ + void connect() + { + assert(m_state == State::Connecting); + + m_action = Action::None; + m_condition = Condition::None; + + m_proto.connect(*this); + + assert((m_state == State::Connected && m_action == Action::None && m_condition == Condition::None) || + (m_state == State::Connecting && m_action == Action::Connect && m_condition != Condition::None)); + } + + /** + * Accept a new client. If there are no pending connection, throws an error. + * + * If the client cannot be accepted immediately, the client is returned and accept with no arguments + * must be called on it. See the underlying protocol for more information. + * + * @pre state must be State::Bound + * @param info the address where to store client's information (optional) + * @return the new socket + * @throw Error on errors + * @post returned client's state is set to State::Accepting or State::Accepted + * @note For non-blocking sockets, see the underlying protocol function for more details + */ + Socket<Address, Protocol> accept(Address *info) + { + assert(m_state == State::Bound); + + m_action = Action::None; + m_condition = Condition::None; + + sockaddr_storage storage; + socklen_t length = sizeof (storage); + + Socket<Address, Protocol> sc = m_proto.accept(*this, reinterpret_cast<sockaddr *>(&storage), &length); + + if (info) { + *info = Address{&storage, length}; + } + + /* Master do not change */ + assert(m_state == State::Bound); + assert(m_action == Action::None); + assert(m_condition == Condition::None); + + /* Client */ + assert( + (sc.state() == State::Accepting && sc.action() == Action::Accept && sc.condition() != Condition::None) || + (sc.state() == State::Accepted && sc.action() == Action::None && sc.condition() == Condition::None) + ); + + return sc; + } + + /** + * Continue the accept process on this client. This function must be called only when the socket is + * ready to be readable or writable! (see condition). + * + * @pre state must be State::Accepting + * @throw Error on errors + * @post if connection is complete, state is changed to State::Accepted, action and condition are unset + * @post if connection is still in progress, condition is set + */ + void accept() + { + assert(m_state == State::Accepting); + + m_action = Action::None; + m_condition = Condition::None; + + m_proto.accept(*this); + + assert( + (m_state == State::Accepting && m_action == Action::Accept && m_condition != Condition::None) || + (m_state == State::Accepted && m_action == Action::None && m_condition == Condition::None) + ); + } + + /** + * Get the local name. This is a wrapper of getsockname(). + * + * @return the address + * @throw Error on failures + * @pre state() must not be State::Closed + */ + Address address() const + { + assert(m_state != State::Closed); + + sockaddr_storage ss; + socklen_t length = sizeof (sockaddr_storage); + + if (::getsockname(m_handle, (sockaddr *)&ss, &length) == Failure) { + throw Error{Error::System, "getsockname"}; + } + + return Address(&ss, length); + } + + /** + * Receive some data. + * + * If the operation cannot be complete immediately, 0 is returned and user must call the function + * again when ready. See the underlying protocol for more information. + * + * If action is set to Action::None and result is set to 0, disconnection occured. + * + * @param data the destination buffer + * @param length the buffer length + * @pre action must not be Action::Send + * @return the number of bytes received or 0 + * @throw Error on error + * @note For non-blocking sockets, see the underlying protocol function for more details + */ + unsigned recv(void *data, unsigned length) + { + assert(m_action != Action::Send); + + m_action = Action::None; + m_condition = Condition::None; + + unsigned nbread = m_proto.recv(*this, data, length); + + return nbread; + } + + /** + * Overloaded function. + * + * @param count the number of bytes to receive + * @return the string + * @throw Error on error + */ + inline std::string recv(unsigned count) + { + std::string result; + + result.resize(count); + auto n = recv(const_cast<char *>(result.data()), count); + result.resize(n); + + return result; + } + + /** + * Send some data. + * + * If the operation cannot be complete immediately, 0 is returned and user must call the function + * again when ready. See the underlying protocol for more information. + * + * @param data the data buffer + * @param length the buffer length + * @return the number of bytes sent or 0 + * @pre action() must not be Flag::Receive + * @throw Error on error + * @note For non-blocking sockets, see the underlying protocol function for more details + */ + unsigned send(const void *data, unsigned length) + { + assert(m_action != Action::Receive); + + m_action = Action::None; + m_condition = Condition::None; + + unsigned nbsent = m_proto.send(*this, data, length); + + assert((m_action == Action::None && m_condition == Condition::None) || + (m_action == Action::Send && m_condition != Condition::None)); + + return nbsent; + } + + /** + * Overloaded function. + * + * @param data the string to send + * @return the number of bytes sent + * @throw Error on error + */ + inline unsigned send(const std::string &data) + { + return send(data.c_str(), data.size()); + } + + /** + * Send data to an end point. + * + * If the operation cannot be complete immediately, 0 is returned and user must call the function + * again when ready. See the underlying protocol for more information. + * + * @param data the buffer + * @param length the buffer length + * @param address the client address + * @param addrlen the address length + * @return the number of bytes sent + * @throw net::Error on errors + * @note For non-blocking sockets, see the underlying protocol function for more details + */ + inline unsigned sendto(const void *data, unsigned length, const sockaddr *address, socklen_t addrlen) + { + return m_proto.sendto(*this, data, length, address, addrlen); + } + + /** + * Overloaded function. + * + * @param data the buffer + * @param length the buffer length + * @param address the destination + * @return the number of bytes sent + * @throw net::Error on errors + */ + inline unsigned sendto(const void *data, unsigned length, const Address &address) + { + return sendto(data, length, address.address(), address.length()); + } + + /** + * Overloaded function. + * + * @param data the data + * @param address the address + * @return the number of bytes sent + * @throw net:;Error on errors + */ + inline unsigned sendto(const std::string &data, const Address &address) + { + return sendto(data.c_str(), data.length(), address); + } + + /** + * Receive data from an end point. + * + * If the operation cannot be complete immediately, 0 is returned and user must call the function + * again when ready. See the underlying protocol for more information. + * + * @param data the destination buffer + * @param length the buffer length + * @param address the address destination + * @param addrlen the address length (in/out) + * @return the number of bytes received + * @throw net::Error on errors + * @note For non-blocking sockets, see the underlying protocol function for more details + */ + inline unsigned recvfrom(void *data, unsigned length, sockaddr *address, socklen_t *addrlen) + { + return m_proto.recvfrom(*this, data, length, address, addrlen); + } + + /** + * Overloaded function. + * + * @param data the destination buffer + * @param length the buffer length + * @param info the address destination + * @return the number of bytes received + * @throw net::Error on errors + */ + inline unsigned recvfrom(void *data, unsigned length, Address *info = nullptr) + { + sockaddr_storage storage; + socklen_t addrlen = sizeof (sockaddr_storage); + + auto n = recvfrom(data, length, reinterpret_cast<sockaddr *>(&storage), &addrlen); + + if (info && n != 0) { + *info = Address{&storage, addrlen}; + } + + return n; + } + + /** + * Overloaded function. + * + * @param count the maximum number of bytes to receive + * @param info the client information + * @return the string + * @throw net::Error on errors + */ + std::string recvfrom(unsigned count, Address *info = nullptr) + { + std::string result; + + result.resize(count); + auto n = recvfrom(const_cast<char *>(result.data()), count, info); + result.resize(n); + + return result; + } + + /** + * Close the socket. + * + * Automatically called from the destructor. + */ + void close() + { + if (m_handle != Invalid) { +#if defined(_WIN32) + ::closesocket(m_handle); +#else + ::close(m_handle); +#endif + m_handle = Invalid; + } + + m_state = State::Closed; + m_action = Action::None; + m_condition = Condition::None; + } + + /** + * Assignment operator forbidden. + * + * @return *this + */ + Socket &operator=(const Socket &) = delete; + + /** + * Transfer ownership from other to this. The other socket is left + * invalid and will not be closed. + * + * @param other the other socket + * @return this + */ + Socket &operator=(Socket &&other) noexcept + { + m_handle = other.m_handle; + m_proto = std::move(other.m_proto); + m_state = other.m_state; + m_action = other.m_action; + m_condition = other.m_condition; + + /* Invalidate other */ + other.m_handle = Invalid; + other.m_state = State::Closed; + other.m_action = Action::None; + other.m_condition = Condition::None; + + return *this; + } +}; + +/** + * Compare two sockets. + * + * @param s1 the first socket + * @param s2 the second socket + * @return true if they equals + */ +template <typename Address, typename Protocol> +bool operator==(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2) +{ + return s1.handle() == s2.handle(); +} + +/** + * Compare two sockets. + * + * @param s1 the first socket + * @param s2 the second socket + * @return true if they are different + */ +template <typename Address, typename Protocol> +bool operator!=(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2) +{ + return s1.handle() != s2.handle(); +} + +/** + * Compare two sockets. + * + * @param s1 the first socket + * @param s2 the second socket + * @return true if s1 < s2 + */ +template <typename Address, typename Protocol> +bool operator<(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2) +{ + return s1.handle() < s2.handle(); +} + +/** + * Compare two sockets. + * + * @param s1 the first socket + * @param s2 the second socket + * @return true if s1 > s2 + */ +template <typename Address, typename Protocol> +bool operator>(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2) +{ + return s1.handle() > s2.handle(); +} + +/** + * Compare two sockets. + * + * @param s1 the first socket + * @param s2 the second socket + * @return true if s1 <= s2 + */ +template <typename Address, typename Protocol> +bool operator<=(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2) +{ + return s1.handle() <= s2.handle(); +} + +/** + * Compare two sockets. + * + * @param s1 the first socket + * @param s2 the second socket + * @return true if s1 >= s2 + */ +template <typename Address, typename Protocol> +bool operator>=(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2) +{ + return s1.handle() >= s2.handle(); +} + +/* }}} */ + +/* + * Predefined options + * ------------------------------------------------------------------ + */ + +/* {{{ Options */ + +/** + * Namespace of predefined options. + */ +namespace option { + +/* + * Options for socket + * ------------------------------------------------------------------ + */ + +/* {{{ Options for socket */ + +/** + * @class SockBlockMode + * @brief Set or get the blocking-mode for a socket. + * @warning On Windows, it's not possible to check if the socket is blocking or not. + */ +class SockBlockMode { +public: + /** + * Set to false if you want non-blocking socket. + */ + bool value{false}; + + /** + * Set the option. + * + * @param sc the socket + * @throw Error on errors + */ + template <typename Address, typename Protocol> + void set(Socket<Address, Protocol> &sc) const + { +#if defined(O_NONBLOCK) && !defined(_WIN32) + int flags; + + if ((flags = fcntl(sc.handle(), F_GETFL, 0)) < 0) { + flags = 0; + } + + if (value) { + flags &= ~(O_NONBLOCK); + } else { + flags |= O_NONBLOCK; + } + + if (fcntl(sc.handle(), F_SETFL, flags) < 0) { + throw Error{Error::System, "fcntl"}; + } +#else + unsigned long flags = (value) ? 0 : 1; + + if (ioctlsocket(sc.handle(), FIONBIO, &flags) == Failure) { + throw Error{Error::System, "fcntl"}; + } +#endif + } + + /** + * Get the option. + * + * @return the value + * @throw Error on errors + */ + template <typename Address, typename Protocol> + bool get(Socket<Address, Protocol> &sc) const + { +#if defined(O_NONBLOCK) && !defined(_WIN32) + int flags = fcntl(sc.handle(), F_GETFL, 0); + + if (flags < 0) { + throw Error{Error::System, "fcntl"}; + } + + return !(flags & O_NONBLOCK); +#else + throw Error{Error::Other, "get", "Windows API cannot let you get the blocking status of a socket"}; +#endif + } +}; + +/** + * @class SockReuseAddress + * @brief Reuse address, must be used before calling Socket::bind + */ +class SockReuseAddress { +public: + /** + * Set to true if you want to set the SOL_SOCKET/SO_REUSEADDR option. + */ + bool value{true}; + + /** + * Set the option. + * + * @param sc the socket + * @throw Error on errors + */ + template <typename Address, typename Protocol> + inline void set(Socket<Address, Protocol> &sc) const + { + sc.set(SOL_SOCKET, SO_REUSEADDR, value ? 1 : 0); + } + + /** + * Get the option. + * + * @return the value + * @throw Error on errors + */ + template <typename Address, typename Protocol> + inline bool get(Socket<Address, Protocol> &sc) const + { + return static_cast<bool>(sc.template get<int>(SOL_SOCKET, SO_REUSEADDR)); + } +}; + +/** + * @class SockSendBuffer + * @brief Set or get the output buffer. + */ +class SockSendBuffer { +public: + /** + * Set to the buffer size. + */ + int value{2048}; + + /** + * Set the option. + * + * @param sc the socket + * @throw Error on errors + */ + template <typename Address, typename Protocol> + inline void set(Socket<Address, Protocol> &sc) const + { + sc.set(SOL_SOCKET, SO_SNDBUF, value); + } + + /** + * Get the option. + * + * @return the value + * @throw Error on errors + */ + template <typename Address, typename Protocol> + inline int get(Socket<Address, Protocol> &sc) const + { + return sc.template get<int>(SOL_SOCKET, SO_SNDBUF); + } +}; + +/** + * @class SockReceiveBuffer + * @brief Set or get the input buffer. + */ +class SockReceiveBuffer { +public: + /** + * Set to the buffer size. + */ + int value{2048}; + + /** + * Set the option. + * + * @param sc the socket + * @throw Error on errors + */ + template <typename Address, typename Protocol> + inline void set(Socket<Address, Protocol> &sc) const + { + sc.set(SOL_SOCKET, SO_RCVBUF, value); + } + + /** + * Get the option. + * + * @return the value + * @throw Error on errors + */ + template <typename Address, typename Protocol> + inline int get(Socket<Address, Protocol> &sc) const + { + return sc.template get<int>(SOL_SOCKET, SO_RCVBUF); + } +}; + +/* }}} */ + +/** + * @class TcpNoDelay + * @brief Set this option if you want to disable nagle's algorithm. + */ +class TcpNoDelay { +public: + /** + * Set to true to set TCP_NODELAY option. + */ + bool value{true}; + + /** + * Set the option. + * + * @param sc the socket + * @throw Error on errors + */ + template <typename Address, typename Protocol> + inline void set(Socket<Address, Protocol> &sc) const + { + sc.set(IPPROTO_TCP, TCP_NODELAY, value ? 1 : 0); + } + + /** + * Get the option. + * + * @return the value + * @throw Error on errors + */ + template <typename Address, typename Protocol> + inline bool get(Socket<Address, Protocol> &sc) const + { + return static_cast<bool>(sc.template get<int>(IPPROTO_TCP, TCP_NODELAY)); + } +}; + +/** + * @class Ipv6Only + * @brief Control IPPROTO_IPV6/IPV6_V6ONLY + * + * Note: some systems may or not set this option by default so it's a good idea to set it in any case to either + * false or true if portability is a concern. + */ +class Ipv6Only { +public: + /** + * Set this to use only IPv6. + */ + bool value{true}; + + /** + * Set the option. + * + * @param sc the socket + * @throw Error on errors + */ + template <typename Address, typename Protocol> + inline void set(Socket<Address, Protocol> &sc) const + { + sc.set(IPPROTO_IPV6, IPV6_V6ONLY, value ? 1 : 0); + } + + /** + * Get the option. + * + * @return the value + * @throw Error on errors + */ + template <typename Address, typename Protocol> + inline bool get(Socket<Address, Protocol> &sc) const + { + return static_cast<bool>(sc.template get<int>(IPPROTO_IPV6, IPV6_V6ONLY)); + } +}; + +} // !option + +/* }}} */ + +/* + * Predefined addressed to be used + * ------------------------------------------------------------------ + * + * - Ip, + * - Local. + */ + +/* {{{ Addresses */ + +/** + * Set of predefined addresses. + */ +namespace address { + +/** + * @class Ip + * @brief Base class for IPv6 and IPv4, you can use it if you don't know in advance if you'll use IPv6 or IPv4. + */ +class Ip { +public: + /** + * @enum Type + * @brief Type of ip address. + */ + enum Type { + v4 = AF_INET, //!< AF_INET + v6 = AF_INET6 //!< AF_INET6 + }; + +private: + /* + * Default domain when using default constructors. + * + * Note: AF_INET or AF_INET6, not + */ + static int m_default; + + union { + sockaddr_in m_sin; + sockaddr_in6 m_sin6; + }; + + socklen_t m_length{0}; + int m_domain{AF_INET}; + +public: + /** + * Set the default domain to use when using default Ip constructor. By default, AF_INET is used. + * + * @pre domain must be Type::v4 or Type::v6 + */ + static inline void setDefault(Type domain) noexcept + { + assert(domain == Type::v4 || domain == Type::v6); + + m_default = static_cast<int>(domain); + } + + /** + * Construct using the default domain. + */ + inline Ip() noexcept + : Ip(static_cast<Type>(m_default)) + { + } + + /** + * Default initialize the Ip domain. + * + * @pre domain must be AF_INET or AF_INET6 only + * @param domain the domain (AF_INET or AF_INET6) + */ + Ip(Type domain) noexcept; + + /** + * Construct an address suitable for bind() or connect(). + * + * @pre domain must be Type::v4 or Type::v6 + * @param domain the domain (AF_INET or AF_INET6) + * @param host the host (* for any) + * @param port the port number + * @throw Error on errors + */ + Ip(const std::string &host, int port, Type domain = v4); + + /** + * Construct an address from a storage. + * + * @pre storage's domain must be AF_INET or AF_INET6 only + * @param ss the storage + * @param length the length + */ + Ip(const sockaddr_storage *ss, socklen_t length) noexcept; + + /** + * Get the domain (AF_INET or AF_INET6). + * + * @return the domain + */ + inline int domain() const noexcept + { + return m_domain; + } + + /** + * Return the underlying address, either sockaddr_in6 or sockaddr_in. + * + * @return the address + */ + inline const sockaddr *address() const noexcept + { + if (m_domain == AF_INET6) { + return reinterpret_cast<const sockaddr *>(&m_sin6); + } + + return reinterpret_cast<const sockaddr *>(&m_sin); + } + + /** + * Return the underlying address length. + * + * @return the length + */ + inline socklen_t length() const noexcept + { + return m_length; + } + + /** + * Get the port. + * + * @return the port + */ + inline int port() const noexcept + { + if (m_domain == AF_INET6) { + return ntohs(m_sin6.sin6_port); + } + + return ntohs(m_sin.sin_port); + } +}; + +#if !defined(_WIN32) + +/** + * @class Local + * @brief unix family sockets + * + * Create an address to a specific path. Only available on Unix. + */ +class Local { +private: + sockaddr_un m_sun; + std::string m_path; + +public: + /** + * Get the domain AF_LOCAL. + * + * @return AF_LOCAL + */ + inline int domain() const noexcept + { + return AF_LOCAL; + } + + /** + * Default constructor. + */ + Local() noexcept; + + /** + * Construct an address to a path. + * + * @param path the path + * @param rm remove the file before (default: false) + */ + Local(std::string path, bool rm = false) noexcept; + + /** + * Construct an unix address from a storage address. + * + * @pre storage's domain must be AF_LOCAL + * @param ss the storage + * @param length the length + */ + Local(const sockaddr_storage *ss, socklen_t length) noexcept; + + /** + * Get the sockaddr_un. + * + * @return the address + */ + inline const sockaddr *address() const noexcept + { + return reinterpret_cast<const sockaddr *>(&m_sun); + } + + /** + * Get the address length. + * + * @return the length + */ + inline socklen_t length() const noexcept + { +#if defined(SOCKET_HAVE_SUN_LEN) + return SUN_LEN(&m_sun); +#else + return sizeof (m_sun); +#endif + } +}; + +#endif // !_WIN32 + +} // !address + +/* }}} */ + +/* + * Predefined protocols + * ------------------------------------------------------------------ + * + * - Tcp, for standard stream connections, + * - Udp, for standard datagram connections, + * - Tls, for secure stream connections. + */ + +/* {{{ Protocols */ + +/** + * Set of predefined protocols. + */ +namespace protocol { + +/* {{{ Tcp */ + +/** + * @class Tcp + * @brief Clear TCP implementation. + * + * This is the basic TCP protocol that implements recv, send, connect and accept as wrappers of the usual + * C functions. + */ +class Tcp { +public: + /** + * Socket type. + * + * @return SOCK_STREAM + */ + inline int type() const noexcept + { + return SOCK_STREAM; + } + + /** + * Do nothing. + * + * This function is just present for compatibility, it should never be called. + */ + template <typename Address> + inline void create(Socket<Address, Tcp> &) const noexcept + { + /* No-op */ + } + + /** + * Standard connect. + * + * If the socket is marked non-blocking and the connection cannot be established immediately, then the + * following is true: + * + * - state is set to State::Connecting, + * - action is set to Action::Connect, + * - condition is set to Condition::Writable. + * + * Then the user must wait until the socket is writable and call connect() with 0 arguments. + * + * If the socket is blocking, this function blocks until the connection is complete or an error occurs, in + * that case state is either set to State::Connected or State::Disconnected but action and condition are + * not set. + * + * @param sc the socket + * @param address the address + * @param length the length + * @throw net::Error on errors + * @note Wrapper of connect(2) + */ + template <typename Address, typename Protocol> + void connect(Socket<Address, Protocol> &sc, const sockaddr *address, socklen_t length) + { + if (::connect(sc.handle(), address, length) == Failure) { + /* + * Determine if the error comes from a non-blocking connect that cannot be + * accomplished yet. + */ +#if defined(_WIN32) + int error = WSAGetLastError(); + + if (error == WSAEWOULDBLOCK) { + sc.setState(State::Connecting); + sc.setAction(Action::Connect); + sc.setCondition(Condition::Writable); + } else { + sc.setState(State::Disconnected); + throw Error{Error::System, "connect", error}; + } +#else + if (errno == EINPROGRESS) { + sc.setState(State::Connecting); + sc.setAction(Action::Connect); + sc.setCondition(Condition::Writable); + } else { + sc.setState(State::Disconnected); + throw Error{Error::System, "connect"}; + } +#endif + } else { + sc.setState(State::Connected); + } + } + + /** + * Continue the connection. This function must only be called when the socket is ready for writing, + * the user is responsible of waiting for that condition. + * + * This function check for SOL_SOCKET/SO_ERROR status. + * + * If the connection is complete, status is set to State::Connected, otherwise it is set to + * State::Disconnected. In both cases, action and condition are not set. + * + * @param sc the socket + * @throw net::Error on errors + */ + template <typename Address, typename Protocol> + void connect(Socket<Address, Protocol> &sc) + { + int error = sc.template get<int>(SOL_SOCKET, SO_ERROR); + + if (error == Failure) { + sc.setState(State::Disconnected); + throw Error{Error::System, "connect", error}; + } + + sc.setState(State::Connected); + } + + /** + * Accept a clear client. + * + * If the socket is marked non-blocking and there are no pending connection, this function throws an + * error. The user must wait that the socket is readable before calling this function. + * + * If the socket is blocking, this function blocks until a new client is connected or throws an error on + * errors. + * + * If the socket is correctly returned, its state is set to State::Accepted and its action and condition + * are not set. + * + * In any case, action and condition of this socket are not set. + * + * @param sc the socket + * @param address the address destination + * @param length the address length + * @return the socket + * @throw net::Error on errors + * @note Wrapper of accept(2) + */ + template <typename Address, typename Protocol> + Socket<Address, Protocol> accept(Socket<Address, Protocol> &sc, sockaddr *address, socklen_t *length) + { + Handle handle = ::accept(sc.handle(), address, length); + + if (handle == Invalid) { + throw Error{Error::System, "accept"}; + } + + return Socket<Address, Protocol>{handle, State::Accepted}; + } + + /** + * Continue accept. + * + * This function is just present for compatibility, it should never be called. + */ + template <typename Address, typename Protocol> + inline void accept(Socket<Address, Protocol> &) const noexcept + { + /* no-op */ + } + + /** + * Receive data. + * + * If the socket is marked non-blocking and no data is available, 0 is returned and condition is set to + * Condition::Readable. If 0 is returned and condition is not set, then the state is set to + * State::Disconnected. + * + * If the socket is blocking, this function blocks until some data is available or if an error occurs. + * + * In any case, action is never set. + * + * @param sc the socket + * @param data the destination + * @param length the destination length + * @return the number of bytes read + * @throw Error on errors + * @note Wrapper of recv(2) + */ + template <typename Address> + unsigned recv(Socket<Address, Tcp> &sc, void *data, unsigned length) + { + int nbread = ::recv(sc.handle(), (Arg)data, length, 0); + + if (nbread == Failure) { +#if defined(_WIN32) + int error = WSAGetLastError(); + + if (error == WSAEWOULDBLOCK) { + nbread = 0; + sc.setCondition(Condition::Readable); + } else { + sc.setState(State::Disconnected); + throw Error{Error::System, "recv", error}; + } +#else + if (errno == EAGAIN || errno == EWOULDBLOCK) { + sc.setCondition(Condition::Readable); + } else { + sc.setState(State::Disconnected); + throw Error{Error::System, "recv"}; + } +#endif + } else if (nbread == 0) { + sc.setState(State::Disconnected); + } + + return static_cast<unsigned>(nbread); + } + + /** + * Send some data. + * + * If the socket is marked non-blocking and the operation would block, then 0 is returned and condition is set to + * Condition::Writable. + * + * If the socket is blocking, this function blocks until the data has been sent. + * + * On any other errors, this function throw net::Error. + * + * @param sc the socket + * @param data the buffer to send + * @param length the buffer length + * @return the number of bytes sent + * @throw net::Error on errors + * @note Wrapper of send(2) + */ + template <typename Address> + unsigned send(Socket<Address, Tcp> &sc, const void *data, unsigned length) + { + int nbsent = ::send(sc.handle(), (ConstArg)data, length, 0); + + if (nbsent == Failure) { +#if defined(_WIN32) + int error = WSAGetLastError(); + + if (error == WSAEWOULDBLOCK) { + nbsent = 0; + sc.setCondition(Condition::Writable); + } else { + sc.setState(State::Disconnected); + throw Error{Error::System, "send", error}; + } +#else + if (errno == EAGAIN || errno == EWOULDBLOCK) { + nbsent = 0; + sc.setCondition(Condition::Writable); + } else { + sc.setState(State::Disconnected); + throw Error{Error::System, "send"}; + } +#endif + } + + return static_cast<unsigned>(nbsent); + } +}; + +/* }}} */ + +/* {{{ Udp */ + +/** + * @class Udp + * @brief Clear UDP type. + * + * This class is the basic implementation of UDP sockets. + */ +class Udp { +public: + /** + * Socket type. + * + * @return SOCK_DGRAM + */ + inline int type() const noexcept + { + return SOCK_DGRAM; + } + + /** + * Do nothing. + */ + template <typename Address> + inline void create(Socket<Address, Udp> &) noexcept + { + /* No-op */ + } + + /** + * Receive data from an end point. + * + * If the socket is marked non-blocking and no data is available, 0 is returned and condition is set to + * Condition::Readable. + * + * If the socket is blocking, this functions blocks until some data is available or if an error occurs. + * + * @param sc the socket + * @param data the destination buffer + * @param length the buffer length + * @param address the address + * @param addrlen the initial address length + * @return the number of bytes received + * @throw Error on error + */ + template <typename Address> + unsigned recvfrom(Socket<Address, Udp> &sc, void *data, unsigned length, sockaddr *address, socklen_t *addrlen) + { + int nbread; + + nbread = ::recvfrom(sc.handle(), (Arg)data, length, 0, address, addrlen); + + if (nbread == Failure) { +#if defined(_WIN32) + int error = WSAGetLastError(); + + if (error == WSAEWOULDBLOCK) { + nbread = 0; + sc.setCondition(Condition::Readable); + } else { + throw Error{Error::System, "recvfrom"}; + } +#else + if (errno == EAGAIN || errno == EWOULDBLOCK) { + nbread = 0; + sc.setCondition(Condition::Readable); + } else { + throw Error{Error::System, "recvfrom"}; + } +#endif + } + + return static_cast<unsigned>(nbread); + } + + /** + * Send data to an end point. + * + * If the socket is marked non-blocking and the operation would block, then 0 is returned and condition is set to + * Condition::Writable. + * + * If the socket is blocking, this functions blocks until the data has been sent. + * + * @param sc the socket + * @param data the buffer + * @param length the buffer length + * @param address the client address + * @param addrlen the adderss length + * @return the number of bytes sent + * @throw Error on error + */ + template <typename Address> + unsigned sendto(Socket<Address, Udp> &sc, const void *data, unsigned length, const sockaddr *address, socklen_t addrlen) + { + int nbsent; + + nbsent = ::sendto(sc.handle(), (ConstArg)data, length, 0, address, addrlen); + if (nbsent == Failure) { +#if defined(_WIN32) + int error = WSAGetLastError(); + + if (error == WSAEWOULDBLOCK) { + nbsent = 0; + sc.setCondition(Condition::Writable); + } else { + throw Error{Error::System, "sendto", error}; + } +#else + if (errno == EAGAIN || errno == EWOULDBLOCK) { + nbsent = 0; + sc.setCondition(Condition::Writable); + } else { + throw Error{Error::System, "sendto"}; + } +#endif + } + + return static_cast<unsigned>(nbsent); + } +}; + +/* }}} */ + +/* {{{ Tls */ + +#if !defined(SOCKET_NO_SSL) + +/** + * @class Tls + * @brief OpenSSL secure layer for TCP. + * + * **Note:** This protocol is much more difficult to use with non-blocking sockets, if some operations would block, the + * user is responsible of calling the function again by waiting for the appropriate condition. See the functions for + * more details. + * + * @see Tls::accept + * @see Tls::connect + * @see Tls::recv + * @see Tls::send + */ +class Tls : private Tcp { +private: + using Context = std::shared_ptr<SSL_CTX>; + using Ssl = std::unique_ptr<SSL, void (*)(SSL *)>; + + /* OpenSSL objects */ + Context m_context; + Ssl m_ssl{nullptr, nullptr}; + + /* Status */ + bool m_tcpconnected{false}; + + /* + * User definable parameters + */ + ssl::Method m_method{ssl::Tlsv1}; + std::string m_key; + std::string m_certificate; + bool m_verify{false}; + + /* + * Construct with a context and ssl, for Tls::accept. + */ + Tls(Context context, Ssl ssl) + : m_context{std::move(context)} + , m_ssl{std::move(ssl)} + { + } + + /* + * Get the OpenSSL error message. + */ + inline std::string error(int error) + { + auto msg = ERR_reason_error_string(error); + + return msg == nullptr ? "" : msg; + } + + /* + * Update the states after an uncompleted operation. + */ + template <typename Address, typename Protocol> + inline void updateStates(Socket<Address, Protocol> &sc, State state, Action action, int code) + { + assert(code == SSL_ERROR_WANT_READ || code == SSL_ERROR_WANT_WRITE); + + sc.setState(state); + sc.setAction(action); + + if (code == SSL_ERROR_WANT_READ) { + sc.setCondition(Condition::Readable); + } else { + sc.setCondition(Condition::Writable); + } + } + + /* + * Continue the connect operation. + */ + template <typename Address, typename Protocol> + void processConnect(Socket<Address, Protocol> &sc) + { + int ret = SSL_connect(m_ssl.get()); + + if (ret <= 0) { + int no = SSL_get_error(m_ssl.get(), ret); + + if (no == SSL_ERROR_WANT_READ || no == SSL_ERROR_WANT_WRITE) { + updateStates(sc, State::Connecting, Action::Connect, no); + } else { + sc.setState(State::Disconnected); + throw Error{Error::System, "connect", error(no)}; + } + } else { + sc.setState(State::Connected); + } + } + + /* + * Continue accept. + */ + template <typename Address, typename Protocol> + void processAccept(Socket<Address, Protocol> &sc) + { + int ret = SSL_accept(m_ssl.get()); + + if (ret <= 0) { + int no = SSL_get_error(m_ssl.get(), ret); + + if (no == SSL_ERROR_WANT_READ || no == SSL_ERROR_WANT_WRITE) { + updateStates(sc, State::Accepting, Action::Accept, no); + } else { + sc.setState(State::Disconnected); + throw Error(Error::System, "accept", error(no)); + } + } else { + sc.setState(State::Accepted); + } + } + +public: + /** + * @copydoc Tcp::type + */ + inline int type() const noexcept + { + return SOCK_STREAM; + } + + /** + * Empty TLS constructor. + */ + Tls() + { +#if !defined(SOCKET_NO_SSL_AUTO_INIT) + net::ssl::init(); +#endif + } + + /** + * Set the method. + * + * @param method the method + * @pre the socket must not be already created + */ + inline void setMethod(ssl::Method method) noexcept + { + assert(!m_context); + assert(!m_ssl); + + m_method = method; + } + + /** + * Use the specified private key file. + * + * @param file the path to the private key + */ + inline void setPrivateKey(std::string file) noexcept + { + m_key = std::move(file); + } + + /** + * Use the specified certificate file. + * + * @param file the path to the file + */ + inline void setCertificate(std::string file) noexcept + { + m_certificate = std::move(file); + } + + /** + * Set to true if we must verify the certificate and private key. + * + * @param verify the mode + */ + inline void setVerify(bool verify = true) noexcept + { + m_verify = verify; + } + + /** + * Initialize the SSL objects after have created. + * + * @param sc the socket + * @throw net::Error on errors + */ + template <typename Address> + inline void create(Socket<Address, Tls> &sc) + { + auto method = (m_method == ssl::Tlsv1) ? TLSv1_method() : SSLv23_method(); + + m_context = {SSL_CTX_new(method), SSL_CTX_free}; + m_ssl = {SSL_new(m_context.get()), SSL_free}; + + SSL_set_fd(m_ssl.get(), sc.handle()); + + /* Load certificates */ + if (m_certificate.size() > 0) { + SSL_CTX_use_certificate_file(m_context.get(), m_certificate.c_str(), SSL_FILETYPE_PEM); + } + if (m_key.size() > 0) { + SSL_CTX_use_PrivateKey_file(m_context.get(), m_key.c_str(), SSL_FILETYPE_PEM); + } + if (m_verify && !SSL_CTX_check_private_key(m_context.get())) { + throw Error{Error::System, "(openssl)", "unable to verify key"}; + } + } + + /** + * Connect to a secure host. + * + * If the socket is marked non-blocking and the connection cannot be established yet, then the state is set + * to State::Connecting, the condition is set to Condition::Readable or Condition::Writable, the user must + * wait for the appropriate condition before calling the overload connect which takes 0 argument. + * + * If the socket is blocking, this functions blocks until the connection is complete. + * + * If the connection was completed correctly the state is set to State::Connected. + * + * @param sc the socket + * @param address the address + * @param length the address length + * @throw net::Error on errors + */ + template <typename Address, typename Protocol> + void connect(Socket<Address, Protocol> &sc, const sockaddr *address, socklen_t length) + { + /* 1. Connect using raw TCP */ + Tcp::connect(sc, address, length); + + /* 2. If the connection is complete (e.g. non-blocking), try handshake */ + if (sc.state() == State::Connected) { + m_tcpconnected = true; + processConnect(sc); + } + } + + /** + * Continue the connection. + * + * This function must be called when the socket is ready for reading or writing (check with Socket::condition), + * the state may change exactly like the initial connect call. + * + * @param sc the socket + * @throw net::Error on errors + */ + template <typename Address, typename Protocol> + void connect(Socket<Address, Protocol> &sc) + { + /* 1. Be sure to complete standard connect before */ + if (!m_tcpconnected) { + Tcp::connect(sc); + m_tcpconnected = sc.state() == State::Connected; + } + + if (m_tcpconnected) { + processConnect(sc); + } + } + + /** + * Accept a secure client. + * + * Because SSL needs several round-trips, if the socket is marked non-blocking and the connection is not + * completed yet, a new socket is returned but with the State::Accepting state. Its condition is set to + * Condition::Readable or Condition::Writable, the user is responsible of calling accept overload which takes + * 0 arguments on the returned socket when the condition is met. + * + * If the socket is blocking, this function blocks until the client is accepted and returned. + * + * If the client is accepted correctly, its state is set to State::Accepted. This instance does not change. + * + * @param sc the socket + * @param address the address destination + * @param length the address length + * @return the client + * @throw net::Error on errors + */ + template <typename Address> + Socket<Address, Tls> accept(Socket<Address, Tls> &sc, sockaddr *address, socklen_t *length) + { + Socket<Address, Tls> client = Tcp::accept(sc, address, length); + Tls &proto = client.protocol(); + + /* 1. Share the context */ + proto.m_context = m_context; + + /* 2. Create new SSL instance */ + proto.m_ssl = Ssl{SSL_new(m_context.get()), SSL_free}; + SSL_set_fd(proto.m_ssl.get(), client.handle()); + + /* 3. Try accept process on the **new** client */ + proto.processAccept(client); + + return client; + } + + /** + * Continue accept. + * + * This function must be called on the client that is being accepted. + * + * Like accept or connect, user is responsible of calling this function until the connection is complete. + * + * @param sc the socket + * @throw net::Error on errors + */ + template <typename Address, typename Protocol> + inline void accept(Socket<Address, Protocol> &sc) + { + processAccept(sc); + } + + /** + * Receive some secure data. + * + * If the socket is marked non-blocking, 0 is returned if no data is available yet or if the connection + * needs renegociation. If renegociation is required case, the action is set to Action::Receive and condition + * is set to Condition::Readable or Condition::Writable. The user must wait that the condition is met and + * call this function again. + * + * @param sc the socket + * @param data the destination + * @param len the buffer length + * @return the number of bytes read + * @throw net::Error on errors + */ + template <typename Address> + unsigned recv(Socket<Address, Tls> &sc, void *data, unsigned len) + { + auto nbread = SSL_read(m_ssl.get(), data, len); + + if (nbread <= 0) { + auto no = SSL_get_error(m_ssl.get(), nbread); + + if (no == SSL_ERROR_WANT_READ || no == SSL_ERROR_WANT_WRITE) { + nbread = 0; + updateStates(sc, sc.state(), Action::Receive, no); + } else { + throw Error{Error::System, "recv", error(no)}; + } + } + + return nbread; + } + + /** + * Send some data. + * + * Like recv, if the socket is marked non-blocking and no data can be sent or a negociation is required, + * condition and action are set. See receive for more details + * + * @param sc the socket + * @param data the data to send + * @param len the buffer length + * @return the number of bytes sent + * @throw net::Error on errors + */ + template <typename Address> + unsigned send(Socket<Address, Tls> &sc, const void *data, unsigned len) + { + auto nbsent = SSL_write(m_ssl.get(), data, len); + + if (nbsent <= 0) { + auto no = SSL_get_error(m_ssl.get(), nbsent); + + if (no == SSL_ERROR_WANT_READ || no == SSL_ERROR_WANT_WRITE) { + nbsent = 0; + updateStates(sc, sc.state(), Action::Send, no); + } else { + throw Error{Error::System, "send", error(no)}; + } + } + + return nbsent; + } +}; + +#endif // !SOCKET_NO_SSL + +/* }}} */ + +} // !protocol + +/* }}} */ + +/* + * Convenient helpers + * ------------------------------------------------------------------ + * + * - SocketTcp<Address>, for TCP sockets, + * - SocketUdp<Address>, for UDP sockets, + * - SocketTls<Address>, for secure TCP sockets. + */ + +/* {{{ Helpers */ + +/** + * Helper to create TCP sockets. + */ +template <typename Address> +using SocketTcp = Socket<Address, protocol::Tcp>; + +/** + * Helper to create TCP/IP sockets. + */ +using SocketTcpIp = Socket<address::Ip, protocol::Tcp>; + +#if !defined(_WIN32) + +/** + * Helper to create TCP/Local sockets. + */ +using SocketTcpLocal = Socket<address::Local, protocol::Tcp>; + +#endif + +/** + * Helper to create UDP sockets. + */ +template <typename Address> +using SocketUdp = Socket<Address, protocol::Udp>; + +/** + * Helper to create UDP/IP sockets. + */ +using SocketUdpIp = Socket<address::Ip, protocol::Udp>; + +#if !defined(SOCKET_NO_SSL) + +/** + * Helper to create OpenSSL TCP sockets. + */ +template <typename Address> +using SocketTls = Socket<Address, protocol::Tls>; + +/** + * Helper to create OpenSSL TCP/Ip sockets. + */ +using SocketTlsIp = Socket<address::Ip, protocol::Tls>; + +#endif // !SOCKET_NO_SSL + +/* }}} */ + +/* + * Select wrapper + * ------------------------------------------------------------------ + * + * Wrapper for select(2) and other various implementations. + */ + +/* {{{ Listener */ + +/** + * @class ListenerStatus + * @brief Result of polling + * + * Result of a select call, returns the first ready socket found with its + * flags. + */ +class ListenerStatus { +public: + Handle socket; //!< which socket is ready + Condition flags; //!< the flags +}; + +/** + * Table used in the socket listener to store which sockets have been + * set in which directions. + */ +using ListenerTable = std::map<Handle, Condition>; + +/** + * @class Select + * @brief Implements select(2) + * + * This class is the fallback of any other method, it is not preferred at all for many reasons. + */ +class Select { +public: + /** + * No-op, uses the ListenerTable directly. + */ + inline void set(const ListenerTable &, Handle, Condition, bool) noexcept {} + + /** + * No-op, uses the ListenerTable directly. + */ + inline void unset(const ListenerTable &, Handle, Condition, bool) noexcept {} + + /** + * Return the sockets + */ + std::vector<ListenerStatus> wait(const ListenerTable &table, int ms); + + /** + * Backend identifier + */ + inline const char *name() const noexcept + { + return "select"; + } +}; + +#if defined(SOCKET_HAVE_POLL) + +/** + * @class Poll + * @brief Implements poll(2). + * + * Poll is widely supported and is better than select(2). It is still not the + * best option as selecting the sockets is O(n). + */ +class Poll { +private: + std::vector<pollfd> m_fds; + + short toPoll(Condition flags) const noexcept; + Condition toCondition(short &event) const noexcept; + +public: + /** + * Set the handle. + */ + void set(const ListenerTable &, Handle, Condition, bool); + + /** + * Unset the handle. + */ + void unset(const ListenerTable &, Handle, Condition, bool); + + /** + * Wait for events. + */ + std::vector<ListenerStatus> wait(const ListenerTable &, int ms); + + /** + * Backend identifier + */ + inline const char *name() const noexcept + { + return "poll"; + } +}; + +#endif + +#if defined(SOCKET_HAVE_EPOLL) + +/** + * @class Epoll + * @brief Linux's epoll. + */ +class Epoll { +private: + int m_handle; + std::vector<epoll_event> m_events; + + Epoll(const Epoll &) = delete; + Epoll &operator=(const Epoll &) = delete; + Epoll(const Epoll &&) = delete; + Epoll &operator=(const Epoll &&) = delete; + + uint32_t toEpoll(Condition flags) const noexcept; + Condition toCondition(uint32_t events) const noexcept; + void update(Handle sc, int op, int eflags); + +public: + /** + * Construct the epoll instance. + */ + Epoll(); + + /** + * Close the epoll instance. + */ + ~Epoll(); + + /** + * Set the handle. + */ + void set(const ListenerTable &, Handle, Condition, bool); + + /** + * Unset the handle. + */ + void unset(const ListenerTable &, Handle, Condition, bool); + + /** + * Wait for events. + */ + std::vector<ListenerStatus> wait(const ListenerTable &, int); + + /** + * Backend identifier + */ + inline const char *name() const noexcept + { + return "epoll"; + } +}; + +#endif + +#if defined(SOCKET_HAVE_KQUEUE) + +/** + * @class Kqueue + * @brief Implements kqueue(2). + * + * This implementation is available on all BSD and Mac OS X. It is better than + * poll(2) because it's O(1), however it's a bit more memory consuming. + */ +class Kqueue { +private: + std::vector<struct kevent> m_result; + int m_handle; + + Kqueue(const Kqueue &) = delete; + Kqueue &operator=(const Kqueue &) = delete; + Kqueue(Kqueue &&) = delete; + Kqueue &operator=(Kqueue &&) = delete; + + void update(Handle sc, int filter, int kflags); + +public: + /** + * Construct the kqueue instance. + */ + Kqueue(); + + /** + * Destroy the kqueue instance. + */ + ~Kqueue(); + + /** + * Set the handle. + */ + void set(const ListenerTable &, Handle, Condition, bool); + + /** + * Unset the handle. + */ + void unset(const ListenerTable &, Handle, Condition, bool); + + /** + * Wait for events. + */ + std::vector<ListenerStatus> wait(const ListenerTable &, int); + + /** + * Backend identifier + */ + inline const char *name() const noexcept + { + return "kqueue"; + } +}; + +#endif + +/** + * @class Listener + * @brief Synchronous multiplexing + * + * Convenient wrapper around the select() system call. + * + * This class is implemented using a bridge pattern to allow different uses + * of listener implementation. + * + * You should not reinstanciate a new Listener at each iteartion of your + * main loop as it can be extremely costly. Instead use the same listener that + * you can safely modify on the fly. + * + * Currently, poll, epoll, select and kqueue are available. + * + * To implement the backend, the following functions must be available: + * + * ### Set + * + * @code + * void set(const ListenerTable &, Handle sc, Condition condition, bool add); + * @endcode + * + * This function, takes the socket to be added and the flags. The condition is + * always guaranteed to be correct and the function will never be called twice + * even if the user tries to set the same flag again. + * + * An optional add argument is added for backends which needs to do different + * operation depending if the socket was already set before or if it is the + * first time (e.g EPOLL_CTL_ADD vs EPOLL_CTL_MOD for epoll(7). + * + * ### Unset + * + * @code + * void unset(const ListenerTable &, Handle sc, Condition condition, bool remove); + * @endcode + * + * Like set, this function is only called if the condition is actually set and will + * not be called multiple times. + * + * Also like set, an optional remove argument is set if the socket is being + * completely removed (e.g no more flags are set for this socket). + * + * ### Wait + * + * @code + * std::vector<ListenerStatus> wait(const ListenerTable &, int ms); + * @endcode + * + * Wait for the sockets to be ready with the specified milliseconds. Must return a list of ListenerStatus, + * may throw any exceptions. + * + * ### Name + * + * @code + * inline const char *name() const noexcept + * @endcode + * + * Returns the backend name. Usually the class in lower case. + */ +template <typename Backend = SOCKET_DEFAULT_BACKEND> +class Listener { +private: + Backend m_backend; + ListenerTable m_table; + +public: + /** + * Construct an empty listener. + */ + Listener() = default; + + /** + * Get the backend. + * + * @return the backend + */ + inline const Backend &backend() const noexcept + { + return m_backend; + } + + /** + * Get the non-modifiable table. + * + * @return the table + */ + inline const ListenerTable &table() const noexcept + { + return m_table; + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline ListenerTable::const_iterator begin() const noexcept + { + return m_table.begin(); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline ListenerTable::const_iterator cbegin() const noexcept + { + return m_table.cbegin(); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline ListenerTable::const_iterator end() const noexcept + { + return m_table.end(); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline ListenerTable::const_iterator cend() const noexcept + { + return m_table.cend(); + } + + /** + * Add or update a socket to the listener. + * + * If the socket is already placed with the appropriate flags, the + * function is a no-op. + * + * If incorrect flags are passed, the function does nothing. + * + * @param sc the socket + * @param condition the condition (may be OR'ed) + * @throw Error if the backend failed to set + */ + void set(Handle sc, Condition condition) + { + /* Invalid or useless flags */ + if (condition == Condition::None || static_cast<int>(condition) > 0x3) + return; + + auto it = m_table.find(sc); + + /* + * Do not update the table if the backend failed to add + * or update. + */ + if (it == m_table.end()) { + m_backend.set(m_table, sc, condition, true); + m_table.emplace(sc, condition); + } else { + /* Remove flag if already present */ + if ((condition & Condition::Readable) == Condition::Readable && + (it->second & Condition::Readable) == Condition::Readable) { + condition &= ~(Condition::Readable); + } + if ((condition & Condition::Writable) == Condition::Writable && + (it->second & Condition::Writable) == Condition::Writable) { + condition &= ~(Condition::Writable); + } + + /* Still need a call? */ + if (condition != Condition::None) { + m_backend.set(m_table, sc, condition, false); + it->second |= condition; + } + } + } + + /** + * Unset a socket from the listener, only the flags is removed + * unless the two flagss are requested. + * + * For example, if you added a socket for both reading and writing, + * unsetting the write flags will keep the socket for reading. + * + * @param sc the socket + * @param condition the condition (may be OR'ed) + * @see remove + */ + void unset(Handle sc, Condition condition) + { + auto it = m_table.find(sc); + + /* Invalid or useless flags */ + if (condition == Condition::None || static_cast<int>(condition) > 0x3 || it == m_table.end()) + return; + + /* + * Like set, do not update if the socket is already at the appropriate + * state. + */ + if ((condition & Condition::Readable) == Condition::Readable && + (it->second & Condition::Readable) != Condition::Readable) { + condition &= ~(Condition::Readable); + } + if ((condition & Condition::Writable) == Condition::Writable && + (it->second & Condition::Writable) != Condition::Writable) { + condition &= ~(Condition::Writable); + } + + if (condition != Condition::None) { + /* Determine if it's a complete removal */ + bool removal = ((it->second) & ~(condition)) == Condition::None; + + m_backend.unset(m_table, sc, condition, removal); + + if (removal) { + m_table.erase(it); + } else { + it->second &= ~(condition); + } + } + } + + /** + * Remove completely the socket from the listener. + * + * It is a shorthand for unset(sc, Condition::Readable | Condition::Writable); + * + * @param sc the socket + */ + inline void remove(Handle sc) + { + unset(sc, Condition::Readable | Condition::Writable); + } + + /** + * Remove all sockets. + */ + inline void clear() + { + while (!m_table.empty()) { + remove(m_table.begin()->first); + } + } + + /** + * Get the number of sockets in the listener. + */ + inline ListenerTable::size_type size() const noexcept + { + return m_table.size(); + } + + /** + * Select a socket. Waits for a specific amount of time specified as the duration. + * + * @param duration the duration + * @return the socket ready + */ + template <typename Rep, typename Ratio> + inline ListenerStatus wait(const std::chrono::duration<Rep, Ratio> &duration) + { + assert(!m_table.empty()); + + auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration); + + return m_backend.wait(m_table, cvt.count())[0]; + } + + /** + * Overload with milliseconds. + * + * @param timeout the optional timeout in milliseconds + * @return the socket ready + */ + inline ListenerStatus wait(int timeout = -1) + { + return wait(std::chrono::milliseconds(timeout)); + } + + /** + * Select multiple sockets. + * + * @param duration the duration + * @return the socket ready + */ + template <typename Rep, typename Ratio> + inline std::vector<ListenerStatus> waitMultiple(const std::chrono::duration<Rep, Ratio> &duration) + { + assert(!m_table.empty()); + + auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration); + + return m_backend.wait(m_table, cvt.count()); + } + + /** + * Overload with milliseconds. + * + * @return the socket ready + */ + inline std::vector<ListenerStatus> waitMultiple(int timeout = -1) + { + return waitMultiple(std::chrono::milliseconds(timeout)); + } +}; + +/* }}} */ + +/* + * Callback + * ------------------------------------------------------------------ + * + * Function owner with tests. + */ + +/* {{{ Callback */ + +/** + * @class Callback + * @brief Convenient signal owner that checks if the target is valid. + * + * This class also catch all errors thrown from signals to avoid interfering with our process. + */ +template <typename... Args> +class Callback : public std::function<void (Args...)> { +public: + /** + * Inherited constructors. + */ + using std::function<void (Args...)>::function; + + /** + * Execute the callback only if a target is set. + */ + void operator()(Args... args) const + { + if (*this) { + try { + std::function<void (Args...)>::operator()(args...); + } catch (...) { + } + } + } +}; + +/* }}} */ + +/* + * StreamConnection + * ------------------------------------------------------------------ + * + * Client connected on the server side. + */ + +/* {{{ StreamConnection */ + +/** + * @class StreamConnection + * @brief Connected client on the server side. + * + * This object is created from StreamServer when a new client is connected, it is the higher + * level object of sockets and completely asynchronous. + */ +template <typename Address, typename Protocol> +class StreamConnection { +public: + /** + * Called when the output has changed. + */ + using WriteHandler = Callback<>; + +private: + /* Signals */ + WriteHandler m_onWrite; + + /* Sockets and output buffer */ + Socket<Address, Protocol> m_socket; + std::string m_output; + +public: + /** + * Create the connection. + * + * @param s the socket + */ + StreamConnection(Socket<Address, Protocol> s) + : m_socket{std::move(s)} + { + m_socket.set(net::option::SockBlockMode{false}); + } + + /** + * Access the underlying socket. + * + * @return the socket + * @warning use with care + */ + inline Socket<Address, Protocol> &socket() noexcept + { + return m_socket; + } + + /** + * Access the current output. + * + * @return the output + */ + inline const std::string &output() const noexcept + { + return m_output; + } + + /** + * Overloaded function + * + * @return the output + * @warning use with care, avoid modifying the output if you don't know what you're doing + */ + inline std::string &output() noexcept + { + return m_output; + } + + /** + * Post some data to be sent asynchronously. + * + * @param str the data to append + */ + inline void send(std::string str) + { + m_output += str; + m_onWrite(); + } + + /** + * Kill the client. + */ + inline void close() + { + m_socket.close(); + } + + /** + * Set the write handler, the signal is emitted when the output has changed so that the StreamServer owner + * knows that there are some data to send. + * + * @param handler the handler + * @warning you usually never need to set this yourself + */ + inline void setWriteHandler(WriteHandler handler) + { + m_onWrite = std::move(handler); + } +}; + +/* }}} */ + +/* + * StreamServer + * ------------------------------------------------------------------ + * + * Convenient stream oriented server. + */ + +/* {{{ StreamServer */ + +/** + * @class StreamServer + * @brief Convenient stream server for TCP and TLS. + * + * This class does all the things for you as accepting new clients, listening for it and sending data. It works + * asynchronously without blocking to let you control your process workflow. + * + * This class is not thread safe and you must not call any of the functions from different threads. + */ +template <typename Address, typename Protocol> +class StreamServer { +public: + /** + * Handler when a new client is connected. + */ + using ConnectionHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &>; + + /** + * Handler when a client is disconnected. + */ + using DisconnectionHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &>; + + /** + * Handler when data has been received from a client. + */ + using ReadHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &, const std::string &>; + + /** + * Handler when data has been correctly sent to a client. + */ + using WriteHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &, const std::string &>; + + /** + * Handler when an error occured. + */ + using ErrorHandler = Callback<const Error &>; + + /** + * Handler when there was a timeout. + */ + using TimeoutHandler = Callback<>; + +private: + using ClientMap = std::map<Handle, std::shared_ptr<StreamConnection<Address, Protocol>>>; + + /* Signals */ + ConnectionHandler m_onConnection; + DisconnectionHandler m_onDisconnection; + ReadHandler m_onRead; + WriteHandler m_onWrite; + ErrorHandler m_onError; + TimeoutHandler m_onTimeout; + + /* Sockets */ + Socket<Address, Protocol> m_master; + Listener<> m_listener; + ClientMap m_clients; + + /* + * Update flags depending on the required condition. + */ + void updateFlags(std::shared_ptr<StreamConnection<Address, Protocol>> &client) + { + assert(client->socket().action() != Action::None); + + m_listener.remove(client->socket().handle()); + m_listener.set(client->socket().handle(), client->socket().condition()); + } + + /* + * Continue accept process. + */ + template <typename AcceptCall> + void processAccept(std::shared_ptr<StreamConnection<Address, Protocol>> &client, const AcceptCall &acceptFunc) + { + try { + /* Do the accept */ + acceptFunc(); + + /* 1. First remove completely the client */ + m_listener.remove(client->socket().handle()); + + /* 2. If accept is not finished, wait for the appropriate condition */ + if (client->socket().state() == State::Accepted) { + /* 3. Client is accepted, notify the user */ + m_listener.set(client->socket().handle(), Condition::Readable); + m_onConnection(client); + } else { + /* Operation still in progress */ + updateFlags(client); + } + } catch (const Error &error) { + m_clients.erase(client->socket().handle()); + m_listener.remove(client->socket().handle()); + m_onError(error); + } + } + + /* + * Process initial accept of master socket, this is the initial accepting process. Except on errors, the + * socket is stored but the user will be notified only once the socket is completely accepted. + */ + void processInitialAccept() + { + // TODO: store address too. + std::shared_ptr<StreamConnection<Address, Protocol>> client = std::make_shared<StreamConnection<Address, Protocol>>(m_master.accept(nullptr)); + std::weak_ptr<StreamConnection<Address, Protocol>> ptr{client}; + + /* 1. Register output changed to update listener */ + client->setWriteHandler([this, ptr] () { + auto client = ptr.lock(); + + /* Do not update the listener immediately if an action is pending */ + if (client && client->socket().action() == Action::None && !client->output().empty()) { + m_listener.set(client->socket().handle(), Condition::Writable); + } + }); + + /* 2. Add the client */ + m_clients.insert(std::make_pair(client->socket().handle(), client)); + + /* + * 2. Do an initial check to set the listener flags, at this moment the socket may or not be + * completely accepted. + */ + processAccept(client, [&] () {}); + } + + /* + * Read or complete the read operation. + */ + void processRead(std::shared_ptr<StreamConnection<Address, Protocol>> &client) + { + /* + * Read because there is something to read or because the pending operation is + * read and must complete. + */ + auto buffer = client->socket().recv(512); + + /* + * Now the receive operation may be completed, in that case, two possibilities: + * + * 1. The action is set to None (completed) + * 2. The action is still not complete, update the flags + */ + if (client->socket().action() == Action::None) { + /* Empty mean normal disconnection */ + if (buffer.empty()) { + m_listener.remove(client->socket().handle()); + m_clients.erase(client->socket().handle()); + m_onDisconnection(client); + } else { + /* + * At this step, it is possible that we were completing a receive operation, in this + * case the write flag may be removed, add it if required. + */ + if (!client->output().empty()) { + m_listener.set(client->socket().handle(), Condition::Writable); + } + + m_onRead(client, buffer); + } + } else { + /* Operation in progress */ + updateFlags(client); + } + } + + /* + * Flush the output buffer. + */ + void processWrite(std::shared_ptr<StreamConnection<Address, Protocol>> &client) + { + auto &output = client->output(); + auto nsent = client->socket().send(output); + + if (client->socket().action() == Action::None) { + /* 1. Create a copy of content that has been sent */ + auto sent = output.substr(0, nsent); + + /* 2. Erase the content sent */ + output.erase(0, nsent); + + /* 3. Update listener */ + if (output.empty()) { + m_listener.unset(client->socket().handle(), Condition::Writable); + } + + /* 4. Notify user */ + m_onWrite(client, sent); + } else { + updateFlags(client); + } + } + + void processSync(std::shared_ptr<StreamConnection<Address, Protocol>> &client, Condition flags) + { + try { + auto action = client->socket().action(); + + if (action == Action::Receive || + (action == Action::None && (flags & Condition::Readable) == Condition::Readable)) { + processRead(client); + } else if ((flags & Condition::Writable) == Condition::Writable) { + processWrite(client); + } + } catch (const Error &error) { + m_onDisconnection(client); + m_listener.remove(client->socket().handle()); + m_clients.erase(client->socket().handle()); + } + } + +public: + /** + * Create a stream server with the specified address to bind. + * + * @param protocol the protocol (Tcp or Tls) + * @param address the address to bind + * @param max the max number to listen + * @throw Error on errors + */ + StreamServer(Protocol protocol, const Address &address, int max = 128) + : m_master{std::move(protocol), address} + { + // TODO: m_onError + m_master.set(SOL_SOCKET, SO_REUSEADDR, 1); + m_master.bind(address); + m_master.listen(max); + m_listener.set(m_master.handle(), Condition::Readable); + } + + /** + * Set the connection handler, called when a new client is connected. + * + * @param handler the handler + */ + inline void setConnectionHandler(ConnectionHandler handler) + { + m_onConnection = std::move(handler); + } + + /** + * Set the disconnection handler, called when a client died. + * + * @param handler the handler + */ + inline void setDisconnectionHandler(DisconnectionHandler handler) + { + m_onDisconnection = std::move(handler); + } + + /** + * Set the receive handler, called when a client has sent something. + * + * @param handler the handler + */ + inline void setReadHandler(ReadHandler handler) + { + m_onRead = std::move(handler); + } + + /** + * Set the writing handler, called when some data has been sent to a client. + * + * @param handler the handler + */ + inline void setWriteHandler(WriteHandler handler) + { + m_onWrite = std::move(handler); + } + + /** + * Set the error handler, called when unrecoverable error has occured. + * + * @param handler the handler + */ + inline void setErrorHandler(ErrorHandler handler) + { + m_onError = std::move(handler); + } + + /** + * Set the timeout handler, called when the selection has timeout. + * + * @param handler the handler + */ + inline void setTimeoutHandler(TimeoutHandler handler) + { + m_onTimeout = std::move(handler); + } + + /** + * Poll for the next event. + * + * @param timeout the timeout (-1 for indefinitely) + * @throw Error on errors + */ + void poll(int timeout = -1) + { + try { + auto st = m_listener.wait(timeout); + + if (st.socket == m_master.handle()) { + /* New client */ + processInitialAccept(); + } else { + /* Recv / Send / Accept on a client */ + auto client = m_clients[st.socket]; + + if (client->socket().state() == State::Accepted) { + processSync(client, st.flags); + } else { + processAccept(client, [&] () { client->socket().accept(); }); + } + } + } catch (const Error &error) { + if (error.code() == Error::Timeout) { + m_onTimeout(); + } else { + m_onError(error); + } + } + } +}; + +/* }}} */ + +/* + * StreamClient + * ------------------------------------------------------------------ + */ + +/* {{{ StreamClient */ + +/** + * @class StreamClient + * @brief Client side connection to a server. + * + * This class is not thread safe and you must not call any of the functions from different threads. + */ +template <typename Address, typename Protocol> +class StreamClient { +public: + /** + * Handler when connection is complete. + */ + using ConnectionHandler = Callback<>; + + /** + * Handler when data has been received. + */ + using ReadHandler = Callback<const std::string &>; + + /** + * Handler when data has been sent correctly. + */ + using WriteHandler = Callback<const std::string &>; + + /** + * Handler when disconnected. + */ + using DisconnectionHandler = Callback<>; + + /** + * Handler on unrecoverable error. + */ + using ErrorHandler = Callback<const Error &>; + + /** + * Handler when timeout occured. + */ + using TimeoutHandler = Callback<>; + +private: + /* Signals */ + ConnectionHandler m_onConnection; + ReadHandler m_onRead; + WriteHandler m_onWrite; + DisconnectionHandler m_onDisconnection; + ErrorHandler m_onError; + TimeoutHandler m_onTimeout; + + /* Socket */ + Socket<Address, Protocol> m_socket; + Listener<> m_listener; + + /* Output buffer */ + std::string m_output; + + /* + * Update the flags after an uncompleted operation. This function must only be called when the operation + * has not complete (e.g. connect, recv, send). + */ + void updateFlags() + { + assert(m_socket.action() != Action::None); + + m_listener.remove(m_socket.handle()); + m_listener.set(m_socket.handle(), m_socket.condition()); + } + + /* + * This is the generic connect helper, it will be used to both initiate the connection or to continue the + * connection process if needed. + * + * Thus the template parameter is the appropriate function to call either, m_socket.connect(address) or + * m_socket.connect(). + * + * See poll() and connect() to understand. + */ + template <typename ConnectCall> + void processConnect(const ConnectCall &connectFunc) + { + /* Call m_socket.connect() or m_socket.connect(address) */ + connectFunc(); + + /* Remove entirely */ + m_listener.remove(m_socket.handle()); + + if (m_socket.state() == State::Connected) { + m_onConnection(); + m_listener.set(m_socket.handle(), Condition::Readable); + } else { + /* Connection still in progress */ + updateFlags(); + } + } + + /* + * Receive or complete the receive command, if the command is not complete, the listener is updated + * accordingly. + */ + void processRead() + { + auto received = m_socket.recv(512); + + if (m_socket.action() == Action::None) { + /* 0 means disconnection */ + if (received.empty()) { + m_onDisconnection(); + } else { + /* + * At this step, it is possible that we were completing a receive operation, in this + * case the write flag may be removed, add it if required. + */ + if (m_output.empty()) { + m_listener.unset(m_socket.handle(), Condition::Writable); + } + + m_onRead(received); + } + } else { + /* Receive operation in progress */ + updateFlags(); + } + } + + /* + * Send or complete the send command, if the command is not complete, the listener is updated + * accordingly. + */ + void processWrite() + { + auto nsent = m_socket.send(m_output); + + if (m_socket.action() == Action::None) { + /* 1. Make a copy of what has been sent */ + auto sent = m_output.substr(0, nsent); + + /* 2. Erase sent content */ + m_output.erase(0, nsent); + + /* 3. Update flags if needed */ + if (m_output.empty()) { + m_listener.unset(m_socket.handle(), Condition::Writable); + } + + /* 4. Notify user */ + m_onWrite(sent); + } else { + /* Send operation in progress */ + updateFlags(); + } + } + + /* + * Receive or send. + */ + void processSync(Condition condition) + { + if ((m_socket.action() == Action::Receive) || + (m_socket.action() == Action::None && (condition & Condition::Readable) == Condition::Readable)) { + processRead(); + } else { + processWrite(); + } + } + +public: + /** + * Create a client. The client is automatically marked as non-blocking. + * + * @param protocol the protocol (Tcp or Tls) + * @param address the optional address + * @throw net::Error on failures + */ + StreamClient(Protocol protocol = {}, const Address &address = {}) + : m_socket{std::move(protocol), address} + { + m_socket.set(net::option::SockBlockMode{false}); + m_listener.set(m_socket.handle(), Condition::Readable); + } + + /** + * Set the connection handler, called when the connection succeed. + * + * @param handler the handler + */ + inline void setConnectionHandler(ConnectionHandler handler) + { + m_onConnection = std::move(handler); + } + + /** + * Set the disconnection handler, called when the server closed the connection. + * + * @param handler the handler + */ + inline void setDisconnectionHandler(DisconnectionHandler handler) + { + m_onDisconnection = std::move(handler); + } + + /** + * Set the read handler, called when we received something. + * + * @param handler the handler + */ + inline void setReadHandler(ReadHandler handler) + { + m_onRead = std::move(handler); + } + + /** + * Set the write handler, called when we successfully sent data. + * + * @param handler the handler + */ + inline void setWriteHandler(WriteHandler handler) + { + m_onWrite = std::move(handler); + } + + /** + * Set the error handler, called when unexpected error occurs. + * + * @param handler the handler + */ + inline void setErrorHandler(ErrorHandler handler) + { + m_onError = std::move(handler); + } + + /** + * Connect to a server, this function may connect immediately or not in any case the connection handler + * will be called when the connection completed. + * + * @param address the address to connect to + */ + void connect(const Address &address) noexcept + { + assert(m_socket.state() == State::Open); + + processConnect([&] () { m_socket.connect(address); }); + } + + /** + * Asynchronously send data to the server. + * + * @param str the data to append + */ + void send(std::string str) + { + m_output += str; + + /* Don't update the listener if there is a pending operation */ + if (m_socket.state() == State::Connected && m_socket.action() == Action::None && !m_output.empty()) { + m_listener.set(m_socket.handle(), Condition::Writable); + } + } + + /** + * Wait for the next event. + * + * @param timeout the time to wait in milliseconds + * @throw Error on errors + */ + void poll(int timeout = -1) noexcept + { + try { + auto st = m_listener.wait(timeout); + + if (m_socket.state() != State::Connected) { + /* Continue the connection */ + processConnect([&] () { m_socket.connect(); }); + } else { + /* Read / Write */ + processSync(st.flags); + } + } catch (const Error &error) { + if (error.code() == Error::Timeout) { + m_onTimeout(); + } else { + m_listener.remove(m_socket.handle()); + m_onError(error); + } + } + } +}; + +/* }}} */ + +} // !net + +} // !irccd + +#endif // !_SOCKETS_H_
--- a/lib/irccd/system.cpp Sat Mar 26 14:41:53 2016 +0100 +++ b/lib/irccd/system.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -67,8 +67,7 @@ # include <pwd.h> #endif -#include "private/filesystem.h" - +#include "filesystem.h" #include "logger.h" #include "system.h" #include "util.h"
--- a/lib/irccd/timer.h Sat Mar 26 14:41:53 2016 +0100 +++ b/lib/irccd/timer.h Wed Mar 30 13:52:47 2016 +0200 @@ -30,7 +30,7 @@ #include <mutex> #include <thread> -#include <irccd/private/signals.h> +#include "signals.h" namespace irccd { @@ -124,7 +124,6 @@ /** * Stop the timer, may be used by the user to stop it. * - * @pre isRunning() must return true * @note Thread-safe */ void stop();
--- a/lib/irccd/transport-client.h Sat Mar 26 14:41:53 2016 +0100 +++ b/lib/irccd/transport-client.h Wed Mar 30 13:52:47 2016 +0200 @@ -29,10 +29,9 @@ #include <stdexcept> #include <string> -#include <irccd/private/signals.h> -#include <irccd/private/sockets.h> - #include "server.h" +#include "signals.h" +#include "sockets.h" namespace irccd {
--- a/lib/irccd/transport-server.h Sat Mar 26 14:41:53 2016 +0100 +++ b/lib/irccd/transport-server.h Wed Mar 30 13:52:47 2016 +0200 @@ -28,8 +28,8 @@ #include <string> #include <irccd-config.h> -#include <irccd/private/sockets.h> +#include "sockets.h" #include "transport-client.h" namespace irccd {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/xdg.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,129 @@ +/* + * Xdg.cpp -- XDG directory specifications + * + * Copyright (c) 2013, 2014, 2015 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 <cstdlib> +#include <stdexcept> +#include <sstream> + +#include "xdg.h" + +namespace irccd { + +namespace { + +bool isabsolute(const std::string &path) +{ + return path.length() > 0 && path[0] == '/'; +} + +std::vector<std::string> split(const std::string &arg) +{ + std::stringstream iss(arg); + std::string item; + std::vector<std::string> elems; + + while (std::getline(iss, item, ':')) + if (isabsolute(item)) + elems.push_back(item); + + return elems; +} + +std::string envOrHome(const std::string &var, const std::string &repl) +{ + auto value = getenv(var.c_str()); + + if (value == nullptr || !isabsolute(value)) { + auto home = getenv("HOME"); + + if (home == nullptr) + throw std::runtime_error("could not get home directory"); + + return std::string(home) + "/" + repl; + } + + return value; +} + +std::vector<std::string> listOrDefaults(const std::string &var, const std::vector<std::string> &list) +{ + auto value = getenv(var.c_str()); + + if (!value) + return list; + + // No valid item at all? Use defaults + auto result = split(value); + + return (result.size() == 0) ? list : result; +} + +} // !namespace + +Xdg::Xdg() +{ + m_configHome = envOrHome("XDG_CONFIG_HOME", ".config"); + m_dataHome = envOrHome("XDG_DATA_HOME", ".local/share"); + m_cacheHome = envOrHome("XDG_CACHE_HOME", ".cache"); + + m_configDirs = listOrDefaults("XDG_CONFIG_DIRS", { "/etc/xdg" }); + m_dataDirs = listOrDefaults("XDG_DATA_DIRS", { "/usr/local/share", "/usr/share" }); + + /* + * Runtime directory is a special case and does not have a replacement, the + * application should manage this by itself. + */ + auto runtime = getenv("XDG_RUNTIME_DIR"); + if (runtime && isabsolute(runtime)) + m_runtimeDir = runtime; +} + +const std::string &Xdg::configHome() const noexcept +{ + return m_configHome; +} + +const std::string &Xdg::dataHome() const noexcept +{ + return m_dataHome; +} + +const std::string &Xdg::cacheHome() const noexcept +{ + return m_cacheHome; +} + +const std::string &Xdg::runtimeDir() const +{ + if (m_runtimeDir.size() == 0) + throw std::runtime_error("XDG_RUNTIME_DIR is not set"); + + return m_runtimeDir; +} + +const Xdg::List &Xdg::configDirs() const noexcept +{ + return m_configDirs; +} + +const Xdg::List &Xdg::dataDirs() const noexcept +{ + return m_dataDirs; +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/xdg.h Wed Mar 30 13:52:47 2016 +0200 @@ -0,0 +1,112 @@ +/* + * Xdg.h -- XDG directory specifications + * + * Copyright (c) 2013, 2014, 2015 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_XDG_H_ +#define _IRCCD_XDG_H_ + +/** + * @file Xdg.h + * @brief Read XDG standard specifications + */ + +#include <vector> +#include <string> + +namespace irccd { + +/** + * @class Xdg + * @brief XDG specifications + * + * Read and get XDG directories. This file contains exports thingies so it can + * compiles successfully on Windows but its usage is discouraged. + */ +class Xdg { +public: + /** + * list of directories. + */ + using List = std::vector<std::string>; + +private: + std::string m_configHome; + std::string m_dataHome; + std::string m_cacheHome; + std::string m_runtimeDir; + List m_configDirs; + List m_dataDirs; + +public: + /** + * Open an xdg instance and load directories. + * + * @throw std::runtime_error on failures + */ + Xdg(); + + /** + * Get the config directory. ${XDG_CONFIG_HOME} or ${HOME}/.config + * + * @return the config directory + */ + const std::string &configHome() const noexcept; + + /** + * Get the data directory. ${XDG_DATA_HOME} or ${HOME}/.local/share + * + * @return the data directory + */ + const std::string &dataHome() const noexcept; + + /** + * Get the cache directory. ${XDG_CACHE_HOME} or ${HOME}/.cache + * + * @return the cache directory + */ + const std::string &cacheHome() const noexcept; + + /** + * Get the runtime directory. ${XDG_RUNTIME_DIR} must be set, + * if not, it throws an exception. + * + * The XDG standard says that application should handle XDG_RUNTIME_DIR by + * themselves. + * + * @return the runtime directory + * @throw std::runtime_error on error + */ + const std::string &runtimeDir() const; + + /** + * Get the standard config directories. ${XDG_CONFIG_DIRS} or { "/etc/xdg" } + * + * @return the list of config directories + */ + const List &configDirs() const noexcept; + + /** + * Get the data directories. ${XDG_DATA_DIRS} or { "/usr/local/share", "/usr/share" } + * + * @return the list of data directories + */ + const List &dataDirs() const noexcept; +}; + +} // !irccd + +#endif // !_IRCCD_XDG_H_
--- a/tests/elapsedtimer/CMakeLists.txt Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/elapsedtimer/CMakeLists.txt Wed Mar 30 13:52:47 2016 +0200 @@ -19,5 +19,5 @@ irccd_define_test( NAME elapsedtimer SOURCES main.cpp - LIBRARIES common + LIBRARIES libirccd )
--- a/tests/elapsedtimer/main.cpp Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/elapsedtimer/main.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -20,7 +20,7 @@ #include <gtest/gtest.h> -#include <elapsed-timer.h> +#include <irccd/elapsed-timer.h> using namespace irccd; using namespace std::chrono_literals;
--- a/tests/js-elapsedtimer/CMakeLists.txt Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/js-elapsedtimer/CMakeLists.txt Wed Mar 30 13:52:47 2016 +0200 @@ -18,14 +18,7 @@ irccd_define_test( NAME js-elapsedtimer - SOURCES - ${irccd_SOURCE_DIR}/js.cpp - ${irccd_SOURCE_DIR}/js.cpp - ${irccd_SOURCE_DIR}/js-irccd.cpp - ${irccd_SOURCE_DIR}/js-irccd.h - ${irccd_SOURCE_DIR}/js-elapsed-timer.cpp - ${irccd_SOURCE_DIR}/js-elapsed-timer.h - main.cpp - LIBRARIES common extern-duktape + SOURCES main.cpp + LIBRARIES libirccd )
--- a/tests/js-elapsedtimer/main.cpp Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/js-elapsedtimer/main.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -20,8 +20,8 @@ #include <thread> -#include <js-irccd.h> -#include <js-elapsed-timer.h> +#include <irccd/js-irccd.h> +#include <irccd/js-elapsed-timer.h> using namespace irccd; using namespace std::chrono_literals; @@ -33,7 +33,7 @@ class TestElapsedTimer : public testing::Test { protected: - js::Context m_context; + duk::Context m_context; TestElapsedTimer() { @@ -50,62 +50,93 @@ TEST_F(TestElapsedTimer, standard) { - m_context.peval(js::Script{"timer = new Irccd.ElapsedTimer();"}); + try { + if (duk::pevalString(m_context, "timer = new Irccd.ElapsedTimer();") != 0) + throw duk::error(m_context, -1); + + std::this_thread::sleep_for(300ms); - std::this_thread::sleep_for(300ms); + if (duk::pevalString(m_context, "result = timer.elapsed();") != 0) + throw duk::error(m_context, -1); - m_context.peval(js::Script{"result = timer.elapsed();"}); - assertRange(m_context.getGlobal<int>("result"), 300); + assertRange(duk::getGlobal<int>(m_context, "result"), 300); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } } TEST_F(TestElapsedTimer, reset) { - m_context.peval(js::Script{"timer = new Irccd.ElapsedTimer();"}); + try { + if (duk::pevalString(m_context, "timer = new Irccd.ElapsedTimer();") != 0) + throw duk::error(m_context, -1); + + std::this_thread::sleep_for(300ms); - std::this_thread::sleep_for(300ms); + if (duk::pevalString(m_context, "timer.reset(); result = timer.elapsed();") != 0) + throw duk::error(m_context, -1); - m_context.peval(js::Script{"timer.reset(); result = timer.elapsed();"}); - assertRange(m_context.getGlobal<int>("result"), 0); + assertRange(duk::getGlobal<int>(m_context, "result"), 0); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } } TEST_F(TestElapsedTimer, pause) { - m_context.peval(js::Script{"timer = new Irccd.ElapsedTimer();"}); + try { + if (duk::pevalString(m_context, "timer = new Irccd.ElapsedTimer();") != 0) + throw duk::error(m_context, -1); + + /* + * Simulate a pause in the game like this: + * + * start pause restart elapsed + * | 10ms |.5ms.| 6ms | + * + * Since the game was paused, the 5ms must not be totalized. + */ + std::this_thread::sleep_for(10ms); - /* - * Simulate a pause in the game like this: - * - * start pause restart elapsed - * | 10ms |.5ms.| 6ms | - * - * Since the game was paused, the 5ms must not be totalized. - */ - std::this_thread::sleep_for(10ms); + if (duk::pevalString(m_context, "timer.pause();") != 0) + throw duk::error(m_context, -1); + + std::this_thread::sleep_for(5ms); + + if (duk::pevalString(m_context, "timer.restart();") != 0) + throw duk::error(m_context, -1); - m_context.peval(js::Script{"timer.pause();"}); + std::this_thread::sleep_for(6ms); - std::this_thread::sleep_for(5ms); + if (duk::pevalString(m_context, "result = timer.elapsed()") != 0) + throw duk::error(m_context, -1); - m_context.peval(js::Script{"timer.restart();"}); - - std::this_thread::sleep_for(6ms); - - m_context.peval(js::Script{"result = timer.elapsed()"}); - assertRange(m_context.getGlobal<int>("result"), 16); + assertRange(duk::getGlobal<int>(m_context, "result"), 16); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } } TEST_F(TestElapsedTimer, doublecheck) { - m_context.peval(js::Script{"timer = new Irccd.ElapsedTimer();"}); + try { + if (duk::pevalString(m_context, "timer = new Irccd.ElapsedTimer();") != 0) + throw duk::error(m_context, -1); - std::this_thread::sleep_for(50ms); + std::this_thread::sleep_for(50ms); + + if (duk::pevalString(m_context, "result = timer.elapsed()") != 0) + throw duk::error(m_context, -1); - m_context.peval(js::Script{"result = timer.elapsed()"}); + std::this_thread::sleep_for(50ms); + + if (duk::pevalString(m_context, "result = timer.elapsed()") != 0) + throw duk::error(m_context, -1); - std::this_thread::sleep_for(50ms); - - m_context.peval(js::Script{"result = timer.elapsed()"}); - assertRange(m_context.getGlobal<int>("result"), 100); + assertRange(duk::getGlobal<int>(m_context, "result"), 100); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } } int main(int argc, char **argv)
--- a/tests/js-file/CMakeLists.txt Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/js-file/CMakeLists.txt Wed Mar 30 13:52:47 2016 +0200 @@ -18,15 +18,8 @@ irccd_define_test( NAME js-file - SOURCES - ${irccd_SOURCE_DIR}/js.cpp - ${irccd_SOURCE_DIR}/js.cpp - ${irccd_SOURCE_DIR}/js-file.cpp - ${irccd_SOURCE_DIR}/js-file.h - ${irccd_SOURCE_DIR}/js-irccd.cpp - ${irccd_SOURCE_DIR}/js-irccd.h - main.cpp - LIBRARIES common extern-duktape + SOURCES main.cpp + LIBRARIES libirccd ) #
--- a/tests/js-file/main.cpp Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/js-file/main.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -20,22 +20,23 @@ #include <gtest/gtest.h> -#include <js-file.h> -#include <js-irccd.h> +#include <irccd/js-file.h> +#include <irccd/js-irccd.h> using namespace irccd; TEST(TestJsFile, functionBasename) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsFile(ctx); try { - ctx.peval(js::Script{"result = Irccd.File.basename('/usr/local/etc/irccd.conf');"}); + if (duk::pevalString(ctx, "result = Irccd.File.basename('/usr/local/etc/irccd.conf');") != 0) + throw duk::error(ctx, -1); - ASSERT_EQ("irccd.conf", ctx.getGlobal<std::string>("result")); + ASSERT_EQ("irccd.conf", duk::getGlobal<std::string>(ctx, "result")); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -43,15 +44,15 @@ TEST(TestJsFile, functionDirname) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsFile(ctx); try { - ctx.peval(js::Script{"result = Irccd.File.dirname('/usr/local/etc/irccd.conf');"}); + duk::pevalString(ctx, "result = Irccd.File.dirname('/usr/local/etc/irccd.conf');"); - ASSERT_EQ("/usr/local/etc", ctx.getGlobal<std::string>("result")); + ASSERT_EQ("/usr/local/etc", duk::getGlobal<std::string>(ctx,"result")); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -59,16 +60,16 @@ TEST(TestJsFile, functionExists) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsFile(ctx); try { - ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY); - ctx.peval(js::Script{"result = Irccd.File.exists(directory + '/file.txt')"}); + duk::putGlobal(ctx, "directory", IRCCD_TESTS_DIRECTORY); + duk::pevalString(ctx, "result = Irccd.File.exists(directory + '/file.txt')"); - ASSERT_TRUE(ctx.getGlobal<bool>("result")); + ASSERT_TRUE(duk::getGlobal<bool>(ctx,"result")); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -76,15 +77,15 @@ TEST(TestJsFile, functionExists2) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsFile(ctx); try { - ctx.peval(js::Script{"result = Irccd.File.exists('file_which_does_not_exist.txt')"}); + duk::pevalString(ctx, "result = Irccd.File.exists('file_which_does_not_exist.txt')"); - ASSERT_FALSE(ctx.getGlobal<bool>("result")); + ASSERT_FALSE(duk::getGlobal<bool>(ctx,"result")); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -97,13 +98,14 @@ std::ofstream out("test-js-fs.remove"); } - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsFile(ctx); try { - ctx.peval(js::Script{"Irccd.File.remove('test-js-fs.remove');"}); + if (duk::pevalString(ctx, "Irccd.File.remove('test-js-fs.remove');") != 0) + throw duk::error(ctx, -1); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -115,19 +117,23 @@ TEST(TestJsFile, methodBasename) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsFile(ctx); try { - ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY); - ctx.peval(js::Script{ + duk::putGlobal(ctx,"directory", IRCCD_TESTS_DIRECTORY); + + auto ret = duk::pevalString(ctx, "f = new Irccd.File(directory + '/level-1/file-1.txt', 'r');" "result = f.basename();" - }); + ); - ASSERT_EQ("file-1.txt", ctx.getGlobal<std::string>("result")); + if (ret != 0) + throw duk::error(ctx, -1); + + ASSERT_EQ("file-1.txt", duk::getGlobal<std::string>(ctx,"result")); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -135,20 +141,24 @@ TEST(TestJsFile, methodBasenameClosed) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsFile(ctx); try { - ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY); - ctx.peval(js::Script{ + duk::putGlobal(ctx,"directory", IRCCD_TESTS_DIRECTORY); + + auto ret = duk::pevalString(ctx, "f = new Irccd.File(directory + '/level-1/file-1.txt', 'r');" "f.close();" "result = f.basename();" - }); + ); - ASSERT_EQ("file-1.txt", ctx.getGlobal<std::string>("result")); + if (ret != 0) + throw duk::error(ctx, -1); + + ASSERT_EQ("file-1.txt", duk::getGlobal<std::string>(ctx,"result")); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -156,19 +166,23 @@ TEST(TestJsFile, methodDirname) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsFile(ctx); try { - ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY); - ctx.peval(js::Script{ + duk::putGlobal(ctx,"directory", IRCCD_TESTS_DIRECTORY); + + auto ret = duk::pevalString(ctx, "f = new Irccd.File(directory + '/level-1/file-1.txt', 'r');" "result = f.dirname();" - }); + ); - ASSERT_EQ(std::string{IRCCD_TESTS_DIRECTORY "/level-1"}, ctx.getGlobal<std::string>("result")); + if (ret != 0) + throw duk::error(ctx, -1); + + ASSERT_EQ(std::string{IRCCD_TESTS_DIRECTORY "/level-1"}, duk::getGlobal<std::string>(ctx, "result")); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -176,20 +190,24 @@ TEST(TestJsFile, methodDirnameClosed) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsFile(ctx); try { - ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY); - ctx.peval(js::Script{ + duk::putGlobal(ctx, "directory", IRCCD_TESTS_DIRECTORY); + + auto ret = duk::pevalString(ctx, "f = new Irccd.File(directory + '/level-1/file-1.txt', 'r');" "f.close();" "result = f.dirname();" - }); + ); - ASSERT_EQ(std::string{IRCCD_TESTS_DIRECTORY "/level-1"}, ctx.getGlobal<std::string>("result")); + if (ret != 0) + throw duk::error(ctx, -1); + + ASSERT_EQ(std::string{IRCCD_TESTS_DIRECTORY "/level-1"}, duk::getGlobal<std::string>(ctx, "result")); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -197,20 +215,24 @@ TEST(TestJsFile, methodSeek1) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsFile(ctx); try { - ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY); - ctx.peval(js::Script{ + duk::putGlobal(ctx, "directory", IRCCD_TESTS_DIRECTORY); + + auto ret = duk::pevalString(ctx, "f = new Irccd.File(directory + '/file.txt', 'r');" "f.seek(Irccd.File.SeekSet, 4);" "result = f.read(1);" - }); + ); - ASSERT_EQ(".", ctx.getGlobal<std::string>("result")); + if (ret != 0) + throw duk::error(ctx, -1); + + ASSERT_EQ(".", duk::getGlobal<std::string>(ctx, "result")); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -219,22 +241,27 @@ TEST(TestJsFile, methodSeek1Closed) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsFile(ctx); try { - ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY); - ctx.peval(js::Script{ + duk::putGlobal(ctx, "directory", IRCCD_TESTS_DIRECTORY); + + auto ret = duk::pevalString(ctx, "f = new Irccd.File(directory + '/file.txt', 'r');" "f.close();" "f.seek(Irccd.File.SeekSet, 4);" "result = f.read(1);" "result = typeof (result) === \"undefined\";" - }); + ); + - ASSERT_TRUE(ctx.getGlobal<bool>("result")); + if (ret != 0) + throw duk::error(ctx, -1); + + ASSERT_TRUE(duk::getGlobal<bool>(ctx, "result")); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -242,21 +269,26 @@ TEST(TestJsFile, methodSeek2) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsFile(ctx); try { - ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY); - ctx.peval(js::Script{ + duk::putGlobal(ctx, "directory", IRCCD_TESTS_DIRECTORY); + + auto ret = duk::pevalString(ctx, "f = new Irccd.File(directory + '/file.txt', 'r');" "f.seek(Irccd.File.SeekSet, 2);" "f.seek(Irccd.File.SeekCur, 2);" "result = f.read(1);" - }); + ); + - ASSERT_EQ(".", ctx.getGlobal<std::string>("result")); + if (ret != 0) + throw duk::error(ctx, -1); + + ASSERT_EQ(".", duk::getGlobal<std::string>(ctx, "result")); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -264,23 +296,28 @@ TEST(TestJsFile, methodSeek2Closed) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsFile(ctx); try { - ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY); - ctx.peval(js::Script{ + duk::putGlobal(ctx, "directory", IRCCD_TESTS_DIRECTORY); + + auto ret = duk::pevalString(ctx, "f = new Irccd.File(directory + '/file.txt', 'r');" "f.close();" "f.seek(Irccd.File.SeekSet, 2);" "f.seek(Irccd.File.SeekCur, 2);" "result = f.read(1);" "result = typeof (result) === \"undefined\";" - }); + ); + - ASSERT_TRUE(ctx.getGlobal<bool>("result")); + if (ret != 0) + throw duk::error(ctx, -1); + + ASSERT_TRUE(duk::getGlobal<bool>(ctx, "result")); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -288,20 +325,25 @@ TEST(TestJsFile, methodSeek3) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsFile(ctx); try { - ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY); - ctx.peval(js::Script{ + duk::putGlobal(ctx, "directory", IRCCD_TESTS_DIRECTORY); + + auto ret = duk::pevalString(ctx, "f = new Irccd.File(directory + '/file.txt', 'r');" "f.seek(Irccd.File.SeekEnd, -2);" "result = f.read(1);" - }); + ); + - ASSERT_EQ("x", ctx.getGlobal<std::string>("result")); + if (ret != 0) + throw duk::error(ctx, -1); + + ASSERT_EQ("x", duk::getGlobal<std::string>(ctx, "result")); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -309,22 +351,27 @@ TEST(TestJsFile, methodSeek3Closed) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsFile(ctx); try { - ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY); - ctx.peval(js::Script{ + duk::putGlobal(ctx, "directory", IRCCD_TESTS_DIRECTORY); + + auto ret = duk::pevalString(ctx, "f = new Irccd.File(directory + '/file.txt', 'r');" "f.close();" "f.seek(Irccd.File.SeekEnd, -2);" "result = f.read(1);" "result = typeof (result) === \"undefined\";" - }); + ); + - ASSERT_TRUE(ctx.getGlobal<bool>("result")); + if (ret != 0) + throw duk::error(ctx, -1); + + ASSERT_TRUE(duk::getGlobal<bool>(ctx, "result")); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -332,24 +379,28 @@ TEST(TestJsFile, methodReadline) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsFile(ctx); try { - ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY); - ctx.peval(js::Script{ + duk::putGlobal(ctx, "directory", IRCCD_TESTS_DIRECTORY); + + auto ret = duk::pevalString(ctx, "lines = [];" "f = new Irccd.File(directory + '/lines.txt', 'r');" "for (var s; s = f.readline(); ) {" " lines.push(s);" "}" - }); + ); + + if (ret != 0) + throw duk::error(ctx, -1); std::vector<std::string> expected{"a", "b", "c"}; - ASSERT_EQ(expected, ctx.getGlobal<std::vector<std::string>>("lines")); + ASSERT_EQ(expected, duk::getGlobal<std::vector<std::string>>(ctx, "lines")); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -357,25 +408,29 @@ TEST(TestJsFile, methodReadlineClosed) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsFile(ctx); try { - ctx.putGlobal("directory", IRCCD_TESTS_DIRECTORY); - ctx.peval(js::Script{ + duk::putGlobal(ctx, "directory", IRCCD_TESTS_DIRECTORY); + + auto ret = duk::pevalString(ctx, "lines = [];" "f = new Irccd.File(directory + '/lines.txt', 'r');" "f.close();" "for (var s; s = f.readline(); ) {" " lines.push(s);" "}" - }); + ); + + if (ret != 0) + throw duk::error(ctx, -1); std::vector<std::string> expected; - ASSERT_EQ(expected, ctx.getGlobal<std::vector<std::string>>("lines")); + ASSERT_EQ(expected, duk::getGlobal<std::vector<std::string>>(ctx, "lines")); } catch (const std::exception &ex) { FAIL() << ex.what(); }
--- a/tests/js-irccd/CMakeLists.txt Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/js-irccd/CMakeLists.txt Wed Mar 30 13:52:47 2016 +0200 @@ -18,11 +18,6 @@ irccd_define_test( NAME js-irccd - SOURCES - ${irccd_SOURCE_DIR}/js.cpp - ${irccd_SOURCE_DIR}/js.cpp - ${irccd_SOURCE_DIR}/js-irccd.cpp - ${irccd_SOURCE_DIR}/js-irccd.h - main.cpp - LIBRARIES common extern-duktape + SOURCES main.cpp + LIBRARIES libirccd )
--- a/tests/js-irccd/main.cpp Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/js-irccd/main.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -20,28 +20,30 @@ #include <irccd-config.h> -#include <logger.h> - -#include <js-irccd.h> +#include <irccd/js-irccd.h> +#include <irccd/logger.h> using namespace irccd; TEST(TestJsIrccd, version) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); try { - ctx.peval(js::Script{ + auto ret = duk::pevalString(ctx, "major = Irccd.version.major;" "minor = Irccd.version.minor;" "patch = Irccd.version.patch;" - }); + ); - ASSERT_EQ(IRCCD_VERSION_MAJOR, ctx.getGlobal<int>("major")); - ASSERT_EQ(IRCCD_VERSION_MINOR, ctx.getGlobal<int>("minor")); - ASSERT_EQ(IRCCD_VERSION_PATCH, ctx.getGlobal<int>("patch")); + if (ret != 0) + throw duk::error(ctx, -1); + + ASSERT_EQ(IRCCD_VERSION_MAJOR, duk::getGlobal<int>(ctx, "major")); + ASSERT_EQ(IRCCD_VERSION_MINOR, duk::getGlobal<int>(ctx, "minor")); + ASSERT_EQ(IRCCD_VERSION_PATCH, duk::getGlobal<int>(ctx, "patch")); } catch (const std::exception &ex) { FAIL() << ex.what(); }
--- a/tests/js-logger/CMakeLists.txt Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/js-logger/CMakeLists.txt Wed Mar 30 13:52:47 2016 +0200 @@ -18,13 +18,6 @@ irccd_define_test( NAME js-logger - SOURCES - ${irccd_SOURCE_DIR}/js.cpp - ${irccd_SOURCE_DIR}/js.cpp - ${irccd_SOURCE_DIR}/js-irccd.cpp - ${irccd_SOURCE_DIR}/js-irccd.h - ${irccd_SOURCE_DIR}/js-logger.cpp - ${irccd_SOURCE_DIR}/js-logger.h - main.cpp - LIBRARIES common extern-duktape + SOURCES main.cpp + LIBRARIES libirccd )
--- a/tests/js-logger/main.cpp Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/js-logger/main.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -20,10 +20,9 @@ #include <irccd-config.h> -#include <logger.h> - -#include <js-irccd.h> -#include <js-logger.h> +#include <irccd/js-irccd.h> +#include <irccd/js-logger.h> +#include <irccd/logger.h> using namespace irccd; @@ -57,14 +56,16 @@ TEST(TestJsLogger, info) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsLogger(ctx); try { - ctx.putGlobal("\xff""\xff""name", "test"); - ctx.peval(js::Script{"Irccd.Logger.info(\"hello!\");"}); + duk::putGlobal(ctx, "\xff""\xff""name", "test"); + + if (duk::pevalString(ctx, "Irccd.Logger.info(\"hello!\");") != 0) + throw duk::error(ctx, -1); ASSERT_EQ("plugin test: hello!", lineInfo); } catch (const std::exception &ex) { @@ -74,14 +75,16 @@ TEST(TestJsLogger, warning) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsLogger(ctx); try { - ctx.putGlobal("\xff""\xff""name", "test"); - ctx.peval(js::Script{"Irccd.Logger.warning(\"FAIL!\");"}); + duk::putGlobal(ctx, "\xff""\xff""name", "test"); + + if (duk::pevalString(ctx, "Irccd.Logger.warning(\"FAIL!\");") != 0) + throw duk::error(ctx, -1); ASSERT_EQ("plugin test: FAIL!", lineWarning); } catch (const std::exception &ex) { @@ -93,14 +96,16 @@ TEST(TestJsLogger, debug) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsLogger(ctx); try { - ctx.putGlobal("\xff""\xff""name", "test"); - ctx.peval(js::Script{"Irccd.Logger.debug(\"starting\");"}); + duk::putGlobal(ctx, "\xff""\xff""name", "test"); + + if (duk::pevalString(ctx, "Irccd.Logger.debug(\"starting\");") != 0) + throw duk::error(ctx, -1); ASSERT_EQ("plugin test: starting", lineDebug); } catch (const std::exception &ex) {
--- a/tests/js-system/CMakeLists.txt Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/js-system/CMakeLists.txt Wed Mar 30 13:52:47 2016 +0200 @@ -18,16 +18,7 @@ irccd_define_test( NAME js-system - SOURCES - ${irccd_SOURCE_DIR}/js.cpp - ${irccd_SOURCE_DIR}/js.h - ${irccd_SOURCE_DIR}/js-file.cpp - ${irccd_SOURCE_DIR}/js-file.h - ${irccd_SOURCE_DIR}/js-irccd.cpp - ${irccd_SOURCE_DIR}/js-irccd.h - ${irccd_SOURCE_DIR}/js-system.cpp - ${irccd_SOURCE_DIR}/js-system.h - main.cpp - LIBRARIES common extern-duktape + SOURCES main.cpp + LIBRARIES libirccd )
--- a/tests/js-system/main.cpp Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/js-system/main.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -18,24 +18,23 @@ #include <gtest/gtest.h> -#include <system.h> - -#include <js-irccd.h> -#include <js-system.h> +#include <irccd/js-irccd.h> +#include <irccd/js-system.h> +#include <irccd/system.h> using namespace irccd; TEST(TestJsSystem, home) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsSystem(ctx); try { - ctx.peval(js::Script{"result = Irccd.System.home();"}); + duk::pevalString(ctx, "result = Irccd.System.home();"); - ASSERT_EQ(sys::home(), ctx.getGlobal<std::string>("result")); + ASSERT_EQ(sys::home(), duk::getGlobal<std::string>(ctx, "result")); } catch (const std::exception &ex) { FAIL() << ex.what(); }
--- a/tests/js-timer/CMakeLists.txt Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/js-timer/CMakeLists.txt Wed Mar 30 13:52:47 2016 +0200 @@ -18,49 +18,10 @@ irccd_define_test( NAME js-timer - SOURCES - ${irccd_SOURCE_DIR}/irccd.cpp - ${irccd_SOURCE_DIR}/irccd.h - ${irccd_SOURCE_DIR}/js.cpp - ${irccd_SOURCE_DIR}/js.h - ${irccd_SOURCE_DIR}/js-directory.cpp - ${irccd_SOURCE_DIR}/js-directory.h - ${irccd_SOURCE_DIR}/js-elapsed-timer.cpp - ${irccd_SOURCE_DIR}/js-elapsed-timer.h - ${irccd_SOURCE_DIR}/js-file.cpp - ${irccd_SOURCE_DIR}/js-file.h - ${irccd_SOURCE_DIR}/js-irccd.cpp - ${irccd_SOURCE_DIR}/js-irccd.h - ${irccd_SOURCE_DIR}/js-logger.cpp - ${irccd_SOURCE_DIR}/js-logger.h - ${irccd_SOURCE_DIR}/js-plugin.cpp - ${irccd_SOURCE_DIR}/js-plugin.h - ${irccd_SOURCE_DIR}/js-server.cpp - ${irccd_SOURCE_DIR}/js-system.cpp - ${irccd_SOURCE_DIR}/js-system.h - ${irccd_SOURCE_DIR}/js-timer.cpp - ${irccd_SOURCE_DIR}/js-timer.h - ${irccd_SOURCE_DIR}/js-unicode.cpp - ${irccd_SOURCE_DIR}/js-util.cpp - ${irccd_SOURCE_DIR}/js-util.h - ${irccd_SOURCE_DIR}/plugin.cpp - ${irccd_SOURCE_DIR}/plugin.h - ${irccd_SOURCE_DIR}/rule.cpp - ${irccd_SOURCE_DIR}/rule.h - ${irccd_SOURCE_DIR}/server.cpp - ${irccd_SOURCE_DIR}/server.h - ${irccd_SOURCE_DIR}/server-state.cpp - ${irccd_SOURCE_DIR}/server-state.h - ${irccd_SOURCE_DIR}/timer.cpp - ${irccd_SOURCE_DIR}/timer.h - ${irccd_SOURCE_DIR}/transport-client.cpp - ${irccd_SOURCE_DIR}/transport-client.h - ${irccd_SOURCE_DIR}/transport-server.cpp - ${irccd_SOURCE_DIR}/transport-server.h - main.cpp + SOURCES main.cpp RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/timer-single.js ${CMAKE_CURRENT_SOURCE_DIR}/timer-repeat.js ${CMAKE_CURRENT_SOURCE_DIR}/timer-pending.js - LIBRARIES common extern-duktape extern-ircclient + LIBRARIES libirccd )
--- a/tests/js-timer/main.cpp Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/js-timer/main.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -18,9 +18,9 @@ #include <gtest/gtest.h> -#include <irccd.h> -#include <elapsed-timer.h> -#include <system.h> +#include <irccd/elapsed-timer.h> +#include <irccd/irccd.h> +#include <irccd/system.h> using namespace irccd; @@ -38,7 +38,7 @@ irccd.dispatch(); } - ASSERT_EQ(1, plugin->context().getGlobal<int>("count")); + ASSERT_EQ(1, duk::getGlobal<int>(plugin->context(), "count")); } TEST(Basic, repeat) @@ -55,7 +55,7 @@ irccd.dispatch(); } - ASSERT_GE(plugin->context().getGlobal<int>("count"), 5); + ASSERT_GE(duk::getGlobal<int>(plugin->context(), "count"), 5); } #if 0
--- a/tests/js-unicode/CMakeLists.txt Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/js-unicode/CMakeLists.txt Wed Mar 30 13:52:47 2016 +0200 @@ -18,13 +18,6 @@ irccd_define_test( NAME js-unicode - SOURCES - ${irccd_SOURCE_DIR}/js.cpp - ${irccd_SOURCE_DIR}/js.h - ${irccd_SOURCE_DIR}/js-irccd.cpp - ${irccd_SOURCE_DIR}/js-irccd.h - ${irccd_SOURCE_DIR}/js-unicode.cpp - ${irccd_SOURCE_DIR}/js-unicode.h - main.cpp - LIBRARIES common extern-duktape + SOURCES main.cpp + LIBRARIES libirccd )
--- a/tests/js-unicode/main.cpp Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/js-unicode/main.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -22,24 +22,24 @@ #include <gtest/gtest.h> -#include <js-irccd.h> -#include <js-unicode.h> +#include <irccd/js-irccd.h> +#include <irccd/js-unicode.h> using namespace irccd; TEST(TestJsUnicode, isLetter) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsUnicode(ctx); try { - ctx.peval(js::Script{"result = Irccd.Unicode.isLetter(String('é').charCodeAt(0));"}); - ASSERT_TRUE(ctx.getGlobal<bool>("result")); + duk::pevalString(ctx, "result = Irccd.Unicode.isLetter(String('é').charCodeAt(0));"); + ASSERT_TRUE(duk::getGlobal<bool>(ctx, "result")); - ctx.peval(js::Script{"result = Irccd.Unicode.isLetter(String('€').charCodeAt(0));"}); - ASSERT_FALSE(ctx.getGlobal<bool>("result")); + duk::pevalString(ctx, "result = Irccd.Unicode.isLetter(String('€').charCodeAt(0));"); + ASSERT_FALSE(duk::getGlobal<bool>(ctx, "result")); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -47,17 +47,17 @@ TEST(TestJsUnicode, isLower) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsUnicode(ctx); try { - ctx.peval(js::Script{"result = Irccd.Unicode.isLower(String('é').charCodeAt(0));"}); - ASSERT_TRUE(ctx.getGlobal<bool>("result")); + duk::pevalString(ctx, "result = Irccd.Unicode.isLower(String('é').charCodeAt(0));"); + ASSERT_TRUE(duk::getGlobal<bool>(ctx, "result")); - ctx.peval(js::Script{"result = Irccd.Unicode.isLower(String('É').charCodeAt(0));"}); - ASSERT_FALSE(ctx.getGlobal<bool>("result")); + duk::pevalString(ctx, "result = Irccd.Unicode.isLower(String('É').charCodeAt(0));"); + ASSERT_FALSE(duk::getGlobal<bool>(ctx, "result")); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -65,17 +65,17 @@ TEST(TestJsUnicode, isUpper) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsUnicode(ctx); try { - ctx.peval(js::Script{"result = Irccd.Unicode.isUpper(String('É').charCodeAt(0));"}); - ASSERT_TRUE(ctx.getGlobal<bool>("result")); + duk::pevalString(ctx, "result = Irccd.Unicode.isUpper(String('É').charCodeAt(0));"); + ASSERT_TRUE(duk::getGlobal<bool>(ctx, "result")); - ctx.peval(js::Script{"result = Irccd.Unicode.isUpper(String('é').charCodeAt(0));"}); - ASSERT_FALSE(ctx.getGlobal<bool>("result")); + duk::pevalString(ctx, "result = Irccd.Unicode.isUpper(String('é').charCodeAt(0));"); + ASSERT_FALSE(duk::getGlobal<bool>(ctx, "result")); } catch (const std::exception &ex) { FAIL() << ex.what(); }
--- a/tests/js-util/CMakeLists.txt Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/js-util/CMakeLists.txt Wed Mar 30 13:52:47 2016 +0200 @@ -18,13 +18,6 @@ irccd_define_test( NAME js-util - SOURCES - ${irccd_SOURCE_DIR}/js.cpp - ${irccd_SOURCE_DIR}/js.cpp - ${irccd_SOURCE_DIR}/js-irccd.cpp - ${irccd_SOURCE_DIR}/js-irccd.h - ${irccd_SOURCE_DIR}/js-util.cpp - ${irccd_SOURCE_DIR}/js-util.h - main.cpp - LIBRARIES common extern-duktape extern-ircclient + SOURCES main.cpp + LIBRARIES libirccd )
--- a/tests/js-util/main.cpp Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/js-util/main.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -18,23 +18,25 @@ #include <gtest/gtest.h> -#include <js-irccd.h> -#include <js-util.h> +#include <irccd/js-irccd.h> +#include <irccd/js-util.h> using namespace irccd; TEST(TestJsUtil, formatSimple) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsUtil(ctx); try { - ctx.peval(js::Script{ + auto ret = duk::pevalString(ctx, "result = Irccd.Util.format(\"#{target}\", { target: \"markand\" })" - }); + ); + if (ret != 0) + throw duk::error(ctx, -1); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -42,15 +44,16 @@ TEST(TestJsUtil, splituser) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsUtil(ctx); try { - ctx.peval(js::Script{"result = Irccd.Util.splituser(\"user!~user@hyper/super/host\");"}); + if (duk::pevalString(ctx, "result = Irccd.Util.splituser(\"user!~user@hyper/super/host\");") != 0) + throw duk::error(ctx, -1); - ASSERT_EQ("user", ctx.getGlobal<std::string>("result")); + ASSERT_EQ("user", duk::getGlobal<std::string>(ctx, "result")); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -58,15 +61,16 @@ TEST(TestJsUtil, splithost) { - js::Context ctx; + duk::Context ctx; loadJsIrccd(ctx); loadJsUtil(ctx); try { - ctx.peval(js::Script{"result = Irccd.Util.splithost(\"user!~user@hyper/super/host\");"}); + if (duk::pevalString(ctx, "result = Irccd.Util.splithost(\"user!~user@hyper/super/host\");") != 0) + throw duk::error(ctx, -1); - ASSERT_EQ("!~user@hyper/super/host", ctx.getGlobal<std::string>("result")); + ASSERT_EQ("!~user@hyper/super/host", duk::getGlobal<std::string>(ctx, "result")); } catch (const std::exception &ex) { FAIL() << ex.what(); }
--- a/tests/path/CMakeLists.txt Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/path/CMakeLists.txt Wed Mar 30 13:52:47 2016 +0200 @@ -19,5 +19,5 @@ irccd_define_test( NAME path SOURCES main.cpp - LIBRARIES common + LIBRARIES libirccd )
--- a/tests/path/main.cpp Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/path/main.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -20,8 +20,8 @@ #include <irccd-config.h> -#include <logger.h> -#include <path.h> +#include <irccd/logger.h> +#include <irccd/path.h> namespace irccd {
--- a/tests/rules/CMakeLists.txt Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/rules/CMakeLists.txt Wed Mar 30 13:52:47 2016 +0200 @@ -18,9 +18,6 @@ irccd_define_test( NAME rules - SOURCES - ${irccd_SOURCE_DIR}/rule.cpp - ${irccd_SOURCE_DIR}/rule.h - main.cpp - LIBRARIES common + SOURCES main.cpp + LIBRARIES libirccd )
--- a/tests/rules/main.cpp Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/rules/main.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -18,7 +18,7 @@ #include <gtest/gtest.h> -#include <rule.h> +#include <irccd/rule.h> namespace irccd {
--- a/tests/timer/CMakeLists.txt Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/timer/CMakeLists.txt Wed Mar 30 13:52:47 2016 +0200 @@ -18,9 +18,6 @@ irccd_define_test( NAME timer - SOURCES - ${irccd_SOURCE_DIR}/timer.cpp - ${irccd_SOURCE_DIR}/timer.h - main.cpp - LIBRARIES common + SOURCES main.cpp + LIBRARIES libirccd )
--- a/tests/timer/main.cpp Sat Mar 26 14:41:53 2016 +0100 +++ b/tests/timer/main.cpp Wed Mar 30 13:52:47 2016 +0200 @@ -18,8 +18,8 @@ #include <gtest/gtest.h> -#include <elapsed-timer.h> -#include <timer.h> +#include <irccd/elapsed-timer.h> +#include <irccd/timer.h> using namespace irccd; using namespace std::chrono_literals;