From ee52874033d078f15229dad93a806037917f1801 Mon Sep 17 00:00:00 2001 From: Nils Date: Mon, 21 Jun 2021 12:18:40 +0200 Subject: [PATCH] Added configuration parser --- CMakeLists.txt | 4 ++- config.cpp | 19 ------------ config.hpp | 14 ++++++++- config.inil | 19 ++++++++++++ configParser.cpp | 75 +++++++++++++++++++++++++++++++++++++++++++++++ instance.cpp | 16 +++++----- instance.hpp | 2 +- main.cpp | 2 +- serviceBase.cpp | 18 ++++++++++++ serviceBase.hpp | 4 +++ services/test.cpp | 11 +++---- utility.cpp | 6 ++-- utility.hpp | 2 +- 13 files changed, 150 insertions(+), 42 deletions(-) delete mode 100644 config.cpp create mode 100644 config.inil create mode 100644 configParser.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6976f1c..5c0d142 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ add_executable(asbots utility.cpp serviceBase.cpp services/test.cpp - config.cpp + configParser.cpp ) find_package(PkgConfig REQUIRED) @@ -19,3 +19,5 @@ pkg_check_modules(libasync-uv REQUIRED IMPORTED_TARGET libasync-uv) pkg_check_modules(fmt REQUIRED IMPORTED_TARGET fmt) target_link_libraries(asbots PRIVATE PkgConfig::libasync-uv PkgConfig::fmt) + +configure_file(config.inil config.inil COPYONLY) diff --git a/config.cpp b/config.cpp deleted file mode 100644 index b474387..0000000 --- a/config.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "config.hpp" - - - -Config config = { - .connection = { - .addr = "127.0.0.1", - .port = 6667, - }, - .auth = { - .send_password = "849372523412", - .accept_password = "843574835765" - }, - .server = { - .name = "services.", - .description = "A test service!!", - .uid = "23X" - } -}; diff --git a/config.hpp b/config.hpp index 5cf810c..11348c9 100644 --- a/config.hpp +++ b/config.hpp @@ -3,10 +3,17 @@ #include "uid.hpp" #include +#include +#include +#include struct Config { + struct ParseError : public std::runtime_error { + using std::runtime_error::runtime_error; + }; + struct Connection { std::string addr; int port; @@ -19,5 +26,10 @@ struct Config { SUID uid; // Fourth one reserved for null-terminator bool hidden = false; } server; -} extern config; + + std::unordered_map> misc; +}; + + +Config parseConfig(const std::filesystem::path& path); #endif diff --git a/config.inil b/config.inil new file mode 100644 index 0000000..63804d3 --- /dev/null +++ b/config.inil @@ -0,0 +1,19 @@ +[connection] +addr: 127.0.0.1 +port: 6667 +end + +[auth] +send_password: 849372523412 +accept_password: 843574835765 +end + +[server] +name: services. +description: A test service!! +uid: 23X +end + +[testservice] +channel: #testservice +end diff --git a/configParser.cpp b/configParser.cpp new file mode 100644 index 0000000..c7009b1 --- /dev/null +++ b/configParser.cpp @@ -0,0 +1,75 @@ +#include "config.hpp" +#include "uid.hpp" +#include "utility.hpp" + +#include +#include +#include +#include +#include + + + +inline void noop() {} + +Config parseConfig(const std::filesystem::path& path) { + Config fres; + auto file = std::ifstream(path); + // Read through all sections + while (!file.eof()) { + std::string head; + std::string_view section; + file >> head; + // Skip some emptynes + if (head.empty()) { + continue; + } + // Check section head syntax + if (head.size() < 3 || !head.starts_with('[') || !head.ends_with(']')) { + throw Config::ParseError("Invalid head (section identifier) syntax"); + } + // Extract actual section name + section = {head.data()+1, head.size()-2}; + while (!file.eof()) { + // Get key and value + std::string line; + std::getline(file, line); + // Check for empty line + if (line.empty()) { + continue; + } + // Split into key and value + auto [key, value] = Utility::splitOnce(line, ": "); + // Check for "end" + if (key == "end") { + break; + } + // Do stuff depending on current section using macro magic + # define SECTION(name, ...) if (section == #name) {auto &d = fres.name; __VA_ARGS__; continue;;} noop() + # define ASSIGN(e_key, val) if (key == #e_key) {d.e_key = val; continue;} noop() + SECTION(connection, { + ASSIGN(addr, value); + ASSIGN(port, std::stoi(std::string(value))); + }); + SECTION(auth, { + ASSIGN(send_password, value); + ASSIGN(accept_password, value); + }); + SECTION(server, { + ASSIGN(name, value); + ASSIGN(description, value); + ASSIGN(uid, SUID(value)); + ASSIGN(hidden, value=="true"); + }); + // Misc keys + auto s_res = fres.misc.find(std::string(section)); + if (s_res == fres.misc.end()) { + fres.misc[std::string(section)] = {{std::string(key), std::string(value)}}; + } else { + s_res->second[std::string(key)] = value; + } + } + } + // Return resulting Config object + return fres; +} diff --git a/instance.cpp b/instance.cpp index 33c49a5..66b528d 100644 --- a/instance.cpp +++ b/instance.cpp @@ -34,7 +34,7 @@ void Event::parse(std::string_view str) { name = std::move(split[1]); if (split.size() == 3) { // Get args and text - std::tie(raw_args, text) = colonSplit(split[2]); + std::tie(raw_args, text) = splitOnce(split[2], " :"); } } @@ -43,7 +43,7 @@ void Command::parse(std::string_view str) { auto split = strSplit(str, ' ', 1); name = std::move(split[0]); // Get text from args - std::tie(args, text) = colonSplit(split[1]); + std::tie(args, text) = splitOnce(split[1], " :"); } @@ -248,9 +248,9 @@ async::result Instance::run() { } async::result Instance::login() { - co_await socket->send("PASS {} TS 6 :{}\n"_format(config.get().auth.send_password, config.get().server.uid.str())); + co_await socket->send("PASS {} TS 6 :{}\n"_format(config.auth.send_password, config.server.uid.str())); co_await socket->send("CAPAB :QS EX IE KLN UNKLN ENCAP TB SERVICES EUID EOPMOD MLOCK\n"sv); - co_await socket->send("SERVER {} 1 :{}{}\n"_format(config.get().server.name, config.get().server.hidden ? "(H) " : "", config.get().server.description)); + co_await socket->send("SERVER {} 1 :{}{}\n"_format(config.server.name, config.server.hidden ? "(H) " : "", config.server.description)); co_await socket->send("SVINFO 6 3 0 :{}\n"_format(time(nullptr))); } @@ -268,7 +268,7 @@ async::result Instance::process(const Command command) { if (command.name == "PASS") { // Check password auto given_password = strSplit(command.args, ' ', 1)[0]; - if (given_password != config.get().auth.accept_password) { + if (given_password != config.auth.accept_password) { throw ConnectionError("Server supplied wrong password during authentication"); } authed = true; @@ -277,7 +277,7 @@ async::result Instance::process(const Command command) { } else if (command.name == "ERROR") { throw ConnectionError(command.dump()); } else if (command.name == "SQUIT") { - if (command.args == config.get().server.uid.str()) { + if (command.args == config.server.uid.str()) { throw ConnectionError(command.dump()); } } else if (!authed) { @@ -294,7 +294,7 @@ async::result Instance::process(const Command command) { co_await burst(); } // Reply - co_await socket->send(":{} PONG {} {} :{}\n"_format(config.get().server.uid.str(), config.get().server.name, command.args, command.text)); + co_await socket->send(":{} PONG {} {} :{}\n"_format(config.server.uid.str(), config.server.name, command.args, command.text)); } } @@ -307,7 +307,7 @@ async::result Instance::process(Event event) { // Fetched info else if (event.name == "005") { // Check if that 005 was for me - if (event.args[0] != config.get().server.uid.str()) { + if (event.args[0] != config.server.uid.str()) { co_return; } // Split the list diff --git a/instance.hpp b/instance.hpp index 1075768..c108c8f 100644 --- a/instance.hpp +++ b/instance.hpp @@ -227,7 +227,7 @@ class Instance { public: std::unique_ptr socket; - std::reference_wrapper config; + const Config config; Config::Server connected_server; NetworkInfo netInfo; Cache cache; diff --git a/main.cpp b/main.cpp index 42a2a9f..12bf635 100644 --- a/main.cpp +++ b/main.cpp @@ -15,7 +15,7 @@ int main() { async::queue_scope qs{&rq}; loop_service service; - Instance instance(service, config); + Instance instance(service, parseConfig("config.inil")); instance.services.push_back(std::make_unique()); async::detach(instance.run()); diff --git a/serviceBase.cpp b/serviceBase.cpp index f8252cf..d262594 100644 --- a/serviceBase.cpp +++ b/serviceBase.cpp @@ -2,6 +2,8 @@ #include "instance.hpp" #include +#include +#include #include @@ -11,3 +13,19 @@ async::result ServiceBase::mark_ready(const User& user) { co_await i->send_event(user.get_euid()); ready = true; } + +std::optional ServiceBase::getConfig(const std::string& section, const std::string& key) { + auto &cfg = i->config.misc; + // Get section + auto s_res = cfg.find(section); + if (s_res == cfg.end()) { + return {}; + } + // Get key + auto k_res = s_res->second.find(key); + if (k_res == s_res->second.end()) { + return {}; + } + // Return final result + return {k_res->second}; +} diff --git a/serviceBase.hpp b/serviceBase.hpp index 8c7577d..e1ff1cf 100644 --- a/serviceBase.hpp +++ b/serviceBase.hpp @@ -8,6 +8,9 @@ class Instance; #include "incompletes.hpp" #include +#include +#include +#include @@ -22,5 +25,6 @@ public: virtual async::result on_direct_privmsg(std::string_view msg, u_User& author) = 0; async::result mark_ready(const User& user); + std::optional getConfig(const std::string& section, const std::string& key); }; #endif diff --git a/services/test.cpp b/services/test.cpp index 4095bbe..e7cb325 100644 --- a/services/test.cpp +++ b/services/test.cpp @@ -8,13 +8,10 @@ using fmt::operator""_format; -const auto serviceChannelName = "#servicechannel"; - - async::result TestService::intitialize() { co_await i->netInfo.wait_ready(); user = { - .server = i->config.get().server.uid, + .server = i->config.server.uid, .nick = "services", .ident = "services", .realhost = "services.", @@ -23,13 +20,13 @@ async::result TestService::intitialize() { .loginName = "serviceD" }; Channel serviceChannel = { - .server = i->config.get().server.uid, - .name = serviceChannelName + .server = i->config.server.uid, + .name = std::string(getConfig("testservice", "channel").value_or("#services")) }; co_await mark_ready(user); co_await i->send_event(serviceChannel.get_sjoin(user.uid)); co_await i->send_event(serviceChannel.get_topic("IRC Service channel", user.uid)); - co_await i->send_event(user.get_notice("Test... Hello world!", serviceChannelName)); + co_await i->send_event(user.get_notice("Test... Hello world!", serviceChannel.name)); } async::result TestService::on_event(const Event& event) { diff --git a/utility.cpp b/utility.cpp index fad7258..81135b8 100644 --- a/utility.cpp +++ b/utility.cpp @@ -23,9 +23,9 @@ std::vector strSplit(std::string_view s, char delimiter, size_ return to_return; } -std::tuple colonSplit(std::string_view s) { +std::tuple splitOnce(std::string_view s, std::string_view at) { // Find the colon - auto colonPos = s.find(" :"); + auto colonPos = s.find(at); if (colonPos == s.npos) { return {s, ""}; } @@ -54,7 +54,7 @@ std::string lowers(std::string_view str) { static const char UUIDChars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; UUID Instance::UUIDGen() { size_t numUUID = ++lastUUID; - UUID fres = config.get().server.uid.str(); + UUID fres = config.server.uid.str(); for (auto it = fres.array.end() - 1; it != fres.array.begin()+SUID_len-1; it--) { auto idx = std::min(numUUID, sizeof(UUIDChars)-1); *it = UUIDChars[idx]; diff --git a/utility.hpp b/utility.hpp index de35db7..8221dd5 100644 --- a/utility.hpp +++ b/utility.hpp @@ -11,7 +11,7 @@ namespace Utility { constexpr size_t strSplitInf = 0; std::vector strSplit(std::string_view s, char delimiter, size_t times = strSplitInf); -std::tuple colonSplit(std::string_view s); +std::tuple splitOnce(std::string_view s, std::string_view at); void argsSizeCheck(std::string_view where, std::vector args, size_t expected); std::string lowers(std::string_view str); }