Mercurial > irccd
changeset 73:8ee1178f1219
Irccd: port as a library almost complete, #429
line wrap: on
line diff
--- a/CMakeLists.txt Thu Mar 24 14:07:30 2016 +0100 +++ b/CMakeLists.txt Fri Mar 25 13:58:12 2016 +0100 @@ -49,6 +49,7 @@ project(irccd) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${irccd_SOURCE_DIR}/cmake/packages) +set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) include(CMakeParseArguments)
--- a/irccdctl/CMakeLists.txt Thu Mar 24 14:07:30 2016 +0100 +++ b/irccdctl/CMakeLists.txt Fri Mar 25 13:58:12 2016 +0100 @@ -24,3 +24,7 @@ SOURCES CMakeLists.txt main.cpp LIBRARIES libirccd ) + +if (UNIX) + set_target_properties(irccdctl PROPERTIES LINK_FLAGS -pthread) +endif ()
--- a/lib/irccd/application.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/application.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -48,6 +48,7 @@ Application::Application() { /* Register all commands */ + addCommand(std::make_unique<command::Help>()); addCommand(std::make_unique<command::PluginInfo>()); addCommand(std::make_unique<command::PluginList>()); addCommand(std::make_unique<command::PluginLoad>()); @@ -70,6 +71,7 @@ addCommand(std::make_unique<command::ServerPart>()); addCommand(std::make_unique<command::ServerReconnect>()); addCommand(std::make_unique<command::ServerTopic>()); + addCommand(std::make_unique<command::Watch>()); } } // !irccd
--- a/lib/irccd/command/command.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/command.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -1,3 +1,23 @@ +/* + * 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> @@ -5,74 +25,70 @@ #include "command.h" -namespace irccd { - -/* - * RemoteCommandRequest - * ------------------------------------------------------------------ - */ - -const std::string &RemoteCommandRequest::arg(unsigned index) const noexcept -{ - assert(index < m_args.size()); - - return m_args[index]; -} - -std::string RemoteCommandRequest::argOr(unsigned index, std::string defaultValue) const noexcept -{ - return index < m_args.size() ? m_args[index] : defaultValue; -} +using namespace std::string_literals; -const std::string &RemoteCommandRequest::option(const std::string &key) const noexcept -{ - assert(m_options.count(key) != 0); - - return m_options.find(key)->second; -} - -std::string RemoteCommandRequest::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; -} - -/* - * RemoteCommand - * ------------------------------------------------------------------ - */ +namespace irccd { std::string RemoteCommand::usage() const { std::ostringstream oss; - oss << "usage: sys::programName()"; + oss << "usage: " << sys::programName() << " " << m_name; /* Options summary */ if (options().size() > 0) oss << " [options...]"; - /* Arguments */ - for (const RemoteCommandArg &arg : args()) { - if (!arg.second) - oss << "["; + /* 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"; - oss << arg.first; + /* Options */ + if (options().size() > 0) { + oss << "Options:\n"; + + for (const auto &opt : options()) { + std::ostringstream optoss; - if (!arg.second) - oss << "]"; + /* 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 nullptr; + return json::object({}); } json::Value RemoteCommand::exec(Irccd &, const json::Value &) const
--- a/lib/irccd/command/command.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/command.h Fri Mar 25 13:58:12 2016 +0100 @@ -1,8 +1,25 @@ -#ifndef _REMOTE_COMMAND_H_ -#define _REMOTE_COMMAND_H_ +/* + * 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 <cstdint> #include <map> #include <vector> @@ -14,156 +31,163 @@ class Irccdctl; /** - * @class RemoteCommandOption - * @brief Describe a command line option + * @brief Command line arguments to irccdctl. + * + * This class contains the resolved arguments from command line that can apply to the command. */ -class RemoteCommandOption { +class RemoteCommandRequest { public: - enum { - Argument = (1 << 0) //!< option requires an argument - }; + using Options = std::multimap<std::string, std::string>; + using Args = std::vector<std::string>; private: - std::string m_id; - std::string m_simple; - std::string m_long; - std::string m_description; - std::uint8_t m_flags; + Options m_options; + Args m_args; public: /** - * Constructor an option description. - * - * @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 RemoteCommandOption(std::string id, - std::string simpleKey, - std::string longKey, - std::string description, - std::uint8_t flags = 0) noexcept - : m_id(std::move(id)) - , m_simple(std::move(simpleKey)) - , m_long(std::move(longKey)) - , m_description(std::move(description)) - , m_flags(flags) - { - 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. + * Construct the request. * - * @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 flags. - * - * @return the flags + * @param options the options + * @param args the arguments */ - inline std::uint8_t flags() const noexcept - { - return m_flags; - } -}; - -/** - * @brief List of command line options. - */ -using RemoteCommandOptions = std::vector<RemoteCommandOption>; - -using RemoteCommandArg = std::pair<std::string, bool>; - -/** - * @brief List of arguments to pass to the command. - * - * Any argument must have a non-empty name an can be optional if the boolean is set to false. - */ -using RemoteCommandArgs = std::vector<std::pair<std::string, bool>>; - -class RemoteCommandRequest { -private: - std::multimap<std::string, std::string> m_options; - std::vector<std::string> m_args; - -public: - inline RemoteCommandRequest(std::multimap<std::string, std::string> options, std::vector<std::string> args) noexcept + inline RemoteCommandRequest(Options options, Args args) noexcept : m_options(std::move(options)) , m_args(std::move(args)) { } - inline const std::vector<std::string> &args() const noexcept + /** + * Get the arguments. + * + * @return the arguments + */ + inline const Args &args() const noexcept { return m_args; } - inline const std::multimap<std::string, std::string> &options() const noexcept + /** + * 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 m_args.size(); + 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; } - const std::string &arg(unsigned index) const noexcept; + /** + * 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()); - std::string argOr(unsigned index, std::string defaultValue) const noexcept; + 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; + } - const std::string &option(const std::string &key) const noexcept; + /** + * 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; + } - std::string optionOr(const std::string &key, std::string defaultValue) const noexcept; + /** + * 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; @@ -217,18 +241,16 @@ } /** - * Return the command usage, without the prefix. (e.g. host port). - * - * Options are prepended automatically + * Return the command documentation usage. * * @return the usage */ std::string usage() const; /** - * Return the help message for irccdctl invocation. + * Return the help message. * - * @return the help + * @return the help message */ virtual std::string help() const = 0; @@ -237,9 +259,9 @@ * * @return the options */ - virtual RemoteCommandOptions options() const + virtual std::vector<Option> options() const { - return RemoteCommandOptions(); + return {}; } /** @@ -247,12 +269,26 @@ * * @return the arguments */ - virtual RemoteCommandArgs args() const + virtual std::vector<Arg> args() const { - return RemoteCommandArgs(); + 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. @@ -293,6 +329,141 @@ 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 // !_REMOTE_COMMAND_H_ +#endif // !IRCCD_COMMAND_H
--- a/lib/irccd/command/help.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/help.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -16,6 +16,9 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <irccd/irccdctl.h> +#include <irccd/logger.h> + #include "help.h" namespace irccd { @@ -27,9 +30,26 @@ { } +std::vector<RemoteCommand::Arg> Help::args() const +{ + return {{ "command", true }}; +} + std::string Help::help() const { - return ""; + 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
--- a/lib/irccd/command/help.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/help.h Fri Mar 25 13:58:12 2016 +0100 @@ -38,10 +38,17 @@ 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
--- a/lib/irccd/command/plugin-info.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/plugin-info.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -37,9 +37,9 @@ return "Get plugin information."; } -RemoteCommandArgs PluginInfo::args() const +std::vector<RemoteCommand::Arg> PluginInfo::args() const { - return { { "plugin", true } }; + return {{ "plugin", true }}; } json::Value PluginInfo::request(Irccdctl &, const RemoteCommandRequest &args) const @@ -71,11 +71,13 @@ RemoteCommand::result(irccdctl, result); /* Plugin information */ - 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; + 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
--- a/lib/irccd/command/plugin-info.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/plugin-info.h Fri Mar 25 13:58:12 2016 +0100 @@ -49,7 +49,7 @@ /** * @copydoc RemoteCommand::args */ - RemoteCommandArgs args() const; + std::vector<Arg> args() const override; /** * @copydoc RemoteCommand::request
--- a/lib/irccd/command/plugin-load.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/plugin-load.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -34,11 +34,9 @@ return "Load a plugin."; } -RemoteCommandArgs PluginLoad::args() const +std::vector<RemoteCommand::Arg> PluginLoad::args() const { - return RemoteCommandArgs{ - { "plugin", true } - }; + return {{ "plugin", true }}; } json::Value PluginLoad::exec(Irccd &irccd, const json::Value &request) const
--- a/lib/irccd/command/plugin-load.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/plugin-load.h Fri Mar 25 13:58:12 2016 +0100 @@ -49,7 +49,7 @@ /** * @copydoc TransportCommand::args */ - RemoteCommandArgs args() const override; + std::vector<Arg> args() const override; /** * @copydoc TransportCommand::exec
--- a/lib/irccd/command/plugin-reload.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/plugin-reload.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -34,11 +34,9 @@ return "Reload a plugin."; } -RemoteCommandArgs PluginReload::args() const +std::vector<RemoteCommand::Arg> PluginReload::args() const { - return RemoteCommandArgs{ - { "plugin", true } - }; + return {{ "plugin", true }}; } json::Value PluginReload::exec(Irccd &irccd, const json::Value &request) const
--- a/lib/irccd/command/plugin-reload.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/plugin-reload.h Fri Mar 25 13:58:12 2016 +0100 @@ -49,7 +49,7 @@ /** * @copydoc TransportCommand::args */ - RemoteCommandArgs args() const override; + std::vector<Arg> args() const override; /** * @copydoc TransportCommand::exec
--- a/lib/irccd/command/plugin-unload.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/plugin-unload.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -34,11 +34,9 @@ return "Unload a plugin."; } -RemoteCommandArgs PluginUnload::args() const +std::vector<RemoteCommand::Arg> PluginUnload::args() const { - return RemoteCommandArgs{ - { "plugin", true } - }; + return {{ "plugin", true }}; } json::Value PluginUnload::exec(Irccd &irccd, const json::Value &request) const
--- a/lib/irccd/command/plugin-unload.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/plugin-unload.h Fri Mar 25 13:58:12 2016 +0100 @@ -49,7 +49,7 @@ /** * @copydoc TransportCommand::args */ - RemoteCommandArgs args() const override; + std::vector<Arg> args() const override; /** * @copydoc RemoteCommand::exec
--- a/lib/irccd/command/server-cmode.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-cmode.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -33,9 +33,9 @@ return ""; } -RemoteCommandArgs ServerChannelMode::args() const +std::vector<RemoteCommand::Arg> ServerChannelMode::args() const { - return RemoteCommandArgs{ + return { { "server", true }, { "channel", true }, { "mode", true }
--- a/lib/irccd/command/server-cmode.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-cmode.h Fri Mar 25 13:58:12 2016 +0100 @@ -49,7 +49,7 @@ /** * @copydoc TransportCommand::args */ - RemoteCommandArgs args() const override; + std::vector<Arg> args() const override; /** * @copydoc TransportCommand::exec
--- a/lib/irccd/command/server-cnotice.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-cnotice.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -34,9 +34,9 @@ return ""; } -RemoteCommandArgs ServerChannelNotice::args() const +std::vector<RemoteCommand::Arg> ServerChannelNotice::args() const { - return RemoteCommandArgs{ + return { { "server", true }, { "channel", true }, { "message", true }
--- a/lib/irccd/command/server-cnotice.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-cnotice.h Fri Mar 25 13:58:12 2016 +0100 @@ -58,7 +58,7 @@ /** * @copydoc TransportCommand::args */ - RemoteCommandArgs args() const override; + std::vector<Arg> args() const override; /** * @copydoc TransportCommand::exec
--- a/lib/irccd/command/server-connect.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-connect.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -122,24 +122,24 @@ std::string ServerConnect::help() const { - return ""; + return "Connect to a server."; } -RemoteCommandOptions ServerConnect::options() const +std::vector<RemoteCommand::Option> ServerConnect::options() const { - return RemoteCommandOptions{ - { "command", "-c", "--command", "command character to use" }, - { "nickname", "-n", "--nickname", "nickname to use" }, - { "realname", "-r", "--realname", "realname to use" }, - { "sslverify", "-S", "--ssl-verify", "verify SSL" }, - { "ssl", "-s", "--ssl", "connect with SSL" }, - { "username", "-u", "--username", "username to use" } + 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" }, }; } -RemoteCommandArgs ServerConnect::args() const +std::vector<RemoteCommand::Arg> ServerConnect::args() const { - return RemoteCommandArgs{ + return { { "id", true }, { "host", true }, { "port", false }
--- a/lib/irccd/command/server-connect.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-connect.h Fri Mar 25 13:58:12 2016 +0100 @@ -49,12 +49,12 @@ /** * @copydoc TransportCommand::options */ - RemoteCommandOptions options() const override; + std::vector<Option> options() const override; /** * @copydoc TransportCommand::args */ - RemoteCommandArgs args() const override; + std::vector<Arg> args() const override; /** * @copydoc TransportCommand::exec
--- a/lib/irccd/command/server-disconnect.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-disconnect.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -34,11 +34,9 @@ return ""; } -RemoteCommandArgs ServerDisconnect::args() const +std::vector<RemoteCommand::Arg> ServerDisconnect::args() const { - return RemoteCommandArgs{ - { "server", false } - }; + return {{ "server", false }}; } json::Value ServerDisconnect::exec(Irccd &irccd, const json::Value &request) const
--- a/lib/irccd/command/server-disconnect.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-disconnect.h Fri Mar 25 13:58:12 2016 +0100 @@ -46,7 +46,7 @@ */ std::string help() const override; - RemoteCommandArgs args() const override; + std::vector<Arg> args() const override; /** * @copydoc TransportCommand::exec
--- a/lib/irccd/command/server-info.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-info.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -36,11 +36,9 @@ return ""; } -RemoteCommandArgs ServerInfo::args() const +std::vector<RemoteCommand::Arg> ServerInfo::args() const { - return RemoteCommandArgs{ - { "server", true }, - }; + return {{ "server", true }}; } json::Value ServerInfo::request(Irccdctl &, const RemoteCommandRequest &args) const
--- a/lib/irccd/command/server-info.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-info.h Fri Mar 25 13:58:12 2016 +0100 @@ -46,7 +46,7 @@ /** * @copydoc TransportCommand::args */ - RemoteCommandArgs args() const override; + std::vector<Arg> args() const override; json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
--- a/lib/irccd/command/server-invite.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-invite.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -34,9 +34,9 @@ return ""; } -RemoteCommandArgs ServerInvite::args() const +std::vector<RemoteCommand::Arg> ServerInvite::args() const { - return RemoteCommandArgs{ + return { { "server", true }, { "nickname", true }, { "channel", true }
--- a/lib/irccd/command/server-invite.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-invite.h Fri Mar 25 13:58:12 2016 +0100 @@ -46,7 +46,7 @@ /** * @copydoc RemoteCommand::args */ - RemoteCommandArgs args() const override; + std::vector<Arg> args() const override; /** * @copydoc RemoteCommand::request
--- a/lib/irccd/command/server-join.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-join.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -34,9 +34,9 @@ return ""; } -RemoteCommandArgs ServerJoin::args() const +std::vector<RemoteCommand::Arg> ServerJoin::args() const { - return RemoteCommandArgs{ + return { { "server", true }, { "channel", true }, { "password", false }
--- a/lib/irccd/command/server-join.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-join.h Fri Mar 25 13:58:12 2016 +0100 @@ -46,7 +46,7 @@ /** * @copydoc RemoteCommand::args */ - RemoteCommandArgs args() const override; + std::vector<Arg> args() const override; /** * @copydoc RemoteCommand::request
--- a/lib/irccd/command/server-kick.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-kick.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -34,9 +34,9 @@ return ""; } -RemoteCommandArgs ServerKick::args() const +std::vector<RemoteCommand::Arg> ServerKick::args() const { - return RemoteCommandArgs{ + return { { "server", true }, { "target", true }, { "channel", true },
--- a/lib/irccd/command/server-kick.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-kick.h Fri Mar 25 13:58:12 2016 +0100 @@ -46,7 +46,7 @@ /** * @copydoc RemoteCommand::args */ - RemoteCommandArgs args() const override; + std::vector<Arg> args() const override; /** * @copydoc RemoteCommand::request
--- a/lib/irccd/command/server-me.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-me.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -34,9 +34,9 @@ return ""; } -RemoteCommandArgs ServerMe::args() const +std::vector<RemoteCommand::Arg> ServerMe::args() const { - return RemoteCommandArgs{ + return { { "server", true }, { "target", true }, { "message", true }
--- a/lib/irccd/command/server-me.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-me.h Fri Mar 25 13:58:12 2016 +0100 @@ -46,7 +46,7 @@ /** * @copydoc RemoteCommand::args */ - RemoteCommandArgs args() const override; + std::vector<Arg> args() const override; /** * @copydoc RemoteCommand::request
--- a/lib/irccd/command/server-message.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-message.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -34,9 +34,9 @@ return ""; } -RemoteCommandArgs ServerMessage::args() const +std::vector<RemoteCommand::Arg> ServerMessage::args() const { - return RemoteCommandArgs{ + return { { "server", true }, { "target", true }, { "message", true }
--- a/lib/irccd/command/server-message.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-message.h Fri Mar 25 13:58:12 2016 +0100 @@ -46,7 +46,7 @@ /** * @copydoc RemoteCommand::args */ - RemoteCommandArgs args() const override; + std::vector<Arg> args() const override; /** * @copydoc RemoteCommand::request
--- a/lib/irccd/command/server-mode.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-mode.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -34,9 +34,9 @@ return ""; } -RemoteCommandArgs ServerMode::args() const +std::vector<RemoteCommand::Arg> ServerMode::args() const { - return RemoteCommandArgs{ + return { { "server", true }, { "mode", true } };
--- a/lib/irccd/command/server-mode.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-mode.h Fri Mar 25 13:58:12 2016 +0100 @@ -46,7 +46,7 @@ /** * @copydoc RemoteCommand::args */ - RemoteCommandArgs args() const override; + std::vector<Arg> args() const override; /** * @copydoc RemoteCommand::request
--- a/lib/irccd/command/server-nick.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-nick.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -34,9 +34,9 @@ return ""; } -RemoteCommandArgs ServerNick::args() const +std::vector<RemoteCommand::Arg> ServerNick::args() const { - return RemoteCommandArgs{ + return { { "server", true }, { "nickname", true } };
--- a/lib/irccd/command/server-nick.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-nick.h Fri Mar 25 13:58:12 2016 +0100 @@ -46,7 +46,7 @@ /** * @copydoc RemoteCommand::args */ - RemoteCommandArgs args() const override; + std::vector<Arg> args() const override; /** * @copydoc RemoteCommand::request
--- a/lib/irccd/command/server-notice.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-notice.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -34,9 +34,9 @@ return ""; } -RemoteCommandArgs ServerNotice::args() const +std::vector<RemoteCommand::Arg> ServerNotice::args() const { - return RemoteCommandArgs{ + return { { "server", true }, { "target", true }, { "message", true }
--- a/lib/irccd/command/server-notice.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-notice.h Fri Mar 25 13:58:12 2016 +0100 @@ -46,7 +46,7 @@ /** * @copydoc RemoteCommand::args */ - RemoteCommandArgs args() const override; + std::vector<Arg> args() const override; /** * @copydoc RemoteCommand::request
--- a/lib/irccd/command/server-part.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-part.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -34,9 +34,9 @@ return ""; } -RemoteCommandArgs ServerPart::args() const +std::vector<RemoteCommand::Arg> ServerPart::args() const { - return RemoteCommandArgs{ + return { { "server", true }, { "channel", true }, { "reason", false }
--- a/lib/irccd/command/server-part.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-part.h Fri Mar 25 13:58:12 2016 +0100 @@ -46,7 +46,7 @@ /** * @copydoc RemoteCommand::args */ - RemoteCommandArgs args() const override; + std::vector<Arg> args() const override; /** * @copydoc RemoteCommand::request
--- a/lib/irccd/command/server-reconnect.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-reconnect.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -34,11 +34,9 @@ return ""; } -RemoteCommandArgs ServerReconnect::args() const +std::vector<RemoteCommand::Arg> ServerReconnect::args() const { - return RemoteCommandArgs{ - { "server", false } - }; + return {{ "server", false }}; } json::Value ServerReconnect::request(Irccdctl &, const RemoteCommandRequest &args) const
--- a/lib/irccd/command/server-reconnect.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-reconnect.h Fri Mar 25 13:58:12 2016 +0100 @@ -46,7 +46,7 @@ /** * @copydoc RemoteCommand::args */ - RemoteCommandArgs args() const override; + std::vector<Arg> args() const override; /** * @copydoc RemoteCommand::request
--- a/lib/irccd/command/server-topic.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-topic.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -34,9 +34,9 @@ return ""; } -RemoteCommandArgs ServerTopic::args() const +std::vector<RemoteCommand::Arg> ServerTopic::args() const { - return RemoteCommandArgs{ + return { { "server", true }, { "channel", true }, { "topic", true }
--- a/lib/irccd/command/server-topic.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/server-topic.h Fri Mar 25 13:58:12 2016 +0100 @@ -46,7 +46,7 @@ /** * @copydoc RemoteCommand::args */ - RemoteCommandArgs args() const override; + std::vector<Arg> args() const override; /** * @copydoc RemoteCommand::request
--- a/lib/irccd/command/watch.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/watch.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -18,8 +18,11 @@ #include <functional> #include <iostream> +#include <sstream> #include <unordered_map> +#include <irccd/irccdctl.h> + #include "watch.h" namespace irccd { @@ -185,30 +188,30 @@ } // !namespace -#if 0 - -void Watch::usage(Irccdctl &) const +Watch::Watch() + : RemoteCommand("watch", "General") { - log::warning() << "usage: " << sys::programName() << " watch [-f|--format native|json]\n\n"; - log::warning() << "Start watching irccd events. You can use different output formats, native\n"; - log::warning() << "is human readable format, json is pretty formatted json.\n\n"; - log::warning() << "Example:\n"; - log::warning() << "\t " << sys::programName() << " watch -f json" << std::endl; +} + +std::vector<RemoteCommand::Option> Watch::options() const +{ + return {{ "format", "f", "format", "format", "output format" }}; } -void Watch::exec(Irccdctl &ctl, const std::vector<std::string> &args) const +std::string Watch::help() const { - std::vector<std::string> copy(args); - std::string format("native"); + std::ostringstream oss; - parser::Options options{ - { "-f", true }, - { "--format", true } - }; + 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."; - for (const auto &o : parser::read(copy, options)) - if (o.first == "-f" || o.first == "--format") - format = o.second; + 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); @@ -233,10 +236,10 @@ } throw std::runtime_error("connection lost"); + + return nullptr; } -#endif - } // !command } // !irccd
--- a/lib/irccd/command/watch.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/command/watch.h Fri Mar 25 13:58:12 2016 +0100 @@ -38,10 +38,17 @@ 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
--- a/lib/irccd/irccd.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/irccd.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -571,7 +571,7 @@ } /* 3. Try to execute it */ - json::Value response; + json::Value response = json::object({}); try { response = it->second->exec(*this, object); @@ -580,14 +580,16 @@ if (!response.isObject()) response = json::object({}); - response.insert("command", it->first); - response.insert("status", "true"); + response.insert("status", true); } catch (const std::exception &ex) { - response.insert("status", "false"); + response.insert("status", false); response.insert("error", ex.what()); } - /* 4. Send the result */ + /* 4. Store the command name result */ + response.insert("response", it->first); + + /* 5. Send the result */ tc->send(response.toJson(0)); }); }
--- a/lib/irccd/irccdctl.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/irccdctl.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -48,6 +48,7 @@ using namespace std::placeholders; using namespace std::chrono_literals; +using namespace std::string_literals; /* * Config file format @@ -75,6 +76,7 @@ void Irccdctl::usage() const { + // TODO: CHANGE log::warning() << "usage: " << sys::programName() << " [options...] <command> [command-options...] [command-args...]\n\n"; log::warning() << "General options:\n"; log::warning() << "\tc, --config file\tspecify the configuration file\n"; @@ -354,7 +356,48 @@ void Irccdctl::exec(const RemoteCommand &cmd, std::vector<std::string> args) { - + /* 1. Build options from command line arguments. */ + parser::Options def; + + for (const auto &opt : cmd.options()) { + /* parser::read needs '-' and '--' so add them */ + if (!opt.simpleKey().empty()) + def.emplace("-"s + opt.simpleKey(), !opt.arg().empty()); + if (!opt.longKey().empty()) + def.emplace("--"s + opt.longKey(), !opt.arg().empty()); + } + + /* 2. Parse them, remove them from args (in parser::read) and build the map with id. */ + RemoteCommandRequest::Options requestOptions; + + for (const auto &pair : parser::read(args, def)) { + auto options = cmd.options(); + auto it = std::find_if(options.begin(), options.end(), [&] (const auto &opt) { + return ("-"s + opt.simpleKey()) == pair.first || ("--"s + opt.longKey()) == pair.first; + }); + + requestOptions.emplace(it->id(), pair.second); + } + + /* 3. Check number of arguments. */ + if (args.size() < cmd.min()) + throw std::runtime_error("too few arguments"); + if (args.size() > cmd.max()) + throw std::runtime_error("too many arguments"); + + /* 4. Construct the request, if the returned value is not an object, do not send anything (e.g. help). */ + json::Value request = cmd.request(*this, RemoteCommandRequest(std::move(requestOptions), std::move(args))); + + if (!request.isObject()) + return; + + request.insert("command", cmd.name()); + + /* 5. Send the command */ + m_connection->send(request.toJson(0), 30000); + + /* 6. Parse the result */ + cmd.result(*this, m_connection->next(cmd.name(), 30000)); } void Irccdctl::exec(const Alias &alias, std::vector<std::string> argsCopy)
--- a/lib/irccd/path.cpp Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/path.cpp Fri Mar 25 13:58:12 2016 +0100 @@ -53,7 +53,7 @@ # include <libproc.h> # endif -# include <xdg.h> +# include "private/xdg.h" #endif #include "private/filesystem.h"
--- a/lib/irccd/private/connection.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/private/connection.h Fri Mar 25 13:58:12 2016 +0100 @@ -49,6 +49,16 @@ public: /** + * Default constructor. + */ + Connection() = default; + + /** + * Default destructor. + */ + virtual ~Connection() = default; + + /** * Wait for the next requested response. * * @param name the response name
--- a/lib/irccd/private/sockets.h Thu Mar 24 14:07:30 2016 +0100 +++ b/lib/irccd/private/sockets.h Fri Mar 25 13:58:12 2016 +0100 @@ -2506,7 +2506,7 @@ template <typename Address> inline void create(Socket<Address, Tls> &sc) { - auto method = (m_method == ssl::Tlsv1) ? TLSv1_method() : SSLv3_method(); + 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};