From 541239b2d5d47d6d745a85313d8272c4374bbc95 Mon Sep 17 00:00:00 2001 From: Nils Date: Mon, 28 Jun 2021 12:06:55 +0200 Subject: [PATCH] First very very very basic ChanServ implementation --- exceptions.hpp | 3 +++ incompletes.hpp | 2 -- instance.cpp | 43 ++++++++++++++++++++++--------------------- instance.hpp | 34 +++++++++++++++++++--------------- main.cpp | 2 ++ serviceBase.cpp | 2 +- serviceBase.hpp | 4 ++-- services/chanserv.cpp | 33 +++++++++++++++++++-------------- services/chanserv.hpp | 11 ++++++----- services/nickserv.cpp | 6 +++--- services/nickserv.hpp | 8 ++++---- 11 files changed, 81 insertions(+), 67 deletions(-) diff --git a/exceptions.hpp b/exceptions.hpp index d24d66c..749f713 100644 --- a/exceptions.hpp +++ b/exceptions.hpp @@ -33,6 +33,9 @@ struct DesyncError : public std::exception { const char *what() const throw() { return "Server has desynced!!!"; } + DesyncError() { + abort(); + } }; struct InsufficientArgsError : public ParseError { diff --git a/incompletes.hpp b/incompletes.hpp index d45e53b..5adda14 100644 --- a/incompletes.hpp +++ b/incompletes.hpp @@ -25,6 +25,4 @@ 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 f8ccce1..4cc328a 100644 --- a/instance.cpp +++ b/instance.cpp @@ -145,8 +145,8 @@ void User::parse_euid(const Event& event, NetworkInfo& netInfo) { realname = std::move(event.text); } -void User::removeChannel(const u_Channel& channel) { - std::remove_if(channels.begin(), channels.end(), [&] (auto obj) {return obj == &channel;}); +void User::removeChannel(const Channel *channel) { + std::remove_if(channels.begin(), channels.end(), [&] (auto obj) {return obj == channel;}); } @@ -154,13 +154,14 @@ void Channel::parse_sjoin(const Event& event, Cache& cache, NetworkInfo& netInfo this->server = std::get(event.sender.id); // Check size argsSizeCheck("SJOIN", event.args, 3); - // Move values - name = std::move(event.args[1]); + // Set values + creationTime = std::stoull(std::string(event.args[0])); + name = event.args[1]; mode.parse(event.args[2], netInfo); // Get members for (auto& raw_uuid : strSplit(event.text, ' ')) { // Erase prefix - if (raw_uuid.size() > UUID_len) { + while (raw_uuid.size() > UUID_len) { char prefixChar = raw_uuid[0]; raw_uuid = {raw_uuid.data()+1, raw_uuid.size()-1}; char modeChar = netInfo.channelModes.prefixMap[prefixChar]; @@ -172,18 +173,18 @@ void Channel::parse_sjoin(const Event& event, Cache& cache, NetworkInfo& netInfo if (res == cache.users.end()) { throw DesyncError(); } - u_User& member = *res; + User *member = res->get(); // Append member to list - members.push_back(&member); + members.push_back(member); } } -void Channel::removeMember(const u_User& member) { - std::remove_if(members.begin(), members.end(), [&] (auto obj) {return *obj == member;}); +void Channel::removeMember(const User *member) { + std::remove_if(members.begin(), members.end(), [&] (auto obj) {return obj == member;}); } -std::vector::iterator Cache::find_user_by_nick(std::string_view nick) { +std::vector>::iterator Cache::find_user_by_nick(std::string_view nick) { for (auto it = users.begin(); ; it++) { if (it == users.end() || it->get()->nick == nick) { return it; @@ -191,7 +192,7 @@ std::vector::iterator Cache::find_user_by_nick(std::string_view nick) { } } -std::vector::iterator Cache::find_user_by_uid(const UUID& uid) { +std::vector>::iterator Cache::find_user_by_uid(const UUID& uid) { for (auto it = users.begin(); ; it++) { if (it == users.end() || it->get()->uid == uid) { return it; @@ -199,7 +200,7 @@ std::vector::iterator Cache::find_user_by_uid(const UUID& uid) { } } -std::vector::iterator Cache::find_channel_by_name(std::string_view name) { +std::vector>::iterator Cache::find_channel_by_name(std::string_view name) { for (auto it = channels.begin(); ; it++) { if (it == channels.end() || it->get()->name == name) { return it; @@ -388,7 +389,7 @@ async::result Instance::process(Event event) { } // Delete user from all channels for (auto& channel : res->get()->channels) { - channel->get()->removeMember({*res}); + channel->removeMember(res->get()); } // Delete user from cache cache.users.erase(res); @@ -466,10 +467,10 @@ async::result Instance::process(Event event) { throw DesyncError(); } // Assign user to channel and vice versa - c_res->get()->members.push_back(&*u_res); - u_res->get()->channels.push_back(&*c_res); + c_res->get()->members.push_back(u_res->get()); + u_res->get()->channels.push_back(c_res->get()); // Update channel modes - c_res->get()->mode.parse(event.args[2], netInfo); + c_res->get()->mode.parse(event.fromArgToEnd(event.args[2]), netInfo); } else if (event.name == "PART" || event.name == "KICK") { // User left channel // Get channel from cache @@ -483,8 +484,8 @@ async::result Instance::process(Event event) { throw DesyncError(); } // Remove channel from both user and channel - c_res->get()->removeMember(*u_res); - u_res->get()->removeChannel(*c_res); + c_res->get()->removeMember(u_res->get()); + u_res->get()->removeChannel(c_res->get()); } else if (event.name == "TMODE" || event.name == "BMASK") { // Channel modes changed argsSizeCheck(event.name, event.args, 3); @@ -492,7 +493,7 @@ async::result Instance::process(Event event) { std::string_view modes, channelName; { channelName = event.args[1]; - modes = event.args[2]; + modes = event.fromArgToEnd(event.args[2]); } // Get channel from cache auto c_res = cache.find_channel_by_name(channelName); @@ -500,7 +501,7 @@ async::result Instance::process(Event event) { throw DesyncError(); } // Apply changes - c_res->get()->mode.parse("+{} {}"_format(modes, event.text), netInfo); + c_res->get()->mode.parse("+{}"_format(modes), netInfo); } // Messages else if (event.name == "PRIVMSG") { @@ -517,7 +518,7 @@ async::result Instance::process(Event event) { // 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); + co_await service->on_direct_privmsg(event.text, res->get()); } } } diff --git a/instance.hpp b/instance.hpp index 1439e99..bcdd1d8 100644 --- a/instance.hpp +++ b/instance.hpp @@ -52,6 +52,9 @@ struct Event { // DON'T std::move() this class. EVER!!! void splitArgs() { args = Utility::strSplit(raw_args, ' '); } + std::string_view fromArgToEnd(std::string_view arg) { + return {arg.data(), raw_args.size() - (arg.data() - raw_args.data())}; + } }; struct Command { @@ -83,17 +86,18 @@ struct Channel { SUID server; std::string name; ModeSet mode; + time_t timeStamp = 0; std::string topic; - std::vector members; + std::vector members; void parse_sjoin(const Event &event, Cache& cache, NetworkInfo& netInfo); - void removeMember(const u_User& member); + void removeMember(const User *member); - Event get_sjoin(const UUID& initial_user) const { + Event get_sjoin(const UUID& initial_user, const SUID& my_sid) const { return { - .sender = SUID(server), + .sender = my_sid, .name = "SJOIN", - .raw_args = fmt::format("{} {} +{}", time(nullptr), name, mode.str), + .raw_args = fmt::format("{} {} +", timeStamp?timeStamp:time(nullptr), name), .text = fmt::format("@{}", initial_user.str()) }; } @@ -105,11 +109,11 @@ struct Channel { .text = std::string(topic) }; } - Event get_tmode(const ModeSet& modes, const UUID& user) const { + Event get_tmode(const ModeSet& modes, const AnyUID& sender) const { return { - .sender = user, + .sender = AnyUID(sender), .name = "TMODE", - .raw_args = fmt::format("{} {} {}", time(nullptr), name, modes.fullStr()) + .raw_args = fmt::format("{} {} +{}", timeStamp?timeStamp:time(nullptr), name, modes.fullStr()) }; } }; @@ -125,11 +129,11 @@ struct User { std::string realhost; UUID uid; std::string realname; - std::vector channels; + std::vector channels; std::optional loginName; void parse_euid(const Event &event, NetworkInfo& netInfo); - void removeChannel(const u_Channel &channel); + void removeChannel(const Channel *channel); Event get_euid() const { return { @@ -200,12 +204,12 @@ struct User { }; struct Cache { - std::vector users; - std::vector channels; + std::vector> users; + std::vector> channels; - std::vector::iterator find_user_by_nick(std::string_view nick); - std::vector::iterator find_user_by_uid(const UUID& uid); - std::vector::iterator find_channel_by_name(std::string_view name); + std::vector>::iterator find_user_by_nick(std::string_view nick); + std::vector>::iterator find_user_by_uid(const UUID& uid); + std::vector>::iterator find_channel_by_name(std::string_view name); }; struct NetworkInfo { diff --git a/main.cpp b/main.cpp index 831f2aa..d9c43a4 100644 --- a/main.cpp +++ b/main.cpp @@ -19,6 +19,7 @@ #include "instance.hpp" #include "services/nickserv.hpp" +#include "services/chanserv.hpp" #include #include @@ -34,6 +35,7 @@ int main() { Instance instance(service, parseConfig("config.inil")); instance.services.push_back(std::make_unique()); + instance.services.push_back(std::make_unique()); async::detach(instance.run()); async::run_forever(rq.run_token(), loop_service_wrapper{service}); diff --git a/serviceBase.cpp b/serviceBase.cpp index 1f28c15..7c3e8bb 100644 --- a/serviceBase.cpp +++ b/serviceBase.cpp @@ -50,7 +50,7 @@ std::optional ServiceBase::getConfig(const std::string& sectio return {k_res->second}; } -async::result ServiceBase::on_direct_privmsg(std::string_view msg, u_User& author) { +async::result ServiceBase::on_direct_privmsg(std::string_view msg, User *author) { auto split = Utility::strSplit(msg, ' '); User user = {.server = i->config.server.uid, .uid = uuid}; diff --git a/serviceBase.hpp b/serviceBase.hpp index 53a095c..7252b7d 100644 --- a/serviceBase.hpp +++ b/serviceBase.hpp @@ -38,11 +38,11 @@ public: UUID uuid; Instance *i; bool ready = false; - std::unordered_map (u_User& sender, const std::vector& args)>, std::string_view, size_t, bool>> commands; + std::unordered_map (User *sender, const std::vector& args)>, std::string_view, size_t, bool>> commands; virtual async::result intitialize() = 0; virtual async::result on_event(const Event& event); - virtual async::result on_direct_privmsg(std::string_view msg, u_User& author); + virtual async::result on_direct_privmsg(std::string_view msg, User *author); async::result mark_ready(const User& user); std::optional getConfig(const std::string& section, const std::string& key); diff --git a/services/chanserv.cpp b/services/chanserv.cpp index 98f883d..9d71204 100644 --- a/services/chanserv.cpp +++ b/services/chanserv.cpp @@ -38,7 +38,8 @@ async::result ChanServ::intitialize() { .uid = uuid, .realname = i->netInfo.name }; - co_await mark_ready(user); + + opMe.parse("+o {}"_format(user.uid.str()), i->netInfo); using namespace sqlite_orm; storage = std::make_unique(make_storage( @@ -51,16 +52,16 @@ async::result ChanServ::intitialize() { storage->sync_schema(); commands = { - {"register", {[this] (u_User& sender, std::vector args) -> async::result { + {"register", {[this] (User *sender, std::vector args) -> async::result { // Get channel - u_Channel *channel; + Channel *channel; { auto res = i->cache.find_channel_by_name(args[0]); if (res == i->cache.channels.end()) { co_await i->send_event(user.get_notice("Error: {} does not exist!"_format(args[0]), sender->uid)); co_return; } - channel = &*res; + channel = res->get(); } // Check if user is operator in the channel // TODO @@ -68,7 +69,7 @@ async::result ChanServ::intitialize() { { auto channelregs_found = storage->get_all(where(c(&ChannelReg::name) == std::string(args[0]))); if (!channelregs_found.empty()) { - co_await i->send_event(user.get_notice("Error: {} has already been registered!"_format(sender->nick), sender->uid)); + co_await i->send_event(user.get_notice("Error: {} has already been registered!"_format(args[0]), sender->uid)); co_return; } } @@ -85,17 +86,21 @@ async::result ChanServ::intitialize() { }, "", 1, true} }, }; + + co_await mark_ready(user); } -async::result ChanServ::initializeChannel(u_Channel *channel, const ChannelReg& channelReg) { - // We need to loop twice because co_await'ing inside a loop through a non-local dynamically-sized container is DANGEROUS - std::vector to_mark_as_admin; - for (const auto member : channel->get()->members) { - if (member->get()->loginName.value_or("") == channelReg.owner) { - to_mark_as_admin.push_back(member); +async::result ChanServ::initializeChannel(Channel *channel, const ChannelReg& channelReg) { + // Get all owners nicks mode +o + ModeSet initialModes; + for (const auto& member : channel->members) { + if (member->loginName.has_value() && member->loginName.value() == channelReg.owner) { + initialModes.parse("+o {}"_format(member->uid.str()), i->netInfo); } } - for (const auto member : to_mark_as_admin) { - co_await i->send_event(channel->get()->get_tmode({"o", {std::tuple{'o', member->get()->uid.str()}}}, member->get()->uid)); - } + // Join the channel + auto je = channel->get_sjoin(user.uid, i->config.server.uid); // Needs to be done seperately to avoid some... super weird... crash + co_await i->send_event(je); + // Grant owners nicks OP + co_await i->send_event(channel->get_tmode(initialModes, user.uid)); } diff --git a/services/chanserv.hpp b/services/chanserv.hpp index 1ea73df..bb47a34 100644 --- a/services/chanserv.hpp +++ b/services/chanserv.hpp @@ -15,8 +15,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _TEST_HPP -#define _TEST_HPP +#ifndef _CHANSERV_HPP +#define _CHANSERV_HPP #include "../instance.hpp" #include "../serviceBase.hpp" @@ -32,14 +32,15 @@ struct ChannelReg { std::string name, owner; }; -using Storage = sqlite_orm::internal::storage_t >, sqlite_orm::internal::column_t, std::allocator >, const std::__cxx11::basic_string, std::allocator >& (ChannelReg::*)() const, void (ChannelReg::*)(std::__cxx11::basic_string, std::allocator >)>, sqlite_orm::internal::column_t, std::allocator >, const std::__cxx11::basic_string, std::allocator >& (ChannelReg::*)() const, void (ChannelReg::*)(std::__cxx11::basic_string, std::allocator >)> > >; - class ChanServ : public ServiceBase { + using Storage = sqlite_orm::internal::storage_t >, sqlite_orm::internal::column_t, std::allocator >, const std::__cxx11::basic_string, std::allocator >& (ChannelReg::*)() const, void (ChannelReg::*)(std::__cxx11::basic_string, std::allocator >)>, sqlite_orm::internal::column_t, std::allocator >, const std::__cxx11::basic_string, std::allocator >& (ChannelReg::*)() const, void (ChannelReg::*)(std::__cxx11::basic_string, std::allocator >)> > >; + std::unique_ptr storage; User user; + ModeSet opMe; virtual async::result intitialize() override; - async::result initializeChannel(u_Channel *channel, const ChannelReg& channelReg); + async::result initializeChannel(Channel *channel, const ChannelReg& channelReg); }; #endif diff --git a/services/nickserv.cpp b/services/nickserv.cpp index 256704e..89a2c4e 100644 --- a/services/nickserv.cpp +++ b/services/nickserv.cpp @@ -55,7 +55,7 @@ async::result NickServ::intitialize() { storage->sync_schema(); commands = { - {"identify", {[this] (u_User& sender, const std::vector& args) -> async::result { + {"identify", {[this] (User *sender, const std::vector& args) -> async::result { // Check that user is not already identified if (sender->loginName.has_value()) { co_await i->send_event(user.get_notice("Error: You've already been identified!", sender->uid)); @@ -88,12 +88,12 @@ async::result NickServ::intitialize() { co_await i->send_event(user.get_notice("You have been identified!", sender->uid)); }, "[username] ", 1, false} }, - {"deidentify", {[this] (u_User& sender, const std::vector& args) -> async::result { + {"deidentify", {[this] (User *sender, const std::vector& args) -> async::result { co_await i->send_event(sender->get_encap_su("", i->config.server.uid)); co_await i->send_event(user.get_notice("You have been deidentified!", sender->uid)); }, "", 0, true} }, - {"register", {[this] (u_User& sender, std::vector args) -> async::result { + {"register", {[this] (User *sender, std::vector args) -> async::result { // Check args if (args.size() != 2) { co_await i->send_event(user.get_notice("Error: Passwords may not contain spaces!", sender->uid)); diff --git a/services/nickserv.hpp b/services/nickserv.hpp index bbce1c5..3e9b754 100644 --- a/services/nickserv.hpp +++ b/services/nickserv.hpp @@ -15,8 +15,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef _TEST_HPP -#define _TEST_HPP +#ifndef _NICKSERV_HPP +#define _NICKSERV_HPP #include "../instance.hpp" #include "../serviceBase.hpp" @@ -32,9 +32,9 @@ struct Account { std::string name, email, password_hash; }; -using Storage = sqlite_orm::internal::storage_t >, sqlite_orm::internal::column_t, std::allocator >, const std::__cxx11::basic_string, std::allocator >& (Account::*)() const, void (Account::*)(std::__cxx11::basic_string, std::allocator >)>, sqlite_orm::internal::column_t, std::allocator >, const std::__cxx11::basic_string, std::allocator >& (Account::*)() const, void (Account::*)(std::__cxx11::basic_string, std::allocator >)>, sqlite_orm::internal::column_t, std::allocator >, const std::__cxx11::basic_string, std::allocator >& (Account::*)() const, void (Account::*)(std::__cxx11::basic_string, std::allocator >)> > >; - class NickServ : public ServiceBase { + using Storage = sqlite_orm::internal::storage_t >, sqlite_orm::internal::column_t, std::allocator >, const std::__cxx11::basic_string, std::allocator >& (Account::*)() const, void (Account::*)(std::__cxx11::basic_string, std::allocator >)>, sqlite_orm::internal::column_t, std::allocator >, const std::__cxx11::basic_string, std::allocator >& (Account::*)() const, void (Account::*)(std::__cxx11::basic_string, std::allocator >)>, sqlite_orm::internal::column_t, std::allocator >, const std::__cxx11::basic_string, std::allocator >& (Account::*)() const, void (Account::*)(std::__cxx11::basic_string, std::allocator >)> > >; + std::unique_ptr storage; User user;