#include "priv_thread.hpp" #include "command_thread.hpp" #include "connection.hpp" #include "write_irc.hpp" #include #include #include #include #include PrivThread::PrivThread(Connection& connection, std::string config_path) : connection_{connection} , config_path_{std::move(config_path)} { } auto PrivThread::check_command(CommandEvent& event, std::string priv) -> bool { auto check_priv = [&priv](auto& map, std::string const& name) { auto const cursor = map.find(name); return cursor != map.end() && cursor->second.contains(priv); }; auto check = [&check_priv](auto& map, auto& name) { return (not name.empty() && (check_priv(map, wildcard) || check_priv(map, std::string{name}))); }; return check(oper_privs_, event.oper) || check(account_privs_, event.account); } auto PrivThread::list_privs(std::string_view nick) -> void { for (auto const& [oper, privset] : oper_privs_) { std::string message = "Oper "; message += oper; message += ":"; for (auto const& priv : privset) { message += " "; message += priv; } send_notice(connection_, nick, message); } for (auto const& [account, privset] : account_privs_) { std::string message = "Account "; message += account; message += ":"; for (auto const& priv : privset) { message += " "; message += priv; } send_notice(connection_, nick, message); } } auto PrivThread::on_command(CommandEvent& event) -> void { if ("list_privs" == event.command) { if (check_command(event, PrivThread::owner_priv)) { list_privs(event.nick); } } else if ("add_priv" == event.command) { if (check_command(event, PrivThread::owner_priv)) { auto in = std::istringstream{std::string{event.arg}}; std::string kind, name, priv; if (in >> kind >> name >> priv) { if ("oper" == kind) { oper_privs_[name].insert(priv); save_config(); send_notice(connection_, event.nick, "ack"); BOOST_LOG_TRIVIAL(info) << "Priv added. by:" << event.nick << " oper:" << name << " priv:" << priv; return; } else if ("account" == kind) { account_privs_[name].insert(priv); save_config(); send_notice(connection_, event.nick, "ack"); BOOST_LOG_TRIVIAL(info) << "Priv added. by:" << event.nick << " account:" << name << " priv:" << priv; return; } } send_notice(connection_, event.nick, "nak"); } } else if ("remove_priv" == event.command) { if (check_command(event, PrivThread::owner_priv)) { auto in = std::istringstream{std::string{event.arg}}; std::string kind, name, priv; if (in >> kind >> name >> priv) { if ("oper" == kind) { if (wildcard == priv) { oper_privs_.erase(name); } else { oper_privs_[name].erase(priv); if (oper_privs_[name].empty()) oper_privs_.erase(name); } save_config(); send_notice(connection_, event.nick, "ack"); BOOST_LOG_TRIVIAL(info) << "Priv removed. by:" << event.nick << " oper:" << name << " priv:" << priv; return; } else if ("account" == kind) { if (wildcard == priv) { account_privs_.erase(name); } else { account_privs_[name].erase(priv); if (account_privs_[name].empty()) account_privs_.erase(name); } save_config(); send_notice(connection_, event.nick, "ack"); BOOST_LOG_TRIVIAL(info) << "Priv removed. by:" << event.nick << " account:" << name << " priv:" << priv; return; } } send_notice(connection_, event.nick, "nak"); } } } auto PrivThread::save_config() -> void { auto config = toml::table{}; auto serialize_table = [&config](auto name, auto& map) { if (not map.empty()) { auto tab = toml::table{}; for (auto const& [oper, privs] : map) { auto privset = toml::array{}; for (auto const& priv : privs) { privset.push_back(priv); } tab.insert(oper, privset); } config.insert(name, tab); } }; serialize_table("oper", oper_privs_); serialize_table("account", account_privs_); std::string tmp = config_path_ + ".tmp"; std::ofstream{tmp} << config; std::filesystem::rename(tmp, config_path_); } auto PrivThread::load_config() -> void { if (auto in = std::ifstream{config_path_}) { auto const config = toml::parse(in); if (config.contains("oper")) { for (auto const [oper, privs] : *config["oper"].as_table()) { auto& privset = oper_privs_[std::string{oper.str()}]; for (auto const& priv : *privs.as_array()) { privset.insert(priv.as_string()->get()); } } } if (config.contains("account")) { for (auto const [account, privs] : *config["account"].as_table()) { auto& privset = account_privs_[std::string{account.str()}]; for (auto const& priv : *privs.as_array()) { privset.insert(priv.as_string()->get()); } } } } } auto PrivThread::start(Connection& connection, std::string config_path) -> std::shared_ptr { auto thread = std::make_shared(connection, config_path); thread->load_config(); connection.add_listener([thread](CommandEvent& event) { thread->on_command(event); }); return thread; }