#include "settings.hpp" #include "web.hpp" #include "myirc/bot.hpp" #include "myirc/challenge.hpp" #include "myirc/client.hpp" #include "myirc/connection.hpp" #include "myirc/openssl_utils.hpp" #include "myirc/registration.hpp" #include "myirc/sasl_mechanism.hpp" #include "myirc/ref.hpp" #include "myirc/irc_coroutine.hpp" #include #include #include #include #include #include using namespace std::literals; using myirc::SaslMechanism; using myirc::SaslPlain; using myirc::SaslExternal; using myirc::SaslEcdsa; using myirc::Bot; using myirc::Client; using myirc::Connection; using myirc::Registration; using myirc::Challenge; using myirc::Ref; auto configure_sasl(const Settings &settings) -> std::unique_ptr { if (settings.sasl_mechanism == "PLAIN" && not settings.sasl_authcid.empty() ) { return std::make_unique( settings.sasl_authcid, settings.sasl_authzid, settings.sasl_password); } else if (settings.sasl_mechanism == "EXTERNAL") { return std::make_unique(settings.sasl_authzid); } else if ( settings.sasl_mechanism == "ECDSA" && not settings.sasl_authcid.empty() && not settings.sasl_key_file.empty() ) { if (auto sasl_key = myirc::key_from_file(settings.sasl_key_file, settings.sasl_key_password)) return std::make_unique( settings.sasl_authcid, settings.sasl_authzid, std::move(sasl_key)); } return nullptr; } static auto start( boost::asio::io_context &io, const Settings &settings, std::shared_ptr webhook ) -> void { Ref tls_cert; if (settings.use_tls && not settings.tls_cert_file.empty()) { tls_cert = myirc::cert_from_file(settings.tls_cert_file); } Ref tls_key; if (settings.use_tls && not settings.tls_key_file.empty()) { tls_key = myirc::key_from_file(settings.tls_key_file, settings.tls_key_password); } const auto connection = std::make_shared(io); const auto client = Client::start(connection); const auto bot = Bot::start(client); Registration::start({ .nickname = settings.nickname, .realname = settings.realname, .username = settings.username, .password = settings.password, .sasl_mechanism = configure_sasl(settings), }, client); // Configure CHALLENGE on registration if applicable if (not settings.challenge_username.empty() && not settings.challenge_key_file.empty()) { if (auto key = myirc::key_from_file(settings.challenge_key_file, settings.challenge_key_password)) { client->sig_registered.connect([&settings, connection, key = std::move(key)]() { Challenge::start(connection, settings.challenge_username, key); }); } } client->sig_registered.connect([connection, webhook]() { webhook->set_connection(connection); }); // On disconnect reconnect in 5 seconds // connection is captured in the disconnect handler so it can keep itself alive connection->sig_disconnect.connect( [&io, &settings, connection, webhook]() { webhook->clear_connection(); auto timer = std::make_shared(io); timer->expires_after(5s); timer->async_wait([&io, &settings, timer, webhook](auto) { start(io, settings, webhook); }); } ); // Dispatch commands to the webhook logic bot->sig_command.connect([webhook, connection](const Bot::Command &cmd) { auto cursor = webhook_commands.find(std::string{cmd.command}); if (cursor != webhook_commands.end()) { cursor->second(webhook, cmd); } }); connection->start({ .tls = settings.use_tls, .host = settings.host, .port = settings.service, .verify = settings.tls_hostname, .client_cert = std::move(tls_cert), .client_key = std::move(tls_key), }); } static auto get_settings(const char * const filename) -> Settings { if (auto config_stream = std::ifstream{filename}) { return Settings::from_stream(config_stream); } else { BOOST_LOG_TRIVIAL(error) << "Unable to open configuration"; std::exit(1); } } auto main(int argc, char *argv[]) -> int { //boost::log::core::get()->set_filter(boost::log::trivial::severity >= boost::log::trivial::warning); if (argc != 3) { BOOST_LOG_TRIVIAL(error) << "Bad arguments"; return 1; } const auto settings = get_settings(argv[1]); auto io = boost::asio::io_context{}; auto webhooks = start_webhook(io, argv[2]); start(io, settings, webhooks); io.run(); }