diff options
author | Sadie Powell <sadie@witchery.services> | 2021-05-25 22:06:50 +0100 |
---|---|---|
committer | Sadie Powell <sadie@witchery.services> | 2024-03-07 18:50:36 +0000 |
commit | 12214bee7228f884918dacff56108604e10bdc46 (patch) | |
tree | fa977d680247d58a01f0664989c429be928eb55c | |
parent | 08a35231ef00ee9dd3af80c186a78b740cd8185a (diff) |
Add the initial version of the Atheme database importer.
-rw-r--r-- | data/anope.example.conf | 16 | ||||
-rw-r--r-- | include/anope.h | 3 | ||||
-rw-r--r-- | include/modules/hs_request.h | 23 | ||||
-rw-r--r-- | include/modules/info.h | 49 | ||||
-rw-r--r-- | modules/database/db_atheme.cpp | 1426 | ||||
-rw-r--r-- | modules/encryption/enc_sha256.cpp | 6 | ||||
-rw-r--r-- | modules/hostserv/hs_request.cpp | 40 | ||||
-rw-r--r-- | modules/operserv/os_info.cpp | 47 |
8 files changed, 1564 insertions, 46 deletions
diff --git a/data/anope.example.conf b/data/anope.example.conf index 86c124e66..f7a55121f 100644 --- a/data/anope.example.conf +++ b/data/anope.example.conf @@ -1106,6 +1106,22 @@ mail } /* + * db_atheme + * + * This allows importing databases from Atheme. You should load another database module as + * well as this as it can only read Atheme databases not write them. + */ +#module +{ + name = "db_atheme" + + /* + * The database name db_atheme should use. + */ + database = "atheme.db" +} + +/* * [RECOMMENDED] db_flatfile * * This is the default flatfile database format. diff --git a/include/anope.h b/include/anope.h index 1a01db189..bafd862e7 100644 --- a/include/anope.h +++ b/include/anope.h @@ -590,6 +590,9 @@ public: */ sepstream(const Anope::string &source, char separator, bool allowempty = false); + /** Retrieves the underlying string. */ + const auto &GetString() const { return tokens; } + /** Fetch the next token from the stream * @param token The next token from the stream is placed here * @return True if tokens still remain, false if there are none left diff --git a/include/modules/hs_request.h b/include/modules/hs_request.h new file mode 100644 index 000000000..43977c01d --- /dev/null +++ b/include/modules/hs_request.h @@ -0,0 +1,23 @@ +/* + * + * (C) 2003-2024 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for further details. + */ + +#pragma once + +class HostRequest +{ +protected: + HostRequest() = default; + +public: + Anope::string nick; + Anope::string ident; + Anope::string host; + time_t time = 0; + + virtual ~HostRequest() = default; +}; diff --git a/include/modules/info.h b/include/modules/info.h new file mode 100644 index 000000000..920aacabe --- /dev/null +++ b/include/modules/info.h @@ -0,0 +1,49 @@ +/* + * + * (C) 2003-2024 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for further details. + */ + +#pragma once + +class OperInfo +{ +protected: + OperInfo() = default; + + OperInfo(const Anope::string &t, const Anope::string &i, const Anope::string &a, time_t c) + : target(t) + , info(i) + , adder(a) + , created(c) + { + } + +public: + Anope::string target; + Anope::string info; + Anope::string adder; + time_t created = 0; + + virtual ~OperInfo() = default; +}; + +class OperInfoList + : public Serialize::Checker<std::vector<OperInfo *>> +{ +public: + OperInfoList() + : Serialize::Checker<std::vector<OperInfo *>>("OperInfo") + { + } + + virtual ~OperInfoList() + { + for (auto *info : *(*this)) + delete info; + } + + virtual OperInfo *Create() = 0; +}; diff --git a/modules/database/db_atheme.cpp b/modules/database/db_atheme.cpp new file mode 100644 index 000000000..d3877b625 --- /dev/null +++ b/modules/database/db_atheme.cpp @@ -0,0 +1,1426 @@ +/* + * + * (C) 2003-2024 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for further details. + * + * Based on the original code of Epona by Lara. + * Based on the original code of Services by Andy Church. + */ + +#include <functional> + +#include "module.h" +#include "modules/bs_badwords.h" +#include "modules/bs_kick.h" +#include "modules/cs_entrymsg.h" +#include "modules/cs_mode.h" +#include "modules/hs_request.h" +#include "modules/info.h" +#include "modules/ns_cert.h" +#include "modules/os_forbid.h" +#include "modules/suspend.h" + +// Handles reading from an Atheme database row. +class AthemeRow final +{ +private: + // The number of failed reads. + unsigned error = 0; + + // The underlying token stream. + spacesepstream stream; + +public: + AthemeRow(const Anope::string &str) + : stream(str) + { + } + + operator bool() const + { + return !error; + } + + // Retrieves the next parameter. + Anope::string Get() + { + Anope::string token; + if (!stream.GetToken(token)) + error++; + return token; + } + + // Retrieves the next parameter as a number. + template<typename Numeric> + std::enable_if_t<std::is_arithmetic_v<Numeric>, Numeric> GetNum() + { + try + { + auto token = Get(); + std::stringstream stream(token.str()); + Numeric ntoken = 0; + stream >> ntoken; + return ntoken; + } + catch (const ConvertException &) { } + return 0; + } + + // Retrieves the entire row. + Anope::string GetRow() + { + return stream.GetString(); + } + + // Retrieves the remaining data in the row. + Anope::string GetRemaining() + { + auto remaining = stream.GetRemaining(); + if (remaining.empty()) + error++; + return remaining; + } + + bool LogError(Module *mod) + { + Log(mod) << "Malformed database line (expected " << error << " fields): " << GetRow(); + return false; + } +}; + +struct ModeData final +{ + Anope::string name; + Anope::string value; + bool set; + + ModeData(const Anope::string &n, bool s, const Anope::string &v = "") + : name(n) + , value(v) + , set(s) + { + } +}; + +struct ChannelData final +{ + Anope::string bot; + Anope::string info_adder; + Anope::string info_message; + time_t info_ts = 0; + std::vector<ModeData> mlocks; + Anope::string suspend_by; + Anope::string suspend_reason; + time_t suspend_ts = 0; +}; + +struct UserData final +{ + bool kill = false; + Anope::string info_adder; + Anope::string info_message; + time_t info_ts = 0; + Anope::string last_mask; + Anope::string last_quit; + Anope::string last_real_mask; + bool noexpire = false; + Anope::string suspend_by; + Anope::string suspend_reason; + time_t suspend_ts = 0; + Anope::string vhost; + Anope::string vhost_creator; + Anope::map<Anope::string> vhost_nick; + time_t vhost_ts = 0; +}; + +class DBAtheme final + : public Module +{ +private: + ServiceReference<AccessProvider> accessprov; + PrimitiveExtensibleItem<ChannelData> chandata; + std::map<Anope::string, char> flags; + ServiceReference<ForbidService> forbidsvc; + PrimitiveExtensibleItem<UserData> userdata; + ServiceReference<XLineManager> sglinemgr; + ServiceReference<XLineManager> snlinemgr; + ServiceReference<XLineManager> sqlinemgr; + + Anope::map<std::function<bool(DBAtheme*,AthemeRow&)>> rowhandlers = { + { "AC", &DBAtheme::HandleIgnore }, + { "AR", &DBAtheme::HandleIgnore }, + { "BE", &DBAtheme::HandleBE }, + { "BLE", &DBAtheme::HandleIgnore }, + { "BOT", &DBAtheme::HandleBOT }, + { "BOT-COUNT", &DBAtheme::HandleIgnore }, + { "BW", &DBAtheme::HandleBW }, + { "CA", &DBAtheme::HandleCA }, + { "CF", &DBAtheme::HandleIgnore }, + { "CFCHAN", &DBAtheme::HandleIgnore }, + { "CFDBV", &DBAtheme::HandleIgnore }, + { "CFMD", &DBAtheme::HandleIgnore }, + { "CFOP", &DBAtheme::HandleIgnore }, + { "CLONES-CD", &DBAtheme::HandleIgnore }, + { "CLONES-CK", &DBAtheme::HandleIgnore }, + { "CLONES-DBV", &DBAtheme::HandleIgnore }, + { "CLONES-EX", &DBAtheme::HandleIgnore }, + { "CLONES-GR", &DBAtheme::HandleIgnore }, + { "CSREQ", &DBAtheme::HandleIgnore }, + { "CSREQ", &DBAtheme::HandleIgnore }, + { "DBV", &DBAtheme::HandleDBV }, + { "GACL", &DBAtheme::HandleIgnore }, + { "GDBV", &DBAtheme::HandleIgnore }, + { "GE", &DBAtheme::HandleIgnore }, + { "GFA", &DBAtheme::HandleIgnore }, + { "GRP", &DBAtheme::HandleIgnore }, + { "GRVER", &DBAtheme::HandleGRVER }, + { "HE", &DBAtheme::HandleIgnore }, + { "HO", &DBAtheme::HandleIgnore }, + { "HR", &DBAtheme::HandleHR }, + { "JM", &DBAtheme::HandleIgnore }, + { "KID", &DBAtheme::HandleIgnore }, + { "KL", &DBAtheme::HandleKL }, + { "LUID", &DBAtheme::HandleIgnore }, + { "MC", &DBAtheme::HandleMC }, + { "MCFP", &DBAtheme::HandleMCFP }, + { "MDA", &DBAtheme::HandleMDA }, + { "MDC", &DBAtheme::HandleMDC }, + { "MDEP", &DBAtheme::HandleIgnore }, + { "MDG", &DBAtheme::HandleIgnore }, + { "MDN", &DBAtheme::HandleMDN }, + { "MDU", &DBAtheme::HandleMDU }, + { "ME", &DBAtheme::HandleME }, + { "MI", &DBAtheme::HandleMI }, + { "MM", &DBAtheme::HandleMM }, + { "MN", &DBAtheme::HandleMN }, + { "MU", &DBAtheme::HandleMU }, + { "NAM", &DBAtheme::HandleNAM }, + { "QID", &DBAtheme::HandleIgnore }, + { "QL", &DBAtheme::HandleQL }, + { "RM", &DBAtheme::HandleIgnore }, + { "RR", &DBAtheme::HandleIgnore }, + { "RW", &DBAtheme::HandleIgnore }, + { "SI", &DBAtheme::HandleIgnore }, + { "SO", &DBAtheme::HandleIgnore }, + { "TS", &DBAtheme::HandleIgnore }, + { "XID", &DBAtheme::HandleIgnore }, + { "XL", &DBAtheme::HandleXL }, + }; + + void ApplyAccess(Anope::string &in, char flag, Anope::string &out, std::initializer_list<const char*> privs) + { + for (const auto *priv : privs) + { + auto pos = in.find(flag); + if (pos != Anope::string::npos) + { + auto privchar = flags.find(priv); + if (privchar != flags.end()) + { + out.push_back(privchar->second); + in.erase(pos, 1); + } + } + } + } + + void ApplyFlags(Extensible *ext, Anope::string &flags, char flag, const char* extname, bool extend = true) + { + auto pos = flags.find(flag); + auto has_flag = (pos != Anope::string::npos); + + if (has_flag == extend) + ext->Extend<bool>(extname); + else + ext->Shrink<bool>(extname); + + if (has_flag) + flags.erase(pos, 1); + } + + void ApplyLocks(ChannelInfo *ci, unsigned locks, const Anope::string &limit, const Anope::string &key, bool set) + { + auto *data = chandata.Require(ci); + + // Start off with the standard mode values. + if (locks & 0x1u) + data->mlocks.emplace_back("INVITE", set); + if (locks & 0x2u) + data->mlocks.emplace_back("KEY", set, key); + if (locks & 0x4u) + data->mlocks.emplace_back("LIMIT", set, limit); + if (locks & 0x8u) + data->mlocks.emplace_back("MODERATED", set); + if (locks & 0x10u) + data->mlocks.emplace_back("NOEXTERNAL", set); + if (locks & 0x40u) + data->mlocks.emplace_back("PRIVATE", set); + if (locks & 0x80u) + data->mlocks.emplace_back("SECRET", set); + if (locks & 0x100u) + data->mlocks.emplace_back("TOPIC", set); + if (locks & 0x200u) + data->mlocks.emplace_back("REGISTERED", set); + + // Atheme also supports per-ircd values here (ew). + if (IRCD->owner->name == "bahamut") + { + if (locks & 0x1000u) + data->mlocks.emplace_back("BLOCKCOLOR", set); + if (locks & 0x2000u) + data->mlocks.emplace_back("REGMODERATED", set); + if (locks & 0x4000u) + data->mlocks.emplace_back("REGISTEREDONLY", set); + if (locks & 0x8000u) + data->mlocks.emplace_back("OPERONLY", set); + + // Anope doesn't recognise the following Bahamut modes currently: + // - 0x10000u ('A') + // - 0x20000u ('P') + } + else if (IRCD->owner->name == "inspircd") + { + if (locks & 0x1000u) + data->mlocks.emplace_back("BLOCKCOLOR", set); + if (locks & 0x2000u) + data->mlocks.emplace_back("REGMODERATED", set); + if (locks & 0x4000u) + data->mlocks.emplace_back("REGISTEREDONLY", set); + if (locks & 0x8000u) + data->mlocks.emplace_back("OPERONLY", set); + if (locks & 0x10000u) + data->mlocks.emplace_back("NONOTICE", set); + if (locks & 0x20000u) + data->mlocks.emplace_back("NOKICK", set); + if (locks & 0x40000u) + data->mlocks.emplace_back("STRIPCOLOR", set); + if (locks & 0x80000u) + data->mlocks.emplace_back("NOKNOCK", set); + if (locks & 0x100000u) + data->mlocks.emplace_back("ALLINVITE", set); + if (locks & 0x200000u) + data->mlocks.emplace_back("NOCTCP", set); + if (locks & 0x400000u) + data->mlocks.emplace_back("AUDITORIUM", set); + if (locks & 0x800000u) + data->mlocks.emplace_back("SSL", set); + if (locks & 0x100000u) + data->mlocks.emplace_back("NONICK", set); + if (locks & 0x200000u) + data->mlocks.emplace_back("CENSOR", set); + if (locks & 0x400000u) + data->mlocks.emplace_back("BLOCKCAPS", set); + if (locks & 0x800000u) + data->mlocks.emplace_back("PERM", set); + if (locks & 0x2000000u) + data->mlocks.emplace_back("DELAYEDJOIN", set); + } + else if (IRCD->owner->name == "ngircd") + { + if (locks & 0x1000u) + data->mlocks.emplace_back("REGISTEREDONLY", set); + if (locks & 0x2000u) + data->mlocks.emplace_back("OPERONLY", set); + if (locks & 0x4000u) + data->mlocks.emplace_back("PERM", set); + if (locks & 0x8000u) + data->mlocks.emplace_back("SSL", set); + } + else if (IRCD->owner->name == "solanum") + { + if (locks & 0x1000u) + data->mlocks.emplace_back("BLOCKCOLOR", set); + if (locks & 0x2000u) + data->mlocks.emplace_back("REGISTEREDONLY", set); + if (locks & 0x4000u) + data->mlocks.emplace_back("OPMODERATED", set); + if (locks & 0x8000u) + data->mlocks.emplace_back("ALLINVITE", set); + if (locks & 0x10000u) + data->mlocks.emplace_back("LBAN", set); + if (locks & 0x20000u) + data->mlocks.emplace_back("PERM", set); + if (locks & 0x40000u) + data->mlocks.emplace_back("ALLOWFORWARD", set); + if (locks & 0x80000u) + data->mlocks.emplace_back("NOFORWARD", set); + if (locks & 0x100000u) + data->mlocks.emplace_back("NOCTCP", set); + if (locks & 0x400000u) + data->mlocks.emplace_back("SSL", set); + if (locks & 0x800000u) + data->mlocks.emplace_back("OPERONLY", set); + if (locks & 0x1000000u) + data->mlocks.emplace_back("ADMINONLY", set); + if (locks & 0x2000000u) + data->mlocks.emplace_back("NONOTICE", set); + if (locks & 0x4000000u) + data->mlocks.emplace_back("PROTECTED", set); + if (locks & 0x8000000) + data->mlocks.emplace_back("NOFILTER", set); + if (locks & 0x10000000U) + data->mlocks.emplace_back("REGMODERATED", set); + } + else if (IRCD->owner->name == "unrealircd") + { + if (locks & 0x1000u) + data->mlocks.emplace_back("BLOCKCOLOR", set); + if (locks & 0x2000u) + data->mlocks.emplace_back("REGMODERATED", set); + if (locks & 0x4000u) + data->mlocks.emplace_back("REGISTEREDONLY", set); + if (locks & 0x8000u) + data->mlocks.emplace_back("OPERONLY", set); + if (locks & 0x20000u) + data->mlocks.emplace_back("NOKICK", set); + if (locks & 0x40000u) + data->mlocks.emplace_back("STRIPCOLOR", set); + if (locks & 0x80000u) + data->mlocks.emplace_back("NOKNOCK", set); + if (locks & 0x100000u) + data->mlocks.emplace_back("NOINVITE", set); + if (locks & 0x200000u) + data->mlocks.emplace_back("NOCTCP", set); + if (locks & 0x800000u) + data->mlocks.emplace_back("SSL", set); + if (locks & 0x1000000u) + data->mlocks.emplace_back("NONICK", set); + if (locks & 0x4000000u) + data->mlocks.emplace_back("CENSOR", set); + if (locks & 0x8000000u) + data->mlocks.emplace_back("PERM", set); + if (locks & 0x1000000u) + data->mlocks.emplace_back("NONOTICE", set); + if (locks & 0x2000000u) + data->mlocks.emplace_back("DELAYJOIN", set); + } + else if (IRCD->owner->name != "ratbox") + { + Log(this) << "Unable to import mode locks for " << IRCD->GetProtocolName(); + } + } + + void ApplyPassword(NickCore *nc, Anope::string &flags, const Anope::string &pass) + { + auto pos = flags.find('C'); + if (pos == Anope::string::npos) + { + // Password is unencrypted so we can use it. + Anope::Encrypt(pass, nc->pass); + return; + } + + // Atheme supports several password hashing methods. We can only import + // some of them currently. + // + // anope-enc-sha256 Converted to enc_sha256 + // argon2 NO + // base64 Converted to the first encryption algorithm + // bcrypt Converted to enc_bcrypt + // crypt3-des NO + // crypt3-md5 NO + // crypt3-sha2-256 NO + // crypt3-sha2-512 NO + // ircservices Converted to enc_old + // pbkdf2 NO + // pbkdf2v2 NO + // rawmd5 Converted to enc_md5 + // rawsha1 Converted to enc_sha1 + // rawsha2-256 Converted to enc_sha256 + // rawsha2-512 NO + // scrypt NO + if (pass.compare(0, 18, "$anope$enc_sha256$", 18) == 0) + { + auto sep = pass.find('$', 18); + Anope::string iv, pass; + Anope::B64Decode(pass.substr(18, sep - 18), iv); + Anope::B64Decode(pass.substr(sep + 1), pass); + nc->pass = "sha256:" + Anope::Hex(pass) + ":" + Anope::Hex(iv); + } + + else if (pass.compare(0, 8, "$base64$", 8) == 0) + { + Anope::string rawpass; + Anope::B64Decode(pass.substr(8), rawpass); + Anope::Encrypt(rawpass, nc->pass); + } + + else if (pass.compare(0, 13, "$ircservices$", 13) == 0) + nc->pass = "oldmd5:" + pass.substr(13); + + else if (pass.compare(0, 8, "$rawmd5$", 8) == 0) + nc->pass = "md5:" + pass.substr(8); + + else if (pass.compare(0, 9, "$rawsha1$", 9) == 0) + nc->pass = "sha1:" + pass.substr(9); + + else if (pass.compare(0, 11, "$rawsha256$", 11) == 0) + nc->pass = "sha256:" + pass.substr(11) + ":6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19"; + + else if (pass.compare(0, 4, "$2a$", 4) == 0 || pass.compare(0, 4, "$2b$", 4) == 0) + nc->pass = "bcrypt:" + pass; + + else + { + // Generate a new password as we can't use the old one. + auto maxpasslen = Config->GetModule("nickserv")->Get<unsigned>("maxpasslen", "50"); + Anope::Encrypt(Anope::Random(maxpasslen), nc->pass); + Log(this) << "Unable to convert the password for " << nc->display << " as Anope does not support the format!"; + } + + } + + bool HandleBE(AthemeRow &row) + { + // BE <email <created> <creator> <reason> + auto email = row.Get(); + auto created = row.GetNum<time_t>(); + auto creator = row.Get(); + auto reason = row.GetRemaining(); + + if (!row) + return row.LogError(this); + + if (!forbidsvc) + { + Log(this) << "Unable to convert forbidden email " << email << " as os_forbid is not loaded"; + return true; + } + + auto *forbid = forbidsvc->CreateForbid(); + forbid->created = created; + forbid->creator = creator; + forbid->mask = email; + forbid->reason = reason; + forbid->type = FT_EMAIL; + forbidsvc->AddForbid(forbid); + return true; + } + + bool HandleBOT(AthemeRow &row) + { + // BOT <nick> <user> <host> <operonly> <created> <real> + auto nick = row.Get(); + auto user = row.Get(); + auto host = row.Get(); + auto operonly = row.GetNum<unsigned>(); + auto created = row.GetNum<time_t>(); + auto real = row.GetRemaining(); + + if (!row) + return row.LogError(this); + + auto *bi = new BotInfo(nick, user, host, real); + bi->oper_only = operonly; + bi->created = created; + return true; + } + + bool HandleBW(AthemeRow &row) + { + // BW <badword> <added> <creator> <channel> <action> + auto badword = row.Get(); + /* auto added = */ row.GetNum<time_t>(); + /* auto creator = */ row.Get(); + auto channel = row.Get(); + /* auto action = */ row.Get(); + + if (!row) + return row.LogError(this); + + auto *ci = ChannelInfo::Find(channel); + if (!ci) + { + Log(this) << "Missing ChannelInfo for BW: " << channel; + return false; + } + + auto *bw = ci->Require<BadWords>("badwords"); + if (!bw) + { + Log(this) << "Unable to import badwords for " << ci->name << " as bs_kick is not loaded"; + return true; + } + + auto *kd = ci->Require<KickerData>("kickerdata"); + if (kd) + { + kd->badwords = true; + kd->ttb[TTB_BADWORDS] = 0; + } + + bw->AddBadWord(badword, BW_ANY); + return true; + } + + bool HandleCA(AthemeRow &row) + { + // CA <channel> <account/mask> <flags> <modifiedtime> <setter> + auto channel = row.Get(); + auto mask = row.Get(); + auto flags = row.Get(); + auto modifiedtime = row.GetNum<time_t>(); + auto setter = row.Get(); + + if (!row) + return row.LogError(this); + + auto *ci = ChannelInfo::Find(channel); + if (!ci) + { + Log(this) << "Missing ChannelInfo for CA: " << channel; + return false; + } + + auto *nc = NickCore::Find(mask); + if (flags.find('b') != Anope::string::npos) + { + if (nc) + ci->AddAkick(setter, nc, "", modifiedtime, modifiedtime); + else + ci->AddAkick(setter, mask, "", modifiedtime, modifiedtime); + return true; + } + + if (!accessprov) + { + Log(this) << "Unable to import channel access for " << ci->name << " as cs_flags is not loaded"; + return true; + } + + Anope::string accessflags; + ApplyAccess(flags, 'A', accessflags, { "ACCESS_LIST" }); + ApplyAccess(flags, 'a', accessflags, { "AUTOPROTECT", "PROTECT", "PROTECTME" }); + ApplyAccess(flags, 'e', accessflags, { "GETKEY", "NOKICK", "UNBANME" }); + ApplyAccess(flags, 'f', accessflags, { "ACCESS_CHANGE" }); + ApplyAccess(flags, 'F', accessflags, { "FOUNDER" }); + ApplyAccess(flags, 'H', accessflags, { "AUTOHALFOP" }); + ApplyAccess(flags, 'h', accessflags, { "HALFOP", "HALFOPME" }); + ApplyAccess(flags, 'i', accessflags, { "INVITE" }); + ApplyAccess(flags, 'O', accessflags, { "AUTOOP" }); + ApplyAccess(flags, 'o', accessflags, { "OP", "OPME" }); + ApplyAccess(flags, 'q', accessflags, { "AUTOOWNER", "OWNER", "OWNERME" }); + ApplyAccess(flags, 'r', accessflags, { "KICK" }); + ApplyAccess(flags, 's', accessflags, { "SET" }); + ApplyAccess(flags, 't', accessflags, { "TOPIC" }); + ApplyAccess(flags, 'V', accessflags, { "AUTOVOICE" }); + ApplyAccess(flags, 'v', accessflags, { "VOICE", "VOICEME" }); + if (!accessflags.empty()) + { + auto *access = accessprov->Create(); + access->SetMask(mask, ci); + access->creator = setter; + access->description = "Imported from Atheme"; + access->last_seen = modifiedtime; + access->created = modifiedtime; + access->AccessUnserialize(accessflags); + ci->AddAccess(access); + } + + if (flags != "+") + Log(this) << "Unable to convert channel access flags " << flags << " for " << ci->name; + + return true; + } + + bool HandleDBV(AthemeRow &row) + { + // DBV <version> + unsigned version = row.GetNum<unsigned>(); + if (version != 12) + { + Log(this) << "Database is version " << version << " which is not supported!"; + return false; + } + return true; + } + + bool HandleGRVER(AthemeRow &row) + { + // GRVER <version> + unsigned version = row.GetNum<unsigned>(); + if (version != 1) + { + Log(this) << "Database grammar is version " << version << " which is not supported!"; + return false; + } + return true; + } + + bool HandleHR(AthemeRow &row) + { + // HR <nick> <host> <reqts> <creator> + auto nick = row.Get(); + auto host = row.Get(); + auto reqts = row.GetNum<time_t>(); + /* auto creator = */ row.Get(); + + if (!row) + return row.LogError(this); + + auto *na = NickAlias::Find(nick); + if (!na) + { + Log(this) << "Missing NickAlias for HR: " << nick; + return false; + } + + auto *hr = na->Require<HostRequest>("hostrequest"); + if (!hr) + { + Log(this) << "Unable to convert host request for " << na->nick << " as hs_request is not loaded"; + return true; + } + + hr->nick = na->nick; + hr->ident.clear(); + hr->host = host; + hr->time = reqts; + return true; + } + + bool HandleKL(AthemeRow &row) + { + // KL <id> <user> <host> <duration> <settime> <setby> <reason> + /* auto id = */ row.GetNum<unsigned>(); + auto user = row.Get(); + auto host = row.Get(); + auto duration = row.GetNum<unsigned>(); + auto settime = row.GetNum<time_t>(); + auto setby = row.Get(); + auto reason = row.GetRemaining(); + + if (!row) + return row.LogError(this); + + if (!sglinemgr) + { + Log(this) << "Unable to import K-line on " << user << "@" << host << " as operserv is not loaded"; + return true; + } + + auto *xl = new XLine(user + "@" + host, setby, settime + duration, reason); + sglinemgr->AddXLine(xl); + return true; + } + + bool HandleIgnore(AthemeRow &row) + { + Log(LOG_DEBUG_3) << "Intentionally ignoring Atheme database row: " << row.GetRow(); + return true; + } + + bool HandleMC(AthemeRow &row) + { + // MC <channel> <regtime> <used> <flags> <mlock-on> <mlock-off> <mlock-limit> [<mlock-key>] + auto channel = row.Get(); + auto regtime = row.GetNum<time_t>(); + /* auto used = */ row.GetNum<time_t>(); + auto flags = row.Get(); + auto mlock_on = row.GetNum<unsigned>(); + auto mlock_off = row.GetNum<unsigned>(); + auto mlock_limit = row.Get(); + + if (!row) + return row.LogError(this); + + auto mlock_key = row.Get(); // May not exist. + + auto *ci = new ChannelInfo(channel); + ci->time_registered = regtime; + + // No equivalent: elnv + ApplyFlags(ci, flags, 'h', "CS_NO_EXPIRE"); + ApplyFlags(ci, flags, 'k', "KEEPTOPIC"); + ApplyFlags(ci, flags, 'o', "NOAUTOOP"); + ApplyFlags(ci, flags, 'p', "CS_PRIVATE"); + ApplyFlags(ci, flags, 'r', "RESTRICTED"); + ApplyFlags(ci, flags, 't', "TOPICLOCK"); + ApplyFlags(ci, flags, 'z', "SECUREOPS"); + + auto pos = flags.find('a'); + if (pos != Anope::string::npos) + { + ci->SetLevel("ACCESS_CHANGE", 0); + flags.erase(pos, 1); + } + + pos = flags.find('f'); + if (pos != Anope::string::npos) + { + auto *kd = ci->Require<KickerData>("kickerdata"); + if (kd) + { + kd->flood = true; + kd->floodlines = 10; + kd->floodsecs = 60; + kd->ttb[TTB_FLOOD] = 0; + flags.erase(pos, 1); + } + else + { + Log(this) << "Unable to convert the 'f' flag for " << ci->name << " as bs_kick is not loaded"; + } + } + + pos = flags.find('g'); + if (pos != Anope::string::npos) + { + auto *bi = Config->GetClient("ChanServ"); + if (bi) + { + bi->Assign(nullptr, ci); + flags.erase(pos, 1); + } + else + Log(this) << "Unable to convert the 'g' flag for " << ci->name << " as chanserv is not loaded"; + } + + if (flags != "+") + Log(this) << "Unable to convert channel flags " << flags << " for " << ci->name; + + ApplyLocks(ci, mlock_on, mlock_limit, mlock_key, true); + ApplyLocks(ci, mlock_off, mlock_limit, mlock_key, false); + return true; + } + + bool HandleMCFP(AthemeRow &row) + { + // MCFP <display> <fingerprint> + auto display = row.Get(); + auto fingerprint = row.Get(); + + if (!row) + return row.LogError(this); + + auto *nc = NickCore::Find(display); + if (!nc) + { + Log(this) << "Missing NickCore for MCFP: " << display; + return false; + } + + auto *cl = nc->Require<NSCertList>("certificates"); + if (!cl) + { + Log(this) << "Unable to convert certificate for " << nc->display << " as ns_cert is not loaded"; + return true; + } + + cl->AddCert(fingerprint); + return true; + } + + bool HandleMDA(AthemeRow &row) + { + // MDA <channel> <account/mask> <key> <value> + auto display = row.Get(); + auto mask = row.Get(); + auto key = row.Get(); + auto value = row.GetRemaining(); + + if (!row) + return row.LogError(this); + + Log(this) << "Unknown channel access metadata " << key << " = " << value; + return true; + } + + bool HandleMDC(AthemeRow &row) + { + // MDC <channel> <key> <value> + auto channel = row.Get(); + auto key = row.Get(); + auto value = row.GetRemaining(); + + if (!row) + return row.LogError(this); + + auto *ci = ChannelInfo::Find(channel); + if (!ci) + { + Log(this) << "Missing ChannelInfo for MDC: " << channel; + return false; + } + + auto *data = chandata.Require(ci); + if (key == "private:botserv:bot-assigned") + data->bot = value; + else if (key == "private:botserv:bot-handle-fantasy") + ci->Extend<bool>("BS_FANTASY"); + else if (key == "private:botserv:no-bot") + ci->Extend<bool>("BS_NOBOT"); + else if (key == "private:close:closer") + data->suspend_by = value; + else if (key == "private:close:reason") + data->suspend_reason = value; + else if (key == "private:close:timestamp") + data->suspend_ts = convertTo<time_t>(value); + else if (key == "private:entrymsg") + { + auto *eml = ci->Require<EntryMessageList>("entrymsg"); + if (!eml) + { + Log(this) << "Unable to convert entry message for " << ci->name << " as cs_mode is not loaded"; + return true; + } + + auto *msg = eml->Create(); + msg->chan = ci->name; + msg->creator = "Unknown"; + msg->message = value; + msg->when = Anope::CurTime; + (*eml)->push_back(msg); + } + else if (key == "private:klinechan:closer") + data->suspend_by = value; + else if (key == "private:klinechan:reason") + data->suspend_reason = value; + else if (key == "private:klinechan:timestamp") + data->suspend_ts = convertTo<time_t>(value); + else if (key == "private:mark:reason") + data->info_message = value; + else if (key == "private:mark:setter") + data->info_adder = value; + else if (key == "private:mark:timestamp") + data->info_ts = convertTo<time_t>(value); + else if (key == "private:topic:setter") + ci->last_topic_setter = value; + else if (key == "private:topic:text") + ci->last_topic = value; + else if (key == "private:topic:ts") + ci->last_topic_time = convertTo<time_t>(value); + else + Log(this) << "Unknown channel metadata " << key << " = " << value; + + return true; + } + + bool HandleMDN(AthemeRow &row) + { + // MDN <display> <key> <value> + auto display = row.Get(); + auto key = row.Get(); + auto value = row.GetRemaining(); + + if (!row) + return row.LogError(this); + + Log(this) << "Unknown nick metadata " << key << " = " << value; + return true; + } + + bool HandleMDU(AthemeRow &row) + { + // MDU <display> <key> <value> + auto display = row.Get(); + auto key = row.Get(); + auto value = row.GetRemaining(); + + if (!row) + return row.LogError(this); + + auto *nc = NickCore::Find(display); + if (!nc) + { + Log(this) << "Missing NickCore for MDU: " << display; + return false; + } + + auto *data = userdata.Require(nc); + if (key == "private:autojoin") + return true; // TODO + else if (key == "private:doenforce") + data->kill = true; + else if (key == "private:enforcetime") + { + if (!data->kill) + return true; // Don't apply this. + + auto kill = Config->GetModule("nickserv")->Get<time_t>("kill", "60s"); + auto killquick = Config->GetModule("nickserv")->Get<time_t>("killquick", "20s"); + auto secs = convertTo<unsigned>(value); + if (secs >= kill) + nc->Extend<bool>("KILLPROTECT"); + else if (secs >= killquick) + nc->Shrink<bool>("KILL_QUICK"); + else + nc->Shrink<bool>("KILL_IMMED"); + } + else if (key == "private:freeze:freezer") + data->suspend_by = value; + else if (key == "private:freeze:reason") + data->suspend_reason = value; + else if (key == "private:freeze:timestamp") + data->suspend_ts = convertTo<time_t>(value); + else if (key == "private:host:actual") + data->last_real_mask = value; + else if (key == "private:host:vhost") + data->last_mask = value; + else if (key == "private:lastquit:message") + data->last_quit = value; + else if (key == "private:mark:reason") + data->info_message = value; + else if (key == "private:mark:setter") + data->info_adder = value; + else if (key == "private:mark:timestamp") + data->info_ts = convertTo<time_t>(value); + else if (key == "private:usercloak") + data->vhost = value; + else if (key == "private:usercloak-assigner") + data->vhost_creator = value; + else if (key == "private:usercloak-timestamp") + data->vhost_ts = convertTo<time_t>(value); + else if (key.compare(0, 18, "private:usercloak:", 18) == 0) + data->vhost_nick[key.substr(18)] = value; + else + Log(this) << "Unknown account metadata " << key << " = " << value; + + return true; + } + + bool HandleME(AthemeRow &row) + { + // ME <target> <source> <sent> <flags> <text> + auto target = row.Get(); + auto source = row.Get(); + auto sent = row.GetNum<time_t>(); + auto flags = row.GetNum<unsigned>(); + auto text = row.GetRemaining(); + + if (!row) + return row.LogError(this); + + auto *nc = NickCore::Find(target); + if (!nc) + { + Log(this) << "Missing NickCore for ME: " << source; + return false; + } + + auto *m = new Memo(); + m->mi = &nc->memos; + m->owner = nc->display; + m->sender = source; + m->time = sent; + m->text = text; + m->unread = flags & 0x1; + nc->memos.memos->push_back(m); + return true; + } + + bool HandleMI(AthemeRow &row) + { + // MI <display> <ignored> + auto display = row.Get(); + auto ignored = row.Get(); + + if (!row) + return row.LogError(this); + + auto *nc = NickCore::Find(display); + if (!nc) + { + Log(this) << "Missing NickCore for MI: " << display; + return false; + } + + nc->memos.ignores.push_back(ignored); + return true; + } + + bool HandleMM(AthemeRow &row) + { + // MM <id> <setterid> <setteraccount> <markedid> <markedaccount> <setts> <num> <message> + /* auto id = */ row.Get(); + /* auto setterid = */ row.Get(); + auto setteraccount = row.Get(); + /* auto markedid = */ row.Get(); + auto markedaccount = row.Get(); + auto setts = row.GetNum<time_t>(); + /* auto num = */ row.Get(); + auto message = row.GetRemaining(); + + if (!row) + return row.LogError(this); + + auto *nc = NickCore::Find(markedaccount); + if (!nc) + { + Log(this) << "Missing NickCore for MM: " << markedaccount; + return false; + } + + auto *oil = nc->Require<OperInfoList>("operinfo"); + if (oil) + { + auto *info = oil->Create(); + info->target = nc->display; + info->info = message; + info->adder = setteraccount; + info->created = setts; + (*oil)->push_back(info); + } + else + { + Log(this) << "Unable to convert oper info for " << nc->display << " as os_info is not loaded"; + } + return true; + } + + bool HandleMN(AthemeRow &row) + { + // MU <display> <nick> <regtime> <lastseen> + auto display = row.Get(); + auto nick = row.Get(); + auto regtime = row.GetNum<time_t>(); + auto lastseen = row.GetNum<time_t>(); + + if (!row) + return row.LogError(this); + + auto *nc = NickCore::Find(display); + if (!nc) + { + Log(this) << "Missing NickCore for MN: " << display; + return false; + } + + auto *na = new NickAlias(nick, nc); + na->time_registered = regtime; + na->last_seen = lastseen ? regtime : na->time_registered; + + auto *data = userdata.Get(nc); + if (data) + { + if (!data->last_mask.empty()) + na->last_usermask = data->last_mask; + + if (!data->last_quit.empty()) + na->last_quit = data->last_quit; + + if (!data->last_real_mask.empty()) + na->last_realhost = data->last_real_mask; + + if (data->noexpire) + na->Extend<bool>("NS_NO_EXPIRE"); + + auto vhost = data->vhost; + auto nick_vhost = data->vhost_nick.find(nick); + if (nick_vhost != data->vhost_nick.end()) + vhost = nick_vhost->second; + if (!vhost.empty()) + na->SetVhost("", vhost, data->vhost_creator, data->vhost_ts); + } + + return true; + } + + bool HandleMU(AthemeRow &row) + { + // MU <id> <display> <pass> <email> <regtime> <lastlogin> <flags> <language> + /* auto id = */ row.Get(); + auto display = row.Get(); + auto pass = row.Get(); + auto email = row.Get(); + /* auto regtime = */ row.GetNum<time_t>(); + /* auto lastlogin = */ row.Get(); + auto flags = row.Get(); + auto language = row.Get(); + + if (!row) + return row.LogError(this); + + auto *nc = new NickCore(display); + nc->email = email; + ApplyPassword(nc, flags, pass); + + // No equivalent: bglmNQrS + ApplyFlags(nc, flags, 'E', "KILLPROTECT"); + ApplyFlags(nc, flags, 'e', "MEMO_MAIL"); + ApplyFlags(nc, flags, 'n', "NEVEROP"); + ApplyFlags(nc, flags, 'o', "AUTOOP", false); + ApplyFlags(nc, flags, 'P', "MSG"); + ApplyFlags(nc, flags, 'p', "NS_PRIVATE"); + ApplyFlags(nc, flags, 's', "HIDE_EMAIL"); + ApplyFlags(nc, flags, 'W', "UNCONFIRMED"); + + // If an Atheme account was awaiting confirmation but Anope is not + // configured to use confirmation then autoconfirm it. + const auto &nsregister = Config->GetModule("ns_register")->Get<const Anope::string>("registration"); + if (nsregister.equals_ci("none")) + nc->Shrink<bool>("UNCONFIRMED"); + + auto pos = flags.find('h'); + if (pos != Anope::string::npos) + { + userdata.Require(nc)->noexpire = true; + flags.erase(pos, 1); + } + + + if (flags != "+") + Log(this) << "Unable to convert account flags " << flags << " for " << nc->display; + + // No translations yet: bg, cy, da. + if (language == "de") + nc->language = "de_DE.UTF-8"; + else if (language == "en") + nc->language = "en_US.UTF-8"; + else if (language == "es") + nc->language = "es_ES.UTF-8"; + else if (language == "fr") + nc->language = "fr_FR.UTF-8"; + else if (language == "ru") + nc->language = "ru_RU.UTF-8"; + else if (language == "tr") + nc->language = "tr_TR.UTF-8"; + else if (language != "default") + { + Log(this) << "Unable to convert language " << language << " for " << nc->display; + } + + return true; + } + + bool HandleNAM(AthemeRow &row) + { + // NAM <nick> + auto nick = row.Get(); + + if (!row) + return row.LogError(this); + + if (!forbidsvc) + { + Log(this) << "Unable to convert forbidden nick " << nick << " as os_forbid is not loaded"; + return true; + } + + auto *forbid = forbidsvc->CreateForbid(); + forbid->creator = "Unknown"; + forbid->mask = nick; + forbid->reason = "Unknown"; + forbid->type = FT_NICK; + forbidsvc->AddForbid(forbid); + return true; + } + + bool HandleQL(AthemeRow &row) + { + // QL <nick> <host> <duration> <settime> <setby> <reason> + /* auto id = */ row.GetNum<unsigned>(); + auto nick = row.Get(); + auto duration = row.GetNum<unsigned>(); + auto settime = row.GetNum<time_t>(); + auto setby = row.Get(); + auto reason = row.GetRemaining(); + + if (!row) + return row.LogError(this); + + if (!sglinemgr) + { + Log(this) << "Unable to import Q-line on " << nick << " as operserv is not loaded"; + return true; + } + + auto *xl = new XLine(nick, setby, settime + duration, reason); + sqlinemgr->AddXLine(xl); + return true; + } + + bool HandleXL(AthemeRow &row) + { + // XL <id> <real> <duration> <settime> <setby> <reason> + /* auto id = */ row.GetNum<unsigned>(); + auto real = row.Get(); + auto duration = row.GetNum<unsigned>(); + auto settime = row.GetNum<time_t>(); + auto setby = row.Get(); + auto reason = row.GetRemaining(); + + if (!row) + return row.LogError(this); + + if (!sglinemgr) + { + Log(this) << "Unable to import X-line on " << real << " as operserv is not loaded"; + return true; + } + + auto *xl = new XLine(real, setby, settime + duration, reason); + snlinemgr->AddXLine(xl); + return true; + } + +public: + DBAtheme(const Anope::string &modname, const Anope::string &creator) + : Module(modname, creator, DATABASE | VENDOR) + , accessprov("AccessProvider", "access/flags") + , chandata(this, "ATHEME_CHANDATA") + , forbidsvc("ForbidService", "forbid") + , userdata(this, "ATHEME_USERDATA") + , sglinemgr("XLineManager","xlinemanager/sgline") + , snlinemgr("XLineManager","xlinemanager/snline") + , sqlinemgr("XLineManager","xlinemanager/sqline") + { + } + + void OnReload(Configuration::Conf *conf) override + { + flags.clear(); + for (int i = 0; i < Config->CountBlock("privilege"); ++i) + { + Configuration::Block *priv = Config->GetBlock("privilege", i); + const Anope::string &name = priv->Get<const Anope::string>("name"); + const Anope::string &value = priv->Get<const Anope::string>("flag"); + if (!name.empty() && !value.empty()) + flags[name] = value[0]; + } + } + + EventReturn OnLoadDatabase() override + { + const auto dbname = Anope::DataDir + "/" + Config->GetModule(this)->Get<const Anope::string>("database", "atheme.db"); + std::ifstream fd(dbname.str()); + if (!fd.is_open()) + { + Log(this) << "Unable to open " << dbname << " for reading!"; + return EVENT_STOP; + } + + for (Anope::string buf; std::getline(fd, buf.str()); ) + { + AthemeRow row(buf); + + auto rowtype = row.Get(); + if (!row) + continue; // Empty row. + + auto rowhandler = rowhandlers.find(rowtype); + if (rowhandler == rowhandlers.end()) + { + Log(this) << "Unknown row type: " << row.GetRow(); + continue; + } + + if (!rowhandler->second(this, row)) + break; + } + + for (const auto &[_, ci] : *RegisteredChannelList) + { + auto *data = chandata.Get(ci); + if (!data) + continue; + + if (!data->bot.empty()) + { + auto *bi = BotInfo::Find(data->bot); + if (bi) + bi->Assign(nullptr, ci); + } + + if (!data->info_message.empty()) + { + auto *oil = ci->Require<OperInfoList>("operinfo"); + if (oil) + { + auto *info = oil->Create(); + info->target = ci->name; + info->info = data->info_message; + info->adder = data->info_adder.empty() ? "Unknown" : data->info_adder; + info->created = data->info_ts; + (*oil)->push_back(info); + } + else + { + Log(this) << "Unable to convert oper info for " << ci->name << " as os_info is not loaded"; + } + } + + if (!data->suspend_reason.empty()) + { + SuspendInfo si; + si.by = data->suspend_by.empty() ? "Unknown" : data->suspend_by; + si.expires = 0; + si.reason = data->suspend_reason; + si.what = ci->name; + si.when = data->suspend_ts; + ci->Extend("CS_SUSPENDED", si); + } + } + + for (const auto &[_, nc] : *NickCoreList) + { + auto *data = userdata.Get(nc); + if (!data) + continue; + + if (!data->info_message.empty()) + { + auto *oil = nc->Require<OperInfoList>("operinfo"); + if (oil) + { + auto *info = oil->Create(); + info->target = nc->display; + info->info = data->info_message; + info->adder = data->info_adder.empty() ? "Unknown" : data->info_adder; + info->created = data->info_ts; + (*oil)->push_back(info); + } + else + { + Log(this) << "Unable to convert oper info for " << nc->display << " as os_info is not loaded"; + } + } + + if (!data->suspend_reason.empty()) + { + SuspendInfo si; + si.by = data->suspend_by.empty() ? "Unknown" : data->suspend_by; + si.expires = 0; + si.reason = data->suspend_reason; + si.what = nc->display; + si.when = data->suspend_ts; + nc->Extend("NS_SUSPENDED", si); + } + } + + return EVENT_STOP; + } + + void OnUplinkSync(Server *s) override + { + for (auto &[_, ci] : *RegisteredChannelList) + { + auto *data = chandata.Get(ci); + if (!data) + continue; + + auto *ml = ci->Require<ModeLocks>("modelocks"); + if (!ml) + { + Log(this) << "Unable to convert mode locks for " << ci->name << " as cs_mode is not loaded"; + continue; + } + + for (const auto &mlock : data->mlocks) + { + auto mh = ModeManager::FindChannelModeByName(mlock.name); + if (!mh) + { + Log(this) << "Unable to find mode while importing mode lock: " << mlock.name; + continue; + } + + ml->SetMLock(mh, mlock.set, mlock.value, "Unknown"); + } + } + } +}; + +MODULE_INIT(DBAtheme) diff --git a/modules/encryption/enc_sha256.cpp b/modules/encryption/enc_sha256.cpp index 155cfe1f0..b9580cd7f 100644 --- a/modules/encryption/enc_sha256.cpp +++ b/modules/encryption/enc_sha256.cpp @@ -278,8 +278,6 @@ public: ESHA256(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION | VENDOR), sha256provider(this) { - - use_iv = false; } @@ -324,10 +322,10 @@ public: this->OnEncrypt(req->GetPassword(), buf); if (nc->pass.equals_cs(buf)) { - /* if we are NOT the first module in the list, + /* if we are NOT the first module in the list or we are using a default IV * we want to re-encrypt the pass with the new encryption */ - if (ModuleManager::FindFirstOf(ENCRYPTION) != this) + if (ModuleManager::FindFirstOf(ENCRYPTION) != this || !memcmp(iv, sha256_h0, 8)) Anope::Encrypt(req->GetPassword(), nc->pass); req->Success(this); } diff --git a/modules/hostserv/hs_request.cpp b/modules/hostserv/hs_request.cpp index be6831ff8..021e5f5b7 100644 --- a/modules/hostserv/hs_request.cpp +++ b/modules/hostserv/hs_request.cpp @@ -15,20 +15,20 @@ */ #include "module.h" +#include "modules/hs_request.h" static ServiceReference<MemoServService> memoserv("MemoServService", "MemoServ"); static void req_send_memos(Module *me, CommandSource &source, const Anope::string &vIdent, const Anope::string &vHost); -struct HostRequest final - : Serializable +struct HostRequestImpl final + : HostRequest + , Serializable { - Anope::string nick; - Anope::string ident; - Anope::string host; - time_t time; - - HostRequest(Extensible *) : Serializable("HostRequest") { } + HostRequestImpl(Extensible *) + : Serializable("HostRequest") + { + } void Serialize(Serialize::Data &data) const override { @@ -47,11 +47,11 @@ struct HostRequest final if (na == NULL) return NULL; - HostRequest *req; + HostRequestImpl *req; if (obj) - req = anope_dynamic_static_cast<HostRequest *>(obj); + req = anope_dynamic_static_cast<HostRequestImpl *>(obj); else - req = na->Extend<HostRequest>("hostrequest"); + req = na->Extend<HostRequestImpl>("hostrequest"); if (req) { req->nick = na->nick; @@ -162,12 +162,12 @@ public: return; } - HostRequest req(na); + HostRequestImpl req(na); req.nick = source.GetNick(); req.ident = user; req.host = host; req.time = Anope::CurTime; - na->Extend<HostRequest>("hostrequest", req); + na->Extend<HostRequestImpl>("hostrequest", req); source.Reply(_("Your vHost has been requested.")); req_send_memos(owner, source, user, host); @@ -206,7 +206,7 @@ public: const Anope::string &nick = params[0]; NickAlias *na = NickAlias::Find(nick); - HostRequest *req = na ? na->GetExt<HostRequest>("hostrequest") : NULL; + HostRequestImpl *req = na ? na->GetExt<HostRequestImpl>("hostrequest") : NULL; if (req) { na->SetVhost(req->ident, req->host, source.GetNick(), req->time); @@ -217,7 +217,7 @@ public: source.Reply(_("vHost for %s has been activated."), na->nick.c_str()); Log(LOG_COMMAND, source, this) << "for " << na->nick << " for vhost " << (!req->ident.empty() ? req->ident + "@" : "") << req->host; - na->Shrink<HostRequest>("hostrequest"); + na->Shrink<HostRequestImpl>("hostrequest"); } else source.Reply(_("No request for nick %s found."), nick.c_str()); @@ -257,10 +257,10 @@ public: const Anope::string &reason = params.size() > 1 ? params[1] : ""; NickAlias *na = NickAlias::Find(nick); - HostRequest *req = na ? na->GetExt<HostRequest>("hostrequest") : NULL; + HostRequestImpl *req = na ? na->GetExt<HostRequestImpl>("hostrequest") : NULL; if (req) { - na->Shrink<HostRequest>("hostrequest"); + na->Shrink<HostRequestImpl>("hostrequest"); if (Config->GetModule(this->owner)->Get<bool>("memouser") && memoserv) { @@ -311,7 +311,7 @@ public: for (const auto &[nick, na] : *NickAliasList) { - HostRequest *hr = na->GetExt<HostRequest>("hostrequest"); + HostRequestImpl *hr = na->GetExt<HostRequestImpl>("hostrequest"); if (!hr) continue; @@ -358,13 +358,13 @@ class HSRequest final CommandHSActivate commandhsactive; CommandHSReject commandhsreject; CommandHSWaiting commandhswaiting; - ExtensibleItem<HostRequest> hostrequest; + ExtensibleItem<HostRequestImpl> hostrequest; Serialize::Type request_type; public: HSRequest(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), commandhsrequest(this), commandhsactive(this), - commandhsreject(this), commandhswaiting(this), hostrequest(this, "hostrequest"), request_type("HostRequest", HostRequest::Unserialize) + commandhsreject(this), commandhswaiting(this), hostrequest(this, "hostrequest"), request_type("HostRequest", HostRequestImpl::Unserialize) { if (!IRCD || !IRCD->CanSetVHost) throw ModuleException("Your IRCd does not support vhosts"); diff --git a/modules/operserv/os_info.cpp b/modules/operserv/os_info.cpp index 2e293c8f7..6ff1089ff 100644 --- a/modules/operserv/os_info.cpp +++ b/modules/operserv/os_info.cpp @@ -7,20 +7,24 @@ */ #include "module.h" +#include "modules/info.h" -struct OperInfo final - : Serializable +struct OperInfoImpl final + : OperInfo + , Serializable { - Anope::string target; - Anope::string info; - Anope::string adder; - time_t created = 0; + OperInfoImpl() + : Serializable("OperInfo") + { + } - OperInfo() : Serializable("OperInfo") { } - OperInfo(const Anope::string &t, const Anope::string &i, const Anope::string &a, time_t c) : - Serializable("OperInfo"), target(t), info(i), adder(a), created(c) { } + OperInfoImpl(const Anope::string &t, const Anope::string &i, const Anope::string &a, time_t c) + : OperInfo(t, i, a, c) + , Serializable("OperInfo") + { + } - ~OperInfo() override; + ~OperInfoImpl() override; void Serialize(Serialize::Data &data) const override { @@ -34,14 +38,13 @@ struct OperInfo final }; struct OperInfos final - : Serialize::Checker<std::vector<OperInfo *> > + : OperInfoList { - OperInfos(Extensible *) : Serialize::Checker<std::vector<OperInfo *> >("OperInfo") { } + OperInfos(Extensible *) { } - ~OperInfos() + OperInfo *Create() override { - for (unsigned i = (*this)->size(); i > 0; --i) - delete (*this)->at(i - 1); + return new OperInfoImpl(); } static Extensible *Find(const Anope::string &target) @@ -53,7 +56,7 @@ struct OperInfos final } }; -OperInfo::~OperInfo() +OperInfoImpl::~OperInfoImpl() { Extensible *e = OperInfos::Find(target); if (e) @@ -68,7 +71,7 @@ OperInfo::~OperInfo() } } -Serializable *OperInfo::Unserialize(Serializable *obj, Serialize::Data &data) +Serializable *OperInfoImpl::Unserialize(Serializable *obj, Serialize::Data &data) { Anope::string starget; data["target"] >> starget; @@ -78,12 +81,12 @@ Serializable *OperInfo::Unserialize(Serializable *obj, Serialize::Data &data) return NULL; OperInfos *oi = e->Require<OperInfos>("operinfo"); - OperInfo *o; + OperInfoImpl *o; if (obj) - o = anope_dynamic_static_cast<OperInfo *>(obj); + o = anope_dynamic_static_cast<OperInfoImpl *>(obj); else { - o = new OperInfo(); + o = new OperInfoImpl(); o->target = starget; } data["info"] >> o->info; @@ -160,7 +163,7 @@ public: } } - (*oi)->push_back(new OperInfo(target, info, source.GetNick(), Anope::CurTime)); + (*oi)->push_back(new OperInfoImpl(target, info, source.GetNick(), Anope::CurTime)); source.Reply(_("Added info to \002%s\002."), target.c_str()); Log(LOG_ADMIN, source, this) << "to add information to " << target; @@ -272,7 +275,7 @@ class OSInfo final public: OSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - commandosinfo(this), oinfo(this, "operinfo"), oinfo_type("OperInfo", OperInfo::Unserialize) + commandosinfo(this), oinfo(this, "operinfo"), oinfo_type("OperInfo", OperInfoImpl::Unserialize) { } |