214 lines
5.1 KiB
C++
214 lines
5.1 KiB
C++
#include "registration_thread.hpp"
|
|
|
|
#include <memory>
|
|
#include <unordered_set>
|
|
#include <unordered_map>
|
|
|
|
namespace {
|
|
struct RegistrationThread : std::enable_shared_from_this<RegistrationThread>
|
|
{
|
|
Connection * connection_;
|
|
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(
|
|
Connection * connection_,
|
|
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;
|
|
};
|
|
|
|
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::on_connect() -> void
|
|
{
|
|
write_irc(*connection_, "CAP", "LS", "302");
|
|
write_irc(*connection_, "PASS", password_);
|
|
write_irc(*connection_, "USER", username_, "*", "*", realname_);
|
|
write_irc(*connection_, "NICK", nickname_);
|
|
|
|
connection_->remove_listener(connect_handle_);
|
|
}
|
|
|
|
auto RegistrationThread::send_req() -> void
|
|
{
|
|
std::string request;
|
|
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"
|
|
};
|
|
|
|
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;
|
|
}
|
|
else
|
|
{
|
|
write_irc(*connection_, "CAP", "END");
|
|
connection_->remove_listener(message_handle_);
|
|
}
|
|
}
|
|
|
|
auto RegistrationThread::capack(IrcMsg const& msg) -> void
|
|
{
|
|
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<std::string>{in},
|
|
std::istream_iterator<std::string>{},
|
|
[this](std::string x) {
|
|
outstanding.erase(x);
|
|
}
|
|
);
|
|
if (outstanding.empty())
|
|
{
|
|
write_irc(*connection_, "CAP", "END");
|
|
connection_->remove_listener(message_handle_);
|
|
}
|
|
}
|
|
}
|
|
|
|
auto RegistrationThread::capls(IrcMsg const& msg) -> void
|
|
{
|
|
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<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)
|
|
{
|
|
send_req();
|
|
}
|
|
}
|
|
}
|
|
|
|
auto RegistrationThread::on_msg(IrcMsg const& msg) -> void
|
|
{
|
|
switch (stage_)
|
|
{
|
|
case Stage::LsReply: capls(msg); return;
|
|
case Stage::AckReply: capack(msg); return;
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
auto registration_thread(
|
|
Connection * connection,
|
|
std::string password,
|
|
std::string username,
|
|
std::string realname,
|
|
std::string nickname
|
|
) -> void
|
|
{
|
|
auto thread = std::make_shared<RegistrationThread>(connection, password, username, realname, nickname);
|
|
thread->message_handle_ = connection->add_listener<IrcMsgEvent>([thread](IrcMsgEvent const& event)
|
|
{
|
|
thread->on_msg(event.irc);
|
|
});
|
|
thread->connect_handle_ = connection->add_listener<ConnectEvent>([thread](ConnectEvent const&)
|
|
{
|
|
thread->on_connect();
|
|
});
|
|
}
|