#include "registration_thread.hpp" RegistrationThread::RegistrationThread( Connection * connection_, std::string password, std::string username, std::string realname, std::string nickname ) : connection_{connection_} , password_{password} , username_{username} , realname_{realname} , nickname_{nickname} , stage_{Stage::LsReply} {} auto RegistrationThread::priority() const -> priority_type { return 2; } auto RegistrationThread::on_connect() -> Thread::callback_result { write_irc(*connection_, "CAP", "LS", "302"); write_irc(*connection_, "PASS", password_); write_irc(*connection_, "USER", username_, "*", "*", realname_); write_irc(*connection_, "NICK", nickname_); return {}; } auto RegistrationThread::send_req() -> Thread::callback_result { std::string request; char const* want[] = { "extended-join", "account-notify", "draft/chathistory", "batch", "soju.im/no-implicit-names", "chghost", "setname", "account-tag", "solanum.chat/oper", "solanum.chat/identify-msg", "solanum.chat/realhost", "server-time", "invite-notify", "extended-join" }; for (auto cap : want) { if (caps.contains(cap)) { request.append(cap); request.push_back(' '); outstanding.insert(cap); } } if (not outstanding.empty()) { request.pop_back(); write_irc(*connection_, "CAP", "REQ", request); stage_ = Stage::AckReply; return {ThreadOutcome::Continue, EventOutcome::Consume}; } else { write_irc(*connection_, "CAP", "END"); return {ThreadOutcome::Finish, EventOutcome::Consume}; } } auto RegistrationThread::capack(IrcMsg const& msg) -> Thread::callback_result { auto const n = msg.args.size(); if ("CAP" == msg.command && n >= 2 && "*" == msg.args[0] && "ACK" == msg.args[1]) { auto in = std::istringstream{std::string{msg.args[2]}}; std::for_each( std::istream_iterator{in}, std::istream_iterator{}, [this](std::string x) { outstanding.erase(x); } ); if (outstanding.empty()) { write_irc(*connection_, "CAP", "END"); return {ThreadOutcome::Finish, EventOutcome::Consume}; } else { return {ThreadOutcome::Continue, EventOutcome::Consume}; } } else { return {}; } } auto RegistrationThread::capls(IrcMsg const& msg) -> Thread::callback_result { auto const n = msg.args.size(); if ("CAP" == msg.command && n >= 2 && "*" == msg.args[0] && "LS" == msg.args[1]) { std::string_view const* kvs; bool last; if (3 == n) { kvs = &msg.args[2]; last = true; } else if (4 == n && "*" == msg.args[2]) { kvs = &msg.args[3]; last = false; } else { return {}; } auto in = std::istringstream{std::string{*kvs}}; std::for_each( std::istream_iterator{in}, std::istream_iterator{}, [this](std::string x) { auto const eq = x.find('='); if (eq == x.npos) { caps.emplace(x, std::string{}); } else { caps.emplace(std::string{x, 0, eq}, std::string{x, eq+1, x.npos}); } } ); if (last) { return send_req(); } return {ThreadOutcome::Continue, EventOutcome::Consume}; } else { return {}; } } auto RegistrationThread::on_msg(IrcMsg const& msg) -> Thread::callback_result { switch (stage_) { case Stage::LsReply: return capls(msg); case Stage::AckReply: return capack(msg); default: return {}; } } auto RegistrationThread::on_event(Event const& event) -> Thread::callback_result { if (auto const irc_event = dynamic_cast(&event)) { return on_msg(irc_event->irc); } if (auto const connect_event = dynamic_cast(&event)) { return on_connect(); } return {}; }