view libirccd-ctl/irccd/ctl/controller.cpp @ 780:560b62f6b0a7

Core: rework socket layer, closes #939 @6h Rework the ip_connector and ip_acceptor to be more easy to use. Also, ip_connector will now use a resolver to find appropriate endpoints. Bring back full support for IPv6/IPv4 with all possible combinations. The tls_stream class now owns a shared ssl::context that is copied from the acceptor or the connector. The tls_connector and tls_acceptor wraps basic ones for convenience and simplicity. Irccd and irccdctl now support local SSL sockets.
author David Demelier <markand@malikania.fr>
date Sun, 04 Nov 2018 17:26:05 +0100
parents dc9af75c609c
children f26bb089232d
line wrap: on
line source

/*
 * controller.cpp -- main irccdctl interface
 *
 * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <cassert>

#include <irccd/sysconfig.hpp>
#include <irccd/json_util.hpp>

#include <irccd/daemon/irccd.hpp>
#include <irccd/daemon/server.hpp>
#include <irccd/daemon/plugin.hpp>
#include <irccd/daemon/rule.hpp>

#include "controller.hpp"

using irccd::json_util::deserializer;

namespace irccd::ctl {

void controller::authenticate(connect_handler handler, nlohmann::json info)
{
	const auto cmd = nlohmann::json::object({
		{ "command",    "auth"          },
		{ "password",   password_       }
	});

	write(cmd, [handler, info, this] (auto code) {
		if (code) {
			handler(std::move(code), nullptr);
			return;
		}

		read([handler, info] (auto code, auto) {
			handler(std::move(code), std::move(info));
		});
	});
}

void controller::verify(connect_handler handler)
{
	read([handler, this] (auto code, auto message) {
		if (code) {
			handler(std::move(code), std::move(message));
			return;
		}

		const deserializer doc(message);
		const auto program = doc.get<std::string>("program");
		const auto major = doc.get<int>("major");

		if (!program && *program != "irccd")
			handler(irccd_error::not_irccd, std::move(message));
		else if (major && *major != IRCCD_VERSION_MAJOR)
			handler(irccd_error::incompatible_version, std::move(message));
		else {
			if (!password_.empty())
				authenticate(std::move(handler), message);
			else
				handler(code, std::move(message));
		}
	});
}

controller::controller(std::unique_ptr<connector> connector) noexcept
	: connector_(std::move(connector))
{
	assert(connector_);
}

auto controller::get_password() const noexcept -> const std::string&
{
	return password_;
}

void controller::set_password(std::string password) noexcept
{
	password_ = std::move(password);
}

void controller::connect(connect_handler handler)
{
	assert(handler);

	connector_->connect([handler, this] (auto code, auto stream) {
		if (code)
			handler(std::move(code), nullptr);
		else {
			stream_ = std::move(stream);
			verify(std::move(handler));
		}
	});
}

void controller::read(stream::recv_handler handler)
{
	assert(handler);
	assert(stream_);

	auto stream = stream_;

	stream_->recv([this, handler, stream] (auto code, auto msg) {
		if (code) {
			stream_ = nullptr;
			handler(std::move(code), std::move(msg));
			return;
		}

		const deserializer doc(msg);
		const auto e = doc.get<int>("error");
		const auto c = doc.get<std::string>("errorCategory");

		if (e && c) {
			if (*c == "irccd")
				code = make_error_code(static_cast<irccd_error::error>(*e));
			else if (*c == "server")
				code = make_error_code(static_cast<server_error::error>(*e));
			else if (*c == "plugin")
				code = make_error_code(static_cast<plugin_error::error>(*e));
			else if (*c == "rule")
				code = make_error_code(static_cast<rule_error::error>(*e));
		}

		handler(std::move(code), std::move(msg));
	});
}

void controller::write(nlohmann::json message, stream::send_handler handler)
{
	assert(message.is_object());
	assert(stream_);

	auto stream = stream_;

	stream_->send(std::move(message), [this, stream, handler] (auto code) {
		if (code)
			stream_ = nullptr;
		if (handler)
			handler(std::move(code));
	});
}

} // !irccd::ctl