changeset 73:8ee1178f1219

Irccd: port as a library almost complete, #429
author David Demelier <markand@malikania.fr>
date Fri, 25 Mar 2016 13:58:12 +0100
parents 98ac3c79009f
children 35ef15100de8
files CMakeLists.txt irccdctl/CMakeLists.txt lib/irccd/application.cpp lib/irccd/command/command.cpp lib/irccd/command/command.h lib/irccd/command/help.cpp lib/irccd/command/help.h lib/irccd/command/plugin-info.cpp lib/irccd/command/plugin-info.h lib/irccd/command/plugin-load.cpp lib/irccd/command/plugin-load.h lib/irccd/command/plugin-reload.cpp lib/irccd/command/plugin-reload.h lib/irccd/command/plugin-unload.cpp lib/irccd/command/plugin-unload.h lib/irccd/command/server-cmode.cpp lib/irccd/command/server-cmode.h lib/irccd/command/server-cnotice.cpp lib/irccd/command/server-cnotice.h lib/irccd/command/server-connect.cpp lib/irccd/command/server-connect.h lib/irccd/command/server-disconnect.cpp lib/irccd/command/server-disconnect.h lib/irccd/command/server-info.cpp lib/irccd/command/server-info.h lib/irccd/command/server-invite.cpp lib/irccd/command/server-invite.h lib/irccd/command/server-join.cpp lib/irccd/command/server-join.h lib/irccd/command/server-kick.cpp lib/irccd/command/server-kick.h lib/irccd/command/server-me.cpp lib/irccd/command/server-me.h lib/irccd/command/server-message.cpp lib/irccd/command/server-message.h lib/irccd/command/server-mode.cpp lib/irccd/command/server-mode.h lib/irccd/command/server-nick.cpp lib/irccd/command/server-nick.h lib/irccd/command/server-notice.cpp lib/irccd/command/server-notice.h lib/irccd/command/server-part.cpp lib/irccd/command/server-part.h lib/irccd/command/server-reconnect.cpp lib/irccd/command/server-reconnect.h lib/irccd/command/server-topic.cpp lib/irccd/command/server-topic.h lib/irccd/command/watch.cpp lib/irccd/command/watch.h lib/irccd/irccd.cpp lib/irccd/irccdctl.cpp lib/irccd/path.cpp lib/irccd/private/connection.h lib/irccd/private/sockets.h
diffstat 54 files changed, 566 insertions(+), 290 deletions(-) [+]
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};