diff --git a/bot.cpp b/bot.cpp index 157fdf4..ba65858 100644 --- a/bot.cpp +++ b/bot.cpp @@ -6,7 +6,7 @@ auto Bot::start(std::shared_ptr self) -> std::shared_ptr { const auto thread = std::make_shared(std::move(self)); - thread->self_->get_connection()->sig_ircmsg.connect([thread](const auto cmd, auto &msg) { + thread->self_->get_connection().sig_ircmsg.connect([thread](const auto cmd, auto &msg) { thread->on_ircmsg(cmd, msg); }); diff --git a/client.hpp b/client.hpp index d44de65..b5a5b23 100644 --- a/client.hpp +++ b/client.hpp @@ -56,6 +56,8 @@ public: { } + auto get_connection() -> Connection & { return connection_; } + static auto start(Connection &) -> std::shared_ptr; auto start_sasl(std::unique_ptr mechanism) -> void; diff --git a/connection.cpp b/connection.cpp index b5ad1e7..aced5c4 100644 --- a/connection.cpp +++ b/connection.cpp @@ -147,11 +147,6 @@ auto Connection::close() -> void stream_.close(); } -static auto is_invalid_last(char x) -> bool -{ - return x == '\0' || x == '\r' || x == '\n'; -} - auto Connection::write_irc(std::string message) -> void { write_line(std::move(message)); @@ -159,7 +154,7 @@ auto Connection::write_irc(std::string message) -> void auto Connection::write_irc(std::string front, std::string_view last) -> void { - if (last.end() != std::find_if(last.begin(), last.end(), is_invalid_last)) + if (last.find_first_of("\r\n\0"sv) != last.npos) { throw std::runtime_error{"bad irc argument"}; } @@ -485,4 +480,4 @@ auto Connection::start(ConnectSettings settings) -> void self->sig_snote.disconnect_all_slots(); self->sig_authenticate.disconnect_all_slots(); }); -} \ No newline at end of file +} diff --git a/connection.hpp b/connection.hpp index a74f8e6..2fa69d0 100644 --- a/connection.hpp +++ b/connection.hpp @@ -2,7 +2,6 @@ #include "irc_command.hpp" #include "ircmsg.hpp" -#include "settings.hpp" #include "snote.hpp" #include "stream.hpp" @@ -39,6 +38,7 @@ struct ConnectSettings std::string socks_user; std::string socks_pass; }; + class Connection : public std::enable_shared_from_this { private: @@ -62,36 +62,34 @@ private: auto watchdog_activity() -> void; auto connect(ConnectSettings settings) -> boost::asio::awaitable; + auto on_authenticate(std::string_view) -> void; -public: /// Build and send well-formed IRC message from individual parameters auto write_irc(std::string) -> void; auto write_irc(std::string, std::string_view) -> void; template auto write_irc(std::string front, std::string_view next, Args... rest) -> void; - /// Write bytes into the socket. - auto write_line(std::string message) -> void; - - Connection(boost::asio::io_context &io); - +public: boost::signals2::signal sig_connect; boost::signals2::signal sig_disconnect; boost::signals2::signal sig_ircmsg; boost::signals2::signal sig_snote; boost::signals2::signal sig_authenticate; + Connection(boost::asio::io_context &io); + + /// Write bytes into the socket. + auto write_line(std::string message) -> void; + auto get_executor() -> boost::asio::any_io_executor { return stream_.get_executor(); } auto start(ConnectSettings) -> void; - auto close() -> void; - auto on_authenticate(std::string_view) -> void; - auto send_ping(std::string_view) -> void; auto send_pong(std::string_view) -> void; auto send_pass(std::string_view) -> void; @@ -111,13 +109,9 @@ public: template auto Connection::write_irc(std::string front, std::string_view next, Args... rest) -> void { - const auto is_invalid = [](const char x) -> bool { - return x == '\0' || x == '\r' || x == '\n' || x == ' '; - }; + using namespace std::literals; - if (next.empty() - || next.front() == ':' - || next.end() != std::find_if(next.begin(), next.end(), is_invalid)) + if (next.empty() || next.front() == ':' || next.find_first_of("\r\n \0"sv) != next.npos) { throw std::runtime_error{"bad irc argument"}; } diff --git a/main.cpp b/main.cpp index 885d7f5..ad7cc89 100644 --- a/main.cpp +++ b/main.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include "bot.hpp" #include "client.hpp" @@ -19,7 +18,7 @@ auto start(boost::asio::io_context &io, const Settings &settings) -> void { const auto connection = std::make_shared(io); const auto client = Client::start(*connection); - Registration::start(*connection, settings, client); + Registration::start(settings, client); const auto bot = Bot::start(client); diff --git a/registration.cpp b/registration.cpp index cc2707d..0ffb4da 100644 --- a/registration.cpp +++ b/registration.cpp @@ -5,31 +5,35 @@ #include "sasl_mechanism.hpp" #include +#include #include #include Registration::Registration( - Connection &connection, const Settings &settings, - std::shared_ptr self + std::shared_ptr client ) - : connection_{connection} - , settings_{settings} - , self_{std::move(self)} + : settings_{settings} + , client_{std::move(client)} { } auto Registration::on_connect() -> void { - connection_.send_cap_ls(); - listen_for_cap_ls(); + client_->get_connection().send_cap_ls(); + slot_ = client_->get_connection().sig_ircmsg.connect( + [self = shared_from_this()](const auto cmd, auto &msg) + { + self->on_ircmsg(cmd, msg); + } + ); if (not settings_.password.empty()) { - connection_.send_pass(settings_.password); + client_->get_connection().send_pass(settings_.password); } - connection_.send_user(settings_.username, settings_.realname); - connection_.send_nick(settings_.nickname); + client_->get_connection().send_user(settings_.username, settings_.realname); + client_->get_connection().send_nick(settings_.nickname); } auto Registration::send_req() -> void @@ -69,13 +73,11 @@ auto Registration::send_req() -> void if (not outstanding.empty()) { request.pop_back(); - connection_.send_cap_req(request); - - listen_for_cap_ack(); + client_->get_connection().send_cap_req(request); } else { - connection_.send_cap_end(); + client_->get_connection().send_cap_end(); } } @@ -94,23 +96,11 @@ auto Registration::on_msg_cap_ack(const IrcMsg &msg) -> void if (settings_.sasl_mechanism.empty()) { - slot_.disconnect(); - connection_.send_cap_end(); + client_->get_connection().send_cap_end(); } else { - self_->start_sasl(std::make_unique(settings_.sasl_authcid, settings_.sasl_authzid, settings_.sasl_password)); - slot_ = connection_.sig_ircmsg.connect([thread = shared_from_this()](auto cmd, auto &msg) { - switch (cmd) - { - default: - break; - case IrcCommand::RPL_SASLSUCCESS: - case IrcCommand::ERR_SASLFAIL: - thread->connection_.send_cap_end(); - thread->slot_.disconnect(); - } - }); + client_->start_sasl(std::make_unique(settings_.sasl_authcid, settings_.sasl_authzid, settings_.sasl_password)); } } } @@ -155,20 +145,18 @@ auto Registration::on_msg_cap_ls(const IrcMsg &msg) -> void if (last) { - slot_.disconnect(); send_req(); } } auto Registration::start( - Connection &connection, const Settings &settings, - std::shared_ptr self + std::shared_ptr client ) -> std::shared_ptr { - const auto thread = std::make_shared(connection, std::move(settings), std::move(self)); + const auto thread = std::make_shared(std::move(settings), std::move(client)); - thread->slot_ = connection.sig_connect.connect([thread]() { + thread->slot_ = thread->client_->get_connection().sig_connect.connect([thread]() { thread->slot_.disconnect(); thread->on_connect(); }); @@ -176,27 +164,48 @@ auto Registration::start( return thread; } -auto Registration::listen_for_cap_ack() -> void +auto Registration::randomize_nick() -> void { - slot_ = connection_.sig_ircmsg.connect([thread = shared_from_this()](IrcCommand cmd, const IrcMsg &msg) { - if (IrcCommand::CAP == cmd && msg.args.size() >= 2 && "ACK" == msg.args[1]) - { - thread->on_msg_cap_ack(msg); - } - }); + std::string new_nick; + new_nick += settings_.nickname.substr(0, 8); + + std::random_device rd; + std::mt19937 gen{rd()}; + std::uniform_int_distribution<> distrib(0, 35); + + for (int i = 0; i < 8; ++i) { + const auto x = distrib(gen); + new_nick += x < 10 ? '0' + x : 'A' + (x-10); + } + + client_->get_connection().send_nick(new_nick); } -auto Registration::listen_for_cap_ls() -> void +auto Registration::on_ircmsg(const IrcCommand cmd, const IrcMsg &msg) -> void { - slot_ = connection_.sig_ircmsg.connect([thread = shared_from_this()](IrcCommand cmd, const IrcMsg &msg) { - if (IrcCommand::CAP == cmd && msg.args.size() >= 2 && "LS" == msg.args[1]) - { - thread->on_msg_cap_ls(msg); + switch (cmd) + { + default: break; + + case IrcCommand::CAP: + if (msg.args.size() >= 2 && "LS" == msg.args[1]) { + on_msg_cap_ls(msg); + } else if (msg.args.size() >= 2 && "ACK" == msg.args[1]) { + on_msg_cap_ack(msg); } - else if (IrcCommand::RPL_WELCOME == cmd) - { - // Server doesn't support CAP negotiation - thread->slot_.disconnect(); - } - }); + break; + + case IrcCommand::ERR_NICKNAMEINUSE: + randomize_nick(); + break; + + case IrcCommand::RPL_WELCOME: + slot_.disconnect(); + break; + + case IrcCommand::RPL_SASLSUCCESS: + case IrcCommand::ERR_SASLFAIL: + client_->get_connection().send_cap_end(); + break; + } } diff --git a/registration.hpp b/registration.hpp index 1b3c3b8..8318bdf 100644 --- a/registration.hpp +++ b/registration.hpp @@ -11,9 +11,8 @@ class Registration : public std::enable_shared_from_this { - Connection &connection_; const Settings &settings_; - std::shared_ptr self_; + std::shared_ptr client_; std::unordered_map caps; std::unordered_set outstanding; @@ -21,22 +20,21 @@ class Registration : public std::enable_shared_from_this boost::signals2::scoped_connection slot_; auto on_connect() -> void; - auto send_req() -> void; auto on_msg_cap_ls(const IrcMsg &msg) -> void; auto on_msg_cap_ack(const IrcMsg &msg) -> void; - auto listen_for_cap_ack() -> void; - auto listen_for_cap_ls() -> void; + auto on_ircmsg(IrcCommand, const IrcMsg &msg) -> void; + + auto send_req() -> void; + auto randomize_nick() -> void; public: Registration( - Connection &, const Settings &, std::shared_ptr ); static auto start( - Connection &, const Settings &, std::shared_ptr ) -> std::shared_ptr;