From 315b65f0c3f42a11e8f868b0f3104d47c0832280 Mon Sep 17 00:00:00 2001 From: Nils Date: Mon, 21 Jun 2021 00:29:37 +0200 Subject: [PATCH] Even more improvements lol --- exceptions.hpp | 21 +++++++++ incompletes.hpp | 13 ++++++ instance.cpp | 101 ++++++++++++++++++++++++++---------------- instance.hpp | 87 +++++++++++++++++++++--------------- serviceBase.hpp | 3 +- services/template.cpp | 2 +- services/template.hpp | 2 +- services/test.cpp | 32 ++++++++++--- services/test.hpp | 2 +- 9 files changed, 178 insertions(+), 85 deletions(-) create mode 100644 exceptions.hpp create mode 100644 incompletes.hpp diff --git a/exceptions.hpp b/exceptions.hpp new file mode 100644 index 0000000..fe2124f --- /dev/null +++ b/exceptions.hpp @@ -0,0 +1,21 @@ +#ifndef __EXCEPTIONS_HPP +#define __EXCEPTIONS_HPP +#include + + + +struct ParseError : public std::runtime_error { + using std::runtime_error::runtime_error; +}; +struct ConnectionError : public std::runtime_error { + using std::runtime_error::runtime_error; +}; +struct DesyncError : public std::exception { + const char *what() const throw() { + return "Server has desynced!!!"; + } + DesyncError() { + std::cout << "DESCONACCREKO" << std::endl; + } +}; +#endif diff --git a/incompletes.hpp b/incompletes.hpp new file mode 100644 index 0000000..2b3faae --- /dev/null +++ b/incompletes.hpp @@ -0,0 +1,13 @@ +#ifndef _INCOMPLETES_HPP +#define _INCOMPLETES_HPP +#include + + + +struct User; +struct Channel; +struct Cache; +struct NetworkInfo; +using u_User = std::unique_ptr; +using u_Channel = std::unique_ptr; +#endif diff --git a/instance.cpp b/instance.cpp index ab73b2a..010aecd 100644 --- a/instance.cpp +++ b/instance.cpp @@ -36,7 +36,7 @@ void Event::parse(std::string_view str) { name = std::move(split[1]); if (split.size() == 3) { // Get args and text - std::tie(args, text) = colonSplit(split[2]); + std::tie(raw_args, text) = colonSplit(split[2]); } } @@ -112,19 +112,22 @@ void ModeSet::parse(std::string_view in, NetworkInfo &netInfo) { void User::parse_euid(const Event& event, NetworkInfo& netInfo) { this->server = std::get(event.sender.id); - auto split = strSplit(event.args, ' ', 8); // Check size - if (split.size() != 9) { + if (event.args.size() < 10) { throw ParseError("In euid parser: This euid event does not have enough arguments"); } // Move values - nick = std::move(split[0]); - hops = std::stoull(std::string(split[1])); - umode.parse(split[3], netInfo); - ident = std::move(split[4]); - host = std::move(split[5]); - realhost = std::move(split[6]); - uid = UUID(std::move(split[7])); + nick = event.args[0]; + hops = std::stoull(std::string(event.args[1])); + umode.parse(event.args[3], netInfo); + ident = event.args[4]; + vhost = event.args[5]; + ip = event.args[6]; + uid = UUID(event.args[7]); + realhost = event.args[8]; + if (event.args[9] != "*" && event.args[9] != "0") { + loginName = event.args[9]; + } // Get realname realname = std::move(event.text); } @@ -136,14 +139,13 @@ void User::removeChannel(const u_Channel& channel) { void Channel::parse_sjoin(const Event& event, Cache& cache, NetworkInfo& netInfo) { this->server = std::get(event.sender.id); - auto split = strSplit(event.args, ' ', 2); // Check size - if (split.size() != 3) { + if (event.args.size() < 3) { throw ParseError("In euid parser: This euid event does not have enough arguments"); } // Move values - name = std::move(split[1]); - mode.parse(split[2], netInfo); + name = std::move(event.args[1]); + mode.parse(event.args[2], netInfo); // Get members for (auto& raw_uuid : strSplit(event.text, ' ')) { // Erase prefix @@ -302,23 +304,21 @@ async::result Instance::process(const Command command) { } } -async::result Instance::process(const Event event) { +async::result Instance::process(Event event) { + event.splitArgs(); std::clog << event.dump() << std::flush; - if (event.name == "NOTICE") { - // Don't do anything special - } else if (!authed) { + if (!authed && event.name != "NOTICE") { throw ConnectionError("Server tried to send an event before authenticating"); } // Fetched info else if (event.name == "005") { // Check if that 005 was for me - if (event.args.find(config.get().server.uid.str()) != 0) { + if (event.args[0] != config.get().server.uid.str()) { co_return; } // Split the list - auto split = strSplit(event.args, ' '); // Iterate and find the information we need - for (const auto& field : split) { + for (const auto& field : event.args) { // Split into key and value auto split = strSplit(field, '=', 1); // Check size @@ -389,7 +389,7 @@ async::result Instance::process(const Event event) { throw DesyncError(); } // Set nick - res->get()->nick = strSplit(event.args, ' ', 1)[0]; + res->get()->nick = event.args[0]; } else if (event.name == "MODE") { // User changed their mode // Find user in cache @@ -399,6 +399,34 @@ async::result Instance::process(const Event event) { } // Update mode res->get()->umode.parse(event.text, netInfo); + } else if (event.name == "ENCAP") { + // Get args + if (event.args.size() < 3) { + throw ParseError("In encap event parser: at least * and encap name need to be passed"); + } + if (event.args[1] == "SU") { + // User logged in + // Check args + if (event.args.size() < 5) { + throw ParseError("In encap su event parser: this encap requires UID as first argument and optionally login name as second one"); + } + // Find user in cache + auto res = cache.find_user_by_uid(event.args[2]); + if (res == cache.users.end()) { + throw DesyncError(); + } + // Update login name + if (event.args.size() > 3) { + auto loginName = event.args[3]; + if (loginName == "*" || loginName == "0") { + res->get()->loginName.reset(); + } else { + res->get()->loginName = event.args[3]; + } + } else { + res->get()->loginName.reset(); + } + } } // Channel updates else if (event.name == "SJOIN") { @@ -411,7 +439,7 @@ async::result Instance::process(const Event event) { } else if (event.name == "TOPIC" || event.name == "TB") { // Channels topic changed // Find channel in cache - auto res = cache.find_channel_by_name(strSplit(event.args, ' ', 1)[0]); + auto res = cache.find_channel_by_name(event.args[0]); if (res == cache.channels.end()) { throw DesyncError(); } @@ -420,12 +448,11 @@ async::result Instance::process(const Event event) { } else if (event.name == "JOIN") { // User joined existing channel // Split args - auto split = strSplit(event.args, ' ', 2); - if (split.size() != 3) { + if (event.args.size() < 3) { throw ParseError("In join event parser: join even did not receive enough arguments (expected 3)"); } // Get channel from cache - auto c_res = cache.find_channel_by_name(split[1]); + auto c_res = cache.find_channel_by_name(event.args[1]); if (c_res == cache.channels.end()) { throw DesyncError(); } @@ -438,17 +465,16 @@ async::result Instance::process(const Event event) { c_res->get()->members.push_back(&*u_res); u_res->get()->channels.push_back(&*c_res); // Update channel modes - c_res->get()->mode.parse(split[2], netInfo); + c_res->get()->mode.parse(event.args[2], netInfo); } else if (event.name == "PART" || event.name == "KICK") { // User left channel - auto split = strSplit(event.args, ' ', 1); // Get channel from cache - auto c_res = cache.find_channel_by_name(split[0]); + auto c_res = cache.find_channel_by_name(event.args[0]); if (c_res == cache.channels.end()) { throw DesyncError(); } // Get user from cache - auto u_res = cache.find_user_by_uid((event.name=="PART")?std::get(event.sender.id):split[1]); + auto u_res = cache.find_user_by_uid((event.name=="PART")?std::get(event.sender.id):event.args[1]); if (u_res == cache.users.end()) { throw DesyncError(); } @@ -460,12 +486,11 @@ async::result Instance::process(const Event event) { // Split args std::string_view modes, channelName; { - auto split = strSplit(event.args, ' ', 2); - if (split.size() != 3) { - throw DesyncError(); + if (event.args.size() < 3) { + throw ParseError("In MODE/BMASK event parser: did not receive enough arguments (expected 3)"); } - channelName = split[1]; - modes = split[2]; + channelName = event.args[1]; + modes = event.args[2]; } // Get channel from cache auto c_res = cache.find_channel_by_name(channelName); @@ -479,18 +504,18 @@ async::result Instance::process(const Event event) { else if (event.name == "PRIVMSG") { // On message // Check that message is not directed to channel - if (isalnum(event.args[0])) { + if (isalnum(event.raw_args[0])) { // Get author from cache auto res = cache.find_user_by_uid(std::get(event.sender.id)); if (res == cache.users.end()) { throw DesyncError(); } // Get target UUID - UUID target(event.args); + UUID target(event.args[0]); // Pass to services with ownership over target for (auto& service : services) { if (service->ready && service->uuid.str() == target.str()) { - co_await service->on_direct_privmsg(event.text, *res->get()); + co_await service->on_direct_privmsg(event.text, *res); } } } diff --git a/instance.hpp b/instance.hpp index 914c908..1075768 100644 --- a/instance.hpp +++ b/instance.hpp @@ -3,6 +3,9 @@ #include "config.hpp" #include "uid.hpp" #include "serviceBase.hpp" +#include "utility.hpp" +#include "incompletes.hpp" +#include "exceptions.hpp" #include #include @@ -11,44 +14,26 @@ #include #include #include +#include #include #include #include -#include #include -struct ParseError : public std::runtime_error { - using std::runtime_error::runtime_error; -}; -struct ConnectionError : public std::runtime_error { - using std::runtime_error::runtime_error; -}; -struct DesyncError : public std::exception { - const char *what() const throw() { - return "Server has desynced!!!"; - } - DesyncError() { - std::cout << "DESCONACCREKO" << std::endl; - } -}; - -struct User; -struct Channel; -struct Cache; -struct NetworkInfo; -using u_User = std::unique_ptr; -using u_Channel = std::unique_ptr; - -struct Event { +struct Event { // DON'T std::move() this class. EVER!!! AnyUID sender; - std::string name, args, text; + std::string name, raw_args, text; + std::vector args; std::string dump() const { - return fmt::format(":{} {} {}{}\n", sender.str(), name, args, (text.empty()?"":" :"+text)); + return fmt::format(":{} {} {}{}\n", sender.str(), name, raw_args, (text.empty()?"":" :"+text)); } void parse(std::string_view str); + void splitArgs() { + args = Utility::strSplit(raw_args, ' '); + } }; struct Command { @@ -82,10 +67,18 @@ struct Channel { return { .sender = SUID(server), .name = "SJOIN", - .args = fmt::format("{} {} +{}", time(nullptr), name, mode.str), + .raw_args = fmt::format("{} {} +{}", time(nullptr), name, mode.str), .text = fmt::format("@{}", initial_user.str()) }; } + Event get_topic(std::string_view topic, const UUID& user) const { + return { + .sender = user, + .name = "TOPIC", + .raw_args = name, + .text = std::string(topic) + }; + } }; struct User { @@ -94,11 +87,13 @@ struct User { size_t hops = 1; ModeSet umode; std::string ident; - std::string host; + std::optional vhost; + std::optional ip; std::string realhost; UUID uid; std::string realname; std::vector channels; + std::optional loginName; void parse_euid(const Event &event, NetworkInfo& netInfo); void removeChannel(const u_Channel &channel); @@ -107,21 +102,32 @@ struct User { return { .sender = SUID(server), .name = "EUID", - .args = fmt::format("{} 1 {} +{} {} {} 0 {} * * :{}", nick, time(nullptr), umode.str, ident, host, uid.str(), realname) + // nick + // | hops + // | | ts + // | | | umode + // | | | | ident + // | | | | | vhost + // | | | | | | ip + // | | | | | | | uid + // | | | | | | | | realhost + // | | | | | | | | | account + // | | | | | | | | | | realname + .raw_args = fmt::format("{} 1 {} +{} {} {} {} {} {} {} :{}", nick, time(nullptr), umode.str, ident, vhost.value_or(realhost), ip.value_or("*"), uid.str(), realhost, loginName.value_or("*"), realname) }; } Event get_join(std::string_view channelName) const { return { .sender = uid, .name = "JOIN", - .args = fmt::format("{} {} +", time(nullptr), channelName) + .raw_args = fmt::format("{} {} +", time(nullptr), channelName) }; } Event get_part(std::string_view channelName, std::string_view message = "") const { return { .sender = uid, .name = "PART", - .args = std::string(channelName), + .raw_args = std::string(channelName), .text = std::string(message) }; } @@ -133,19 +139,28 @@ struct User { }; } - Event get_privmsg(std::string_view message, AnyUID target) { + Event get_encap_su(std::string_view login_name) { + //: ENCAP * SU + return { + .sender = server, + .name = "ENCAP", + .raw_args = fmt::format("* SU {} {}", uid.str(), login_name) + }; + } + + Event get_privmsg(std::string_view message, AnyUID target) const { return { .sender = uid, .name = "PRIVMSG", - .args = std::string(target.str()), + .raw_args = std::string(target.str()), .text = std::string(message) }; } - Event get_notice(std::string_view message, AnyUID target) { + Event get_notice(std::string_view message, AnyUID target) const { return { .sender = uid, .name = "NOTICE", - .args = std::string(target.str()), + .raw_args = std::string(target.str()), .text = std::string(message) }; } @@ -227,7 +242,7 @@ public: async::result login(); async::result burst(); async::result process(const Command); - async::result process(const Event); + async::result process(Event); async::result send_event(const Event&); async::result send_ping(); diff --git a/serviceBase.hpp b/serviceBase.hpp index 004690a..8c7577d 100644 --- a/serviceBase.hpp +++ b/serviceBase.hpp @@ -5,6 +5,7 @@ class User; class Instance; #include "uid.hpp" +#include "incompletes.hpp" #include @@ -18,7 +19,7 @@ public: virtual async::result intitialize() = 0; virtual async::result on_event(const Event& event) = 0; - virtual async::result on_direct_privmsg(std::string_view msg, const User& author) = 0; + virtual async::result on_direct_privmsg(std::string_view msg, u_User& author) = 0; async::result mark_ready(const User& user); }; diff --git a/services/template.cpp b/services/template.cpp index 2573744..82859f2 100644 --- a/services/template.cpp +++ b/services/template.cpp @@ -23,6 +23,6 @@ async::result TestService::on_event(const Event& event) { co_return; } -async::result TestService::on_direct_privmsg(std::string_view msg, const User& author) { +async::result TestService::on_direct_privmsg(std::string_view msg, u_User& author) { co_await i->send_event(user.get_privmsg("I received a message: "+std::string(msg), "#lol")); } diff --git a/services/template.hpp b/services/template.hpp index d436ca9..195485d 100644 --- a/services/template.hpp +++ b/services/template.hpp @@ -12,6 +12,6 @@ class TestService : public ServiceBase { virtual async::result intitialize() override; virtual async::result on_event(const Event& event) override; - virtual async::result on_direct_privmsg(std::string_view msg, const User& author) override; + virtual async::result on_direct_privmsg(std::string_view msg, u_User& author) override; }; #endif diff --git a/services/test.cpp b/services/test.cpp index 3f35a12..a075f44 100644 --- a/services/test.cpp +++ b/services/test.cpp @@ -1,4 +1,5 @@ #include "test.hpp" +#include "../utility.hpp" #include #include @@ -16,18 +17,19 @@ async::result TestService::intitialize() { .server = i->config.get().server.uid, .nick = "services", .ident = "services", - .host = "services.", - .realhost = "127.0.0.1", + .realhost = "services.", .uid = uuid, - .realname = i->netInfo.name + .realname = i->netInfo.name, + .loginName = "serviceD" }; Channel serviceChannel = { .server = i->config.get().server.uid, .name = serviceChannelName }; co_await mark_ready(user); - co_await i->send_event(serviceChannel.get_sjoin(uuid)); - co_await i->send_event(user.get_join(serviceChannelName)); + 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_encap_su("serviceD")); DOES NOT YET WORK!! co_await i->send_event(user.get_notice("Test... Hello world!", serviceChannelName)); } @@ -35,6 +37,22 @@ async::result TestService::on_event(const Event& event) { co_return; } -async::result TestService::on_direct_privmsg(std::string_view msg, const User& author) { - co_await i->send_event(user.get_privmsg("I received a message from {}: {}"_format(author.nick, msg), serviceChannelName)); +async::result TestService::on_direct_privmsg(std::string_view msg, u_User &author) { + auto cmd = Utility::strSplit(msg, ' ', 1); + Event response = user.get_notice("You need to log in first", author->uid); + if (cmd[0] == "SEND") { + if (author->loginName.value_or("") == "NetOP") { + Event event; + try { + event.parse(cmd[1]); + co_await i->send_event(event); + response = user.get_notice("Sent.", author->uid); + } catch (std::exception&e) { + response = user.get_notice(e.what(), author->uid); + } + } + } else { + response = user.get_notice("Unknown command", author->uid); + } + co_await i->send_event(response); } diff --git a/services/test.hpp b/services/test.hpp index d436ca9..195485d 100644 --- a/services/test.hpp +++ b/services/test.hpp @@ -12,6 +12,6 @@ class TestService : public ServiceBase { virtual async::result intitialize() override; virtual async::result on_event(const Event& event) override; - virtual async::result on_direct_privmsg(std::string_view msg, const User& author) override; + virtual async::result on_direct_privmsg(std::string_view msg, u_User& author) override; }; #endif