xbot/registration_thread.cpp

217 lines
5.0 KiB
C++
Raw Normal View History

2023-11-25 09:22:55 -08:00
#include "registration_thread.hpp"
2023-11-25 20:09:20 -08:00
#include <memory>
#include <unordered_set>
#include <unordered_map>
namespace {
struct RegistrationThread : std::enable_shared_from_this<RegistrationThread>
{
2023-11-26 15:40:40 -08:00
Connection& connection_;
2023-11-25 20:09:20 -08:00
std::string password_;
std::string username_;
std::string realname_;
std::string nickname_;
std::unordered_map<std::string, std::string> caps;
std::unordered_set<std::string> outstanding;
Connection::Handle<ConnectEvent> connect_handle_;
Connection::Handle<IrcMsgEvent> message_handle_;
enum class Stage
{
LsReply,
AckReply,
};
Stage stage_;
RegistrationThread(
2023-11-26 15:40:40 -08:00
Connection& connection_,
2023-11-25 20:09:20 -08:00
std::string password,
std::string username,
std::string realname,
std::string nickname
);
auto on_connect() -> void;
auto send_req() -> void;
auto capack(IrcMsg const& msg) -> void;
auto capls(IrcMsg const& msg) -> void;
auto on_msg(IrcMsg const& msg) -> void;
};
2023-11-25 09:22:55 -08:00
RegistrationThread::RegistrationThread(
2023-11-26 15:40:40 -08:00
Connection& connection,
2023-11-25 09:22:55 -08:00
std::string password,
std::string username,
std::string realname,
std::string nickname
)
2023-11-25 20:09:20 -08:00
: connection_{connection}
2023-11-25 09:22:55 -08:00
, password_{password}
, username_{username}
, realname_{realname}
, nickname_{nickname}
, stage_{Stage::LsReply}
{}
2023-11-25 20:09:20 -08:00
auto RegistrationThread::on_connect() -> void
2023-11-25 09:22:55 -08:00
{
2023-11-26 15:40:40 -08:00
send_cap_ls(connection_);
send_pass(connection_, password_);
send_user(connection_, username_, realname_);
send_nick(connection_, nickname_);
2023-11-25 20:09:20 -08:00
2023-11-26 15:40:40 -08:00
connection_.remove_listener(connect_handle_);
2023-11-25 09:22:55 -08:00
}
2023-11-25 20:09:20 -08:00
auto RegistrationThread::send_req() -> void
2023-11-25 09:22:55 -08:00
{
std::string request;
2023-11-25 20:09:20 -08:00
char const* 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"
};
2023-11-25 09:22:55 -08:00
for (auto cap : want)
{
if (caps.contains(cap))
{
request.append(cap);
request.push_back(' ');
outstanding.insert(cap);
}
}
if (not outstanding.empty())
{
request.pop_back();
2023-11-26 15:40:40 -08:00
send_cap_req(connection_, request);
2023-11-25 09:22:55 -08:00
stage_ = Stage::AckReply;
}
else
{
2023-11-26 15:40:40 -08:00
send_cap_end(connection_);
connection_.remove_listener(message_handle_);
2023-11-25 09:22:55 -08:00
}
}
2023-11-25 20:09:20 -08:00
auto RegistrationThread::capack(IrcMsg const& msg) -> void
2023-11-25 09:22:55 -08:00
{
auto const n = msg.args.size();
2023-11-26 15:08:55 -08:00
if (n >= 2 && "*" == msg.args[0] && "ACK" == msg.args[1])
2023-11-25 09:22:55 -08:00
{
auto in = std::istringstream{std::string{msg.args[2]}};
std::for_each(
std::istream_iterator<std::string>{in},
std::istream_iterator<std::string>{},
[this](std::string x) {
outstanding.erase(x);
}
);
if (outstanding.empty())
{
2023-11-26 15:40:40 -08:00
send_cap_end(connection_);
connection_.remove_listener(message_handle_);
2023-11-25 09:22:55 -08:00
}
}
}
2023-11-25 20:09:20 -08:00
auto RegistrationThread::capls(IrcMsg const& msg) -> void
2023-11-25 09:22:55 -08:00
{
auto const n = msg.args.size();
2023-11-26 15:08:55 -08:00
if (n >= 2 && "*" == msg.args[0] && "LS" == msg.args[1])
2023-11-25 09:22:55 -08:00
{
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
{
2023-11-25 20:09:20 -08:00
return;
2023-11-25 09:22:55 -08:00
}
auto in = std::istringstream{std::string{*kvs}};
std::for_each(
std::istream_iterator<std::string>{in},
std::istream_iterator<std::string>{},
[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)
{
2023-11-25 20:09:20 -08:00
send_req();
2023-11-25 09:22:55 -08:00
}
}
}
2023-11-25 20:09:20 -08:00
auto RegistrationThread::on_msg(IrcMsg const& msg) -> void
2023-11-25 09:22:55 -08:00
{
switch (stage_)
{
2023-11-25 20:09:20 -08:00
case Stage::LsReply: capls(msg); return;
case Stage::AckReply: capack(msg); return;
2023-11-25 09:22:55 -08:00
}
}
2023-11-25 20:09:20 -08:00
} // namespace
auto registration_thread(
2023-11-26 15:40:40 -08:00
Connection& connection,
2023-11-25 20:09:20 -08:00
std::string password,
std::string username,
std::string realname,
std::string nickname
) -> void
2023-11-25 09:22:55 -08:00
{
2023-11-26 15:08:55 -08:00
auto const thread = std::make_shared<RegistrationThread>(connection, password, username, realname, nickname);
2023-11-26 15:40:40 -08:00
thread->message_handle_ = connection.add_listener<IrcMsgEvent>([thread](IrcMsgEvent const& event)
2023-11-25 09:22:55 -08:00
{
2023-11-26 15:08:55 -08:00
if (IrcCommand::CAP == event.command)
{
thread->on_msg(event.irc);
}
2023-11-25 20:09:20 -08:00
});
2023-11-26 15:40:40 -08:00
thread->connect_handle_ = connection.add_listener<ConnectEvent>([thread](ConnectEvent const&)
2023-11-25 09:22:55 -08:00
{
2023-11-25 20:09:20 -08:00
thread->on_connect();
});
2023-11-25 09:22:55 -08:00
}