xbot/self_thread.cpp
2025-01-25 15:45:31 -08:00

216 lines
4.9 KiB
C++

#include "self_thread.hpp"
#include "connection.hpp"
#include <mybase64.hpp>
#include <boost/container/flat_map.hpp>
#include <boost/log/trivial.hpp>
using namespace std::literals;
auto SelfThread::on_welcome(const IrcMsg &irc) -> void
{
nickname_ = irc.args[0];
}
auto SelfThread::on_nick(const IrcMsg &irc) -> void
{
if (is_my_mask(irc.source))
{
nickname_ = irc.args[0];
}
}
auto SelfThread::on_umodeis(const IrcMsg &irc) -> void
{
mode_ = irc.args[1];
}
auto SelfThread::on_join(const IrcMsg &irc) -> void
{
if (is_my_mask(irc.source))
{
channels_.insert(std::string{irc.args[0]});
}
}
auto SelfThread::on_kick(const IrcMsg &irc) -> void
{
if (is_my_nick(irc.args[1]))
{
channels_.erase(std::string{irc.args[0]});
}
}
auto SelfThread::on_part(const IrcMsg &irc) -> void
{
if (is_my_mask(irc.source))
{
channels_.erase(std::string{irc.args[0]});
}
}
auto SelfThread::on_mode(const IrcMsg &irc) -> void
{
if (is_my_nick(irc.args[0]))
{
auto polarity = true;
for (const char c : irc.args[1])
{
switch (c)
{
case '+':
polarity = true;
break;
case '-':
polarity = false;
break;
default:
if (polarity)
{
mode_ += c;
}
else
{
const auto ix = mode_.find(c);
if (ix != std::string::npos)
{
mode_.erase(ix, 1);
}
}
break;
}
}
}
}
auto SelfThread::on_isupport(const IrcMsg &msg) -> void
{
const auto hi = msg.args.size() - 1;
for (int i = 1; i < hi; ++i)
{
auto &entry = msg.args[i];
// Leading minus means to stop support
if (entry.starts_with("-"))
{
const auto key = std::string{entry.substr(1)};
if (auto cursor = isupport_.find(key); cursor != isupport_.end())
{
isupport_.erase(cursor);
}
}
else if (const auto cursor = entry.find('='); cursor != entry.npos)
{
isupport_.emplace(entry.substr(0, cursor), entry.substr(cursor + 1));
}
else
{
isupport_.emplace(entry, std::string{});
}
}
}
auto SelfThread::start(Connection &connection) -> std::shared_ptr<SelfThread>
{
auto thread = std::make_shared<SelfThread>(connection);
connection.sig_ircmsg.connect([thread](auto cmd, auto &msg) {
switch (cmd)
{
case IrcCommand::JOIN:
thread->on_join(msg);
break;
case IrcCommand::KICK:
thread->on_kick(msg);
break;
case IrcCommand::MODE:
thread->on_mode(msg);
break;
case IrcCommand::NICK:
thread->on_nick(msg);
break;
case IrcCommand::PART:
thread->on_part(msg);
break;
case IrcCommand::RPL_ISUPPORT:
thread->on_isupport(msg);
break;
case IrcCommand::RPL_UMODEIS:
thread->on_umodeis(msg);
break;
case IrcCommand::RPL_WELCOME:
thread->on_welcome(msg);
break;
default:
break;
}
});
connection.sig_authenticate.connect([thread](auto msg) {
thread->on_authenticate(msg);
});
return thread;
}
auto SelfThread::get_my_nickname() const -> const std::string &
{
return nickname_;
}
auto SelfThread::get_my_mode() const -> const std::string &
{
return mode_;
}
auto SelfThread::get_my_channels() const -> const std::unordered_set<std::string> &
{
return channels_;
}
auto SelfThread::is_my_nick(std::string_view nick) const -> bool
{
return nick == nickname_;
}
auto SelfThread::is_my_mask(std::string_view mask) const -> bool
{
const auto bang = mask.find('!');
return bang != std::string_view::npos && nickname_ == mask.substr(0, bang);
}
auto SelfThread::on_authenticate(const std::string_view body) -> void
{
if (not sasl_mechanism_)
{
BOOST_LOG_TRIVIAL(warning) << "Unexpected AUTHENTICATE from server"sv;
connection_.send_authenticate_abort();
return;
}
if (auto reply = sasl_mechanism_->step(body))
{
connection_.send_authenticate_encoded(*reply);
// Clean up completed SASL transactions
if (sasl_mechanism_->is_complete())
{
sasl_mechanism_.reset();
}
}
}
auto SelfThread::start_sasl(std::unique_ptr<SaslMechanism> mechanism) -> void
{
if (sasl_mechanism_)
{
connection_.send_authenticate("*"sv); // abort SASL
}
sasl_mechanism_ = std::move(mechanism);
connection_.send_authenticate(sasl_mechanism_->mechanism_name());
}