#include "irc_config.h" #include #include namespace roro { namespace { bool validNick(const std::string& n) { if (n.empty() || n.size() > 30) return false; if (std::isdigit(static_cast(n[0])) || n[0] == '-') return false; for (char c : n) if (!std::isalnum(static_cast(c)) && !std::strchr("[]\\`_^{|}-", c)) return false; return true; } } // namespace void IrcConfig::load(const std::string& defaultNick) { nick = defaultNick; std::string s; int32_t i; if (store_.getString("irc_host", s)) host = s; if (store_.getInt("irc_port", i)) port = i; if (store_.getInt("irc_tls", i)) tls = i != 0; if (store_.getInt("irc_selfsign", i)) allowSelfSigned = i != 0; store_.getString("irc_pin", pinnedSha256); if (store_.getString("irc_nick", s)) nick = s; store_.getString("irc_sasl_user", saslUser); store_.getString("irc_sasl_pass", saslPassword); store_.getString("irc_ns_pass", nickservPassword); if (store_.getString("irc_join", s)) autojoin = parseChannels(s); } std::string IrcConfig::validate() const { if (host.empty() || host.size() > 63) return "Server must be 1 to 63 characters"; if (port < 1 || port > 65535) return "Port must be 1 to 65535"; if (!validNick(nick)) return "Nick: letters, digits and []\\`_^{|}- only, not starting with a digit"; for (auto& c : autojoin) if (c.channel.size() < 2 || (c.channel[0] != '#' && c.channel[0] != '&')) return "Auto-join: IRC channels start with # or &, each followed by its key if it has one"; return ""; } std::string IrcConfig::save() { std::string error = validate(); if (!error.empty()) return error; store_.putString("irc_host", host); store_.putInt("irc_port", port); store_.putInt("irc_tls", tls ? 1 : 0); store_.putInt("irc_selfsign", allowSelfSigned ? 1 : 0); store_.putString("irc_pin", pinnedSha256); store_.putString("irc_nick", nick); store_.putString("irc_sasl_user", saslUser); store_.putString("irc_sasl_pass", saslPassword); store_.putString("irc_ns_pass", nickservPassword); store_.putString("irc_join", formatChannels(autojoin)); return ""; } std::vector IrcConfig::parseChannels(const std::string& text) { std::vector out; std::string word; auto take = [&]() { if (word.empty()) return; bool channel = word[0] == '#' || word[0] == '&'; if (!channel && !out.empty() && out.back().key.empty()) out.back().key = word; else out.push_back({word, ""}); // a stray word fails validation as a channel word.clear(); }; for (char c : text) { if (c == ' ' || c == ',') take(); else word += c; } take(); return out; } std::string IrcConfig::formatChannels(const std::vector& channels) { std::string out; for (auto& c : channels) out += (out.empty() ? "" : ", ") + c.channel + (c.key.empty() ? "" : " " + c.key); return out; } } // namespace roro