diff options
Diffstat (limited to 'modules/database')
-rw-r--r-- | modules/database/db_atheme.cpp | 1611 | ||||
-rw-r--r-- | modules/database/db_flatfile.cpp | 134 | ||||
-rw-r--r-- | modules/database/db_old.cpp | 97 | ||||
-rw-r--r-- | modules/database/db_redis.cpp | 233 | ||||
-rw-r--r-- | modules/database/db_sql.cpp | 90 | ||||
-rw-r--r-- | modules/database/db_sql_live.cpp | 55 |
6 files changed, 1882 insertions, 338 deletions
diff --git a/modules/database/db_atheme.cpp b/modules/database/db_atheme.cpp new file mode 100644 index 000000000..a62571b4b --- /dev/null +++ b/modules/database/db_atheme.cpp @@ -0,0 +1,1611 @@ +/* + * + * (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/os_oper.h" +#include "modules/os_session.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() + { + return Anope::Convert<Numeric>(Get(), 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 +{ + char letter; + Anope::string name; + Anope::string value; + bool set; + + ModeData(const Anope::string &n, bool s, const Anope::string &v = "") + : letter(0) + , name(n) + , value(v) + , set(s) + { + } + + ModeData(char l, const Anope::string &v = "") + : letter(l) + , value(v) + , set(true) + { + } + + Anope::string str() const + { + std::stringstream buf; + buf << '+' << (name.empty() ? letter : name); + if (value.empty()) + buf << ' ' << value; + return buf.str(); + } +}; + +struct ChannelData final +{ + Anope::unordered_map<AutoKick *> akicks; + 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; +}; + +namespace +{ + // Whether we can safely import clones. + bool import_clones = true; +} + +class DBAtheme final + : public Module +{ +private: + ServiceReference<AccessProvider> accessprov; + PrimitiveExtensibleItem<ChannelData> chandata; + std::map<Anope::string, char> flags; + 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::HandleCLONESDBV }, + { "CLONES-EX", &DBAtheme::HandleCLONESEX }, + { "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::HandleSO }, + { "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; + } + + // We are processing an encrypted password. + flags.erase(pos, 1); + + // Atheme supports several password hashing methods. We can only import + // some of them currently. + // + // anope-enc-sha256 Converted to enc_sha256 + // argon2 Converted to enc_argon2 + // base64 Converted to the first encryption algorithm + // bcrypt Converted to enc_bcrypt + // crypt3-des NO + // crypt3-md5 Converted to enc_posix + // crypt3-sha2-256 Converted to enc_posix + // crypt3-sha2-512 Converted to enc_posix + // ircservices Converted to enc_old + // pbkdf2 NO + // pbkdf2v2 NO + // rawmd5 Converted to enc_md5 + // rawsha1 Converted to enc_sha1 + // rawsha2-256 Converted to enc_sha2 + // rawsha2-512 Converted to enc_sha2 + // 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, 9, "$argon2d$", 9) == 0) + nc->pass = "argon2d:" + pass; + + else if (pass.compare(0, 9, "$argon2i$", 9) == 0) + nc->pass = "argon2i:" + pass; + + else if (pass.compare(0, 10, "$argon2id$", 10) == 0) + nc->pass = "argon2id:" + pass; + + 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 = "raw-sha256:" + pass.substr(11); + + else if (pass.compare(0, 11, "$rawsha512$", 11) == 0) + nc->pass = "raw-sha512:" + pass.substr(11); + + else if (pass.compare(0, 3, "$1$", 3) == 0 || pass.compare(0, 3, "$5", 3) == 0 || pass.compare(0, 3, "$6", 3) == 0) + nc->pass = "posix:" + pass; + + 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 (!forbid_service) + { + Log(this) << "Unable to convert forbidden email " << email << " as os_forbid is not loaded"; + return true; + } + + auto *forbid = forbid_service->CreateForbid(); + forbid->created = created; + forbid->creator = creator; + forbid->mask = email; + forbid->reason = reason; + forbid->type = FT_EMAIL; + forbid_service->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) + { + auto *data = chandata.Require(ci); + if (nc) + data->akicks[mask] = ci->AddAkick(setter, nc, "", modifiedtime, modifiedtime); + else + data->akicks[mask] = 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 " << mask << " on " << ci->name; + + return true; + } + + bool HandleCLONESDBV(AthemeRow &row) + { + // CLONES-DBV <version> + auto version = row.GetNum<unsigned>(); + if (version != 3) + { + Log(this) << "Clones database is version " << version << " which is not supported!"; + import_clones = false; + } + return true; + } + + bool HandleCLONESEX(AthemeRow &row) + { + if (!import_clones) + return HandleIgnore(row); + + // CLONES-EX <ip> <allowed> <warn> <expires> <reason> + auto ip = row.Get(); + auto allowed = row.GetNum<unsigned>(); + /* auto warn = */ row.GetNum<unsigned>(); + auto expires = row.GetNum<time_t>(); + auto reason = row.GetRemaining(); + + if (!row) + return row.LogError(this); + + if (!session_service) + { + Log(this) << "Unable to import session limit for " << ip << " as os_session is not loaded"; + return true; + } + + auto *exception = session_service->CreateException(); + exception->mask = ip; + exception->limit = allowed; + exception->who = "Unknown"; + exception->time = Anope::CurTime; + exception->expires = expires; + exception->reason = reason; + session_service->AddException(exception); + return true; + } + + bool HandleDBV(AthemeRow &row) + { + // DBV <version> + auto 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> + auto 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 HandleIgnoreMetadata(const Anope::string &target, const Anope::string &key, const Anope::string &value) + { + Log(LOG_DEBUG_3) << "Intentionally ignoring Atheme database metadata for " << target << ": " << key << " = " << value; + 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 channel = row.Get(); + auto mask = 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 MDA: " << channel; + return false; + } + + if (key == "reason") + { + auto *data = chandata.Require(ci); + auto akick = data->akicks.find(mask); + if (akick != data->akicks.end()) + akick->second->reason = value; + } + else + Log(this) << "Unknown channel access metadata for " << mask << " on " << ci->name << ": " << 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:channelts") + return HandleIgnoreMetadata(ci->name, key, value); + 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 = Anope::Convert<time_t>(value, 0); + 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 = Anope::Convert<time_t>(value, 0); + 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 = Anope::Convert<time_t>(value, 0); + else if (key == "private:mlockext") + { + spacesepstream mlocks(value); + for (Anope::string mlock; mlocks.GetToken(mlock); ) + data->mlocks.emplace_back(mlock[0], mlock.substr(1)); + } + else if (key == "private:templates") + return HandleIgnoreMetadata(ci->name, key, 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 = Anope::Convert<time_t>(value, 0); + else if (key.compare(0, 14, "private:stats:", 14) == 0) + return HandleIgnoreMetadata(ci->name, key, value); + else + Log(this) << "Unknown channel metadata for " << ci->name << ": " << key << " = " << value; + + return true; + } + + bool HandleMDN(AthemeRow &row) + { + // MDN <nick> <key> <value> + auto nick = row.Get(); + auto key = row.Get(); + auto value = row.GetRemaining(); + + if (!row) + return row.LogError(this); + + if (!forbid_service) + { + Log(this) << "Unable to convert forbidden nick " << nick << " metadata as os_forbid is not loaded"; + return true; + } + + auto *forbid = forbid_service->FindForbidExact(nick, FT_NICK); + if (!forbid) + { + Log(this) << "Missing forbid for MDN: " << nick; + return false; + } + + if (key == "private:mark:reason") + forbid->reason = value; + else if (key == "private:mark:setter") + forbid->creator = value; + else if (key == "private:mark:timestamp") + forbid->created = Anope::Convert<time_t>(value, 0); + else + Log(this) << "Unknown forbidden nick metadata for " << forbid->mask << ": " << 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 = Anope::Convert<time_t>(value, kill); + 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 = Anope::Convert<time_t>(value, 0); + 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:loginfail:failnum") + return HandleIgnoreMetadata(nc->display, key, value); + else if (key == "private:loginfail:lastfailaddr") + return HandleIgnoreMetadata(nc->display, key, value); + else if (key == "private:loginfail:lastfailtime") + return HandleIgnoreMetadata(nc->display, key, 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 = Anope::Convert<time_t>(value, 0); + else if (key == "private:swhois") + return HandleIgnoreMetadata(nc->display, key, 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 = Anope::Convert<time_t>(value, 0); + else if (key.compare(0, 18, "private:usercloak:", 18) == 0) + data->vhost_nick[key.substr(18)] = value; + else + Log(this) << "Unknown account metadata for " << nc->display << ": " << 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; + nc->time_registered = regtime; + 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 (!forbid_service) + { + Log(this) << "Unable to convert forbidden nick " << nick << " as os_forbid is not loaded"; + return true; + } + + auto *forbid = forbid_service->CreateForbid(); + forbid->creator = "Unknown"; + forbid->mask = nick; + forbid->reason = "Unknown"; + forbid->type = FT_NICK; + forbid_service->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 HandleSO(AthemeRow &row) + { + // SO <display> <type> <flags> + auto display = row.Get(); + auto type = row.Get(); + auto flags = row.Get(); + + if (!row) + return row.LogError(this); + + auto *nc = NickCore::Find(display); + if (!nc) + { + Log(this) << "Missing NickCore for SO: " << display; + return false; + } + + auto *ot = OperType::Find(type); + if (!ot) + { + // Attempt to convert oper types. + if (type == "sra") + ot = OperType::Find("Services Root"); + else if (type == "ircop") + ot = OperType::Find("Services Operator"); + } + + if (!ot) + { + Log(this) << "Unable to convert operator status for " << nc->display << " as there is no equivalent oper type: " << type; + return true; + } + + nc->o = new MyOper(nc->display, ot); + 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") + , 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::ExpandData(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) + { + ChannelMode *mh; + if (mlock.name.empty()) + mh = ModeManager::FindChannelModeByChar(mlock.letter); + else + mh = ModeManager::FindChannelModeByName(mlock.name); + if (!mh) + { + Log(this) << "Unable to find mode while importing mode lock on " << ci->name << ": " << mlock.str(); + continue; + } + + ml->SetMLock(mh, mlock.set, mlock.value, "Unknown"); + } + } + Anope::SaveDatabases(); + } +}; + +MODULE_INIT(DBAtheme) diff --git a/modules/database/db_flatfile.cpp b/modules/database/db_flatfile.cpp index 5ccf7772f..a253fe24f 100644 --- a/modules/database/db_flatfile.cpp +++ b/modules/database/db_flatfile.cpp @@ -15,15 +15,14 @@ #include <sys/wait.h> #endif -class SaveData : public Serialize::Data +class SaveData final + : public Serialize::Data { - public: +public: Anope::string last; - std::fstream *fs; + std::fstream *fs = nullptr; - SaveData() : fs(NULL) { } - - std::iostream& operator[](const Anope::string &key) anope_override + std::iostream &operator[](const Anope::string &key) override { if (key != last) { @@ -35,18 +34,17 @@ class SaveData : public Serialize::Data } }; -class LoadData : public Serialize::Data +class LoadData final + : public Serialize::Data { - public: - std::fstream *fs; - unsigned int id; +public: + std::fstream *fs = nullptr; + unsigned int id = 0; std::map<Anope::string, Anope::string> data; std::stringstream ss; - bool read; - - LoadData() : fs(NULL), id(0), read(false) { } + bool read = false; - std::iostream& operator[](const Anope::string &key) anope_override + std::iostream &operator[](const Anope::string &key) override { if (!read) { @@ -54,12 +52,7 @@ class LoadData : public Serialize::Data { if (token.find("ID ") == 0) { - try - { - this->id = convertTo<unsigned int>(token.substr(3)); - } - catch (const ConvertException &) { } - + this->id = Anope::Convert(token.substr(3), 0); continue; } else if (token.find("DATA ") != 0) @@ -78,20 +71,12 @@ class LoadData : public Serialize::Data return this->ss; } - std::set<Anope::string> KeySet() const anope_override - { - std::set<Anope::string> keys; - for (std::map<Anope::string, Anope::string>::const_iterator it = this->data.begin(), it_end = this->data.end(); it != it_end; ++it) - keys.insert(it->first); - return keys; - } - - size_t Hash() const anope_override + size_t Hash() const override { size_t hash = 0; - for (std::map<Anope::string, Anope::string>::const_iterator it = this->data.begin(), it_end = this->data.end(); it != it_end; ++it) - if (!it->second.empty()) - hash ^= Anope::hash_cs()(it->second); + for (const auto &[_, value] : this->data) + if (!value.empty()) + hash ^= Anope::hash_cs()(value); return hash; } @@ -103,15 +88,17 @@ class LoadData : public Serialize::Data } }; -class DBFlatFile : public Module, public Pipe +class DBFlatFile final + : public Module + , public Pipe { /* Day the last backup was on */ - int last_day; + int last_day = 0; /* Backup file names */ std::map<Anope::string, std::list<Anope::string> > backups; - bool loaded; + bool loaded = false; - int child_pid; + int child_pid = -1; void BackupDatabase() { @@ -121,69 +108,67 @@ class DBFlatFile : public Module, public Pipe { last_day = tm->tm_mday; - const std::vector<Anope::string> &type_order = Serialize::Type::GetTypeOrder(); - std::set<Anope::string> dbs; dbs.insert(Config->GetModule(this)->Get<const Anope::string>("database", "anope.db")); - for (unsigned i = 0; i < type_order.size(); ++i) + for (const auto &type_order : Serialize::Type::GetTypeOrder()) { - Serialize::Type *stype = Serialize::Type::Find(type_order[i]); + Serialize::Type *stype = Serialize::Type::Find(type_order); if (stype && stype->GetOwner()) dbs.insert("module_" + stype->GetOwner()->name + ".db"); } - for (std::set<Anope::string>::const_iterator it = dbs.begin(), it_end = dbs.end(); it != it_end; ++it) + for (const auto &db : dbs) { - const Anope::string &oldname = Anope::DataDir + "/" + *it; - Anope::string newname = Anope::DataDir + "/backups/" + *it + "-" + stringify(tm->tm_year + 1900) + Anope::printf("-%02i-", tm->tm_mon + 1) + Anope::printf("%02i", tm->tm_mday); + const auto oldname = Anope::ExpandData(db); + const auto newname = Anope::ExpandData("backups/" + db + "-" + Anope::ToString(tm->tm_year + 1900) + Anope::printf("-%02i-", tm->tm_mon + 1) + Anope::printf("%02i", tm->tm_mday)); /* Backup already exists or no database to backup */ if (Anope::IsFile(newname) || !Anope::IsFile(oldname)) continue; - Log(LOG_DEBUG) << "db_flatfile: Attempting to rename " << *it << " to " << newname; + Log(LOG_DEBUG) << "db_flatfile: Attempting to rename " << db << " to " << newname; if (rename(oldname.c_str(), newname.c_str())) { Anope::string err = Anope::LastError(); - Log(this) << "Unable to back up database " << *it << " (" << err << ")!"; + Log(this) << "Unable to back up database " << db << " (" << err << ")!"; if (!Config->GetModule(this)->Get<bool>("nobackupokay")) { Anope::Quitting = true; - Anope::QuitReason = "Unable to back up database " + *it + " (" + err + ")"; + Anope::QuitReason = "Unable to back up database " + db + " (" + err + ")"; } continue; } - backups[*it].push_back(newname); + backups[db].push_back(newname); unsigned keepbackups = Config->GetModule(this)->Get<unsigned>("keepbackups"); - if (keepbackups > 0 && backups[*it].size() > keepbackups) + if (keepbackups > 0 && backups[db].size() > keepbackups) { - unlink(backups[*it].front().c_str()); - backups[*it].pop_front(); + unlink(backups[db].front().c_str()); + backups[db].pop_front(); } } } } - public: - DBFlatFile(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR), last_day(0), loaded(false), child_pid(-1) +public: + DBFlatFile(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR) { } #ifndef _WIN32 - void OnRestart() anope_override + void OnRestart() override { OnShutdown(); } - void OnShutdown() anope_override + void OnShutdown() override { if (child_pid > -1) { @@ -197,7 +182,7 @@ class DBFlatFile : public Module, public Pipe } #endif - void OnNotify() anope_override + void OnNotify() override { char buf[512]; int i = this->Read(buf, sizeof(buf) - 1); @@ -219,12 +204,11 @@ class DBFlatFile : public Module, public Pipe Anope::Quitting = true; } - EventReturn OnLoadDatabase() anope_override + EventReturn OnLoadDatabase() override { - const std::vector<Anope::string> &type_order = Serialize::Type::GetTypeOrder(); std::set<Anope::string> tried_dbs; - const Anope::string &db_name = Anope::DataDir + "/" + Config->GetModule(this)->Get<const Anope::string>("database", "anope.db"); + const auto db_name = Anope::ExpandData(Config->GetModule(this)->Get<const Anope::string>("database", "anope.db")); std::fstream fd(db_name.c_str(), std::ios_base::in | std::ios_base::binary); if (!fd.is_open()) @@ -242,18 +226,16 @@ class DBFlatFile : public Module, public Pipe LoadData ld; ld.fs = &fd; - for (unsigned i = 0; i < type_order.size(); ++i) + for (const auto &type_order : Serialize::Type::GetTypeOrder()) { - Serialize::Type *stype = Serialize::Type::Find(type_order[i]); + Serialize::Type *stype = Serialize::Type::Find(type_order); if (!stype || stype->GetOwner()) continue; - std::vector<std::streampos> &pos = positions[stype->GetName()]; - - for (unsigned j = 0; j < pos.size(); ++j) + for (const auto &position : positions[stype->GetName()]) { fd.clear(); - fd.seekg(pos[j]); + fd.seekg(position); Serializable *obj = stype->Unserialize(NULL, ld); if (obj != NULL) @@ -269,7 +251,7 @@ class DBFlatFile : public Module, public Pipe } - void OnSaveDatabase() anope_override + void OnSaveDatabase() override { if (child_pid > -1) { @@ -299,18 +281,16 @@ class DBFlatFile : public Module, public Pipe std::map<Module *, std::fstream *> databases; /* First open the databases of all of the registered types. This way, if we have a type with 0 objects, that database will be properly cleared */ - for (std::map<Anope::string, Serialize::Type *>::const_iterator it = Serialize::Type::GetTypes().begin(), it_end = Serialize::Type::GetTypes().end(); it != it_end; ++it) + for (const auto &[_, s_type] : Serialize::Type::GetTypes()) { - Serialize::Type *s_type = it->second; - if (databases[s_type->GetOwner()]) continue; Anope::string db_name; if (s_type->GetOwner()) - db_name = Anope::DataDir + "/module_" + s_type->GetOwner()->name + ".db"; + db_name = Anope::ExpandData("module_" + s_type->GetOwner()->name + ".db"); else - db_name = Anope::DataDir + "/" + Config->GetModule(this)->Get<const Anope::string>("database", "anope.db"); + db_name = Anope::ExpandData(Config->GetModule(this)->Get<const Anope::string>("database", "anope.db")); std::fstream *fs = databases[s_type->GetOwner()] = new std::fstream((db_name + ".tmp").c_str(), std::ios_base::out | std::ios_base::trunc | std::ios_base::binary); @@ -320,9 +300,8 @@ class DBFlatFile : public Module, public Pipe SaveData data; const std::list<Serializable *> &items = Serializable::GetItems(); - for (std::list<Serializable *>::const_iterator it = items.begin(), it_end = items.end(); it != it_end; ++it) + for (auto *base : items) { - Serializable *base = *it; Serialize::Type *s_type = base->GetSerializableType(); if (!s_type) continue; @@ -338,10 +317,9 @@ class DBFlatFile : public Module, public Pipe *data.fs << "\nEND\n"; } - for (std::map<Module *, std::fstream *>::iterator it = databases.begin(), it_end = databases.end(); it != it_end; ++it) + for (auto &[mod, f] : databases) { - std::fstream *f = it->second; - const Anope::string &db_name = Anope::DataDir + "/" + (it->first ? (it->first->name + ".db") : Config->GetModule(this)->Get<const Anope::string>("database", "anope.db")); + const auto db_name = Anope::ExpandData((mod ? (mod->name + ".db") : Config->GetModule(this)->Get<const Anope::string>("database", "anope.db"))); if (!f->is_open() || !f->good()) { @@ -376,16 +354,16 @@ class DBFlatFile : public Module, public Pipe } /* Load just one type. Done if a module is reloaded during runtime */ - void OnSerializeTypeCreate(Serialize::Type *stype) anope_override + void OnSerializeTypeCreate(Serialize::Type *stype) override { if (!loaded) return; Anope::string db_name; if (stype->GetOwner()) - db_name = Anope::DataDir + "/module_" + stype->GetOwner()->name + ".db"; + db_name = Anope::ExpandData("module_" + stype->GetOwner()->name + ".db"); else - db_name = Anope::DataDir + "/" + Config->GetModule(this)->Get<const Anope::string>("database", "anope.db"); + db_name = Anope::ExpandData(Config->GetModule(this)->Get<const Anope::string>("database", "anope.db")); std::fstream fd(db_name.c_str(), std::ios_base::in | std::ios_base::binary); if (!fd.is_open()) diff --git a/modules/database/db_old.cpp b/modules/database/db_old.cpp index 7cd63716d..68ba2ca07 100644 --- a/modules/database/db_old.cpp +++ b/modules/database/db_old.cpp @@ -35,13 +35,12 @@ else \ #define OLD_BI_PRIVATE 0x0001 #define OLD_NI_KILLPROTECT 0x00000001 /* Kill others who take this nick */ -#define OLD_NI_SECURE 0x00000002 /* Don't recognize unless IDENTIFY'd */ #define OLD_NI_MSG 0x00000004 /* Use PRIVMSGs instead of NOTICEs */ #define OLD_NI_MEMO_HARDMAX 0x00000008 /* Don't allow user to change memo limit */ #define OLD_NI_MEMO_SIGNON 0x00000010 /* Notify of memos at signon and un-away */ #define OLD_NI_MEMO_RECEIVE 0x00000020 /* Notify of new memos when sent */ #define OLD_NI_PRIVATE 0x00000040 /* Don't show in LIST to non-servadmins */ -#define OLD_NI_HIDE_EMAIL 0x00000080 /* Don't show E-mail in INFO */ +#define OLD_NI_HIDE_EMAIL 0x00000080 /* Don't show email in INFO */ #define OLD_NI_HIDE_MASK 0x00000100 /* Don't show last seen address in INFO */ #define OLD_NI_HIDE_QUIT 0x00000200 /* Don't show last quit message in INFO */ #define OLD_NI_KILL_QUICK 0x00000400 /* Kill in 20 seconds instead of 60 */ @@ -60,7 +59,6 @@ else \ #define OLD_CI_TOPICLOCK 0x00000008 #define OLD_CI_RESTRICTED 0x00000010 #define OLD_CI_PEACE 0x00000020 -#define OLD_CI_SECURE 0x00000040 #define OLD_CI_VERBOTEN 0x00000080 #define OLD_CI_ENCRYPTEDPW 0x00000100 #define OLD_CI_NO_EXPIRE 0x00000200 @@ -94,7 +92,7 @@ else \ #define OLD_NEWS_OPER 1 #define OLD_NEWS_RANDOM 2 -static struct mlock_info +static struct mlock_info final { char c; uint32_t m; @@ -146,20 +144,22 @@ enum static void process_mlock(ChannelInfo *ci, uint32_t lock, bool status, uint32_t *limit, Anope::string *key) { ModeLocks *ml = ci->Require<ModeLocks>("modelocks"); - for (unsigned i = 0; i < (sizeof(mlock_infos) / sizeof(mlock_info)); ++i) - if (lock & mlock_infos[i].m) + for (auto &mlock_info : mlock_infos) + { + if (lock & mlock_info.m) { - ChannelMode *cm = ModeManager::FindChannelModeByChar(mlock_infos[i].c); + ChannelMode *cm = ModeManager::FindChannelModeByChar(mlock_info.c); if (cm && ml) { - if (limit && mlock_infos[i].c == 'l') - ml->SetMLock(cm, status, stringify(*limit)); - else if (key && mlock_infos[i].c == 'k') + if (limit && mlock_info.c == 'l') + ml->SetMLock(cm, status, Anope::ToString(*limit)); + else if (key && mlock_info.c == 'k') ml->SetMLock(cm, status, *key); else ml->SetMLock(cm, status); } } + } } static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -205,7 +205,7 @@ static Anope::string Hex(const char *data, size_t l) { const char hextable[] = "0123456789abcdef"; - std::string rv; + Anope::string rv; for (size_t i = 0; i < l; ++i) { unsigned char c = data[i]; @@ -252,7 +252,7 @@ static Anope::string GetLevelName(int level) case 15: return "NOKICK"; case 16: - return "FANTASIA"; + return "FANTASY"; case 17: return "SAY"; case 18: @@ -307,7 +307,7 @@ static char *strscpy(char *d, const char *s, size_t len) return d_orig; } -struct dbFILE +struct dbFILE final { int mode; /* 'r' for reading, 'w' for writing */ FILE *fp; /* The normal file descriptor */ @@ -321,7 +321,7 @@ static dbFILE *open_db_read(const char *service, const char *filename, int versi int myversion; f = new dbFILE; - strscpy(f->filename, (Anope::DataDir + "/" + filename).c_str(), sizeof(f->filename)); + strscpy(f->filename, Anope::ExpandData(filename).c_str(), sizeof(f->filename)); f->mode = 'r'; fp = fopen(f->filename, "rb"); if (!fp) @@ -390,7 +390,7 @@ static int read_string(Anope::string &str, dbFILE *f) return -1; if (len == 0) return 0; - char *s = new char[len]; + auto *s = new char[len]; if (len != fread(s, 1, len, f->fp)) { delete [] s; @@ -445,12 +445,12 @@ static void LoadNicks() Anope::string buffer; READ(read_string(buffer, f)); - NickCore *nc = new NickCore(buffer); + auto *nc = new NickCore(buffer); - const Anope::string settings[] = { "killprotect", "kill_quick", "ns_secure", "ns_private", "hide_email", + const Anope::string settings[] = { "killprotect", "kill_quick", "ns_private", "hide_email", "hide_mask", "hide_quit", "memo_signon", "memo_receive", "autoop", "msg", "ns_keepmodes" }; - for (unsigned j = 0; j < sizeof(settings) / sizeof(Anope::string); ++j) - nc->Shrink<bool>(settings[j].upper()); + for (const auto &setting : settings) + nc->Shrink<bool>(setting.upper()); char pwbuf[32]; READ(read_buffer(pwbuf, f)); @@ -481,8 +481,6 @@ static void LoadNicks() READ(read_uint32(&u32, f)); if (u32 & OLD_NI_KILLPROTECT) nc->Extend<bool>("KILLPROTECT"); - if (u32 & OLD_NI_SECURE) - nc->Extend<bool>("NS_SECURE"); if (u32 & OLD_NI_MSG) nc->Extend<bool>("MSG"); if (u32 & OLD_NI_MEMO_HARDMAX) @@ -539,24 +537,18 @@ static void LoadNicks() case LANG_DE: nc->language = "de_DE.UTF-8"; break; - case LANG_CAT: - nc->language = "ca_ES.UTF-8"; // yes, iso639 defines catalan as CA - break; case LANG_GR: nc->language = "el_GR.UTF-8"; break; case LANG_NL: nc->language = "nl_NL.UTF-8"; break; - case LANG_RU: - nc->language = "ru_RU.UTF-8"; - break; - case LANG_HUN: - nc->language = "hu_HU.UTF-8"; - break; case LANG_PL: nc->language = "pl_PL.UTF-8"; break; + case LANG_CAT: + case LANG_HUN: + case LANG_RU: case LANG_EN_US: case LANG_JA_JIS: case LANG_JA_EUC: @@ -569,7 +561,6 @@ static void LoadNicks() for (uint16_t j = 0; j < u16; ++j) { READ(read_string(buffer, f)); - nc->access.push_back(buffer); } int16_t i16; @@ -577,7 +568,7 @@ static void LoadNicks() READ(read_int16(&nc->memos.memomax, f)); for (int16_t j = 0; j < i16; ++j) { - Memo *m = new Memo; + auto *m = new Memo; READ(read_uint32(&u32, f)); uint16_t flags; READ(read_uint16(&flags, f)); @@ -653,7 +644,7 @@ static void LoadNicks() continue; } - NickAlias *na = new NickAlias(nick, nc); + auto *na = new NickAlias(nick, nc); na->last_usermask = last_usermask; na->last_realname = last_realname; na->last_quit = last_quit; @@ -693,7 +684,7 @@ static void LoadVHosts() continue; } - na->SetVhost(ident, host, creator, vtime); + na->SetVHost(ident, host, creator, vtime); Log() << "Loaded vhost for " << na->nick; } @@ -748,12 +739,12 @@ static void LoadChannels() Anope::string buffer; char namebuf[64]; READ(read_buffer(namebuf, f)); - ChannelInfo *ci = new ChannelInfo(namebuf); + auto *ci = new ChannelInfo(namebuf); - const Anope::string settings[] = { "keeptopic", "peace", "cs_private", "restricted", "cs_secure", "secureops", "securefounder", + const Anope::string settings[] = { "keeptopic", "peace", "cs_private", "restricted", "secureops", "securefounder", "signkick", "signkick_level", "topiclock", "persist", "noautoop", "cs_keepmodes" }; - for (unsigned j = 0; j < sizeof(settings) / sizeof(Anope::string); ++j) - ci->Shrink<bool>(settings[j].upper()); + for (const auto &setting : settings) + ci->Shrink<bool>(setting.upper()); READ(read_string(buffer, f)); ci->SetFounder(NickCore::Find(buffer)); @@ -799,8 +790,6 @@ static void LoadChannels() ci->Extend<bool>("RESTRICTED"); if (tmpu32 & OLD_CI_PEACE) ci->Extend<bool>("PEACE"); - if (tmpu32 & OLD_CI_SECURE) - ci->Extend<bool>("CS_SECURE"); if (tmpu32 & OLD_CI_NO_EXPIRE) ci->Extend<bool>("CS_NO_EXPIRE"); if (tmpu32 & OLD_CI_MEMO_HARDMAX) @@ -894,7 +883,7 @@ static void LoadChannels() } } else - access->AccessUnserialize(stringify(level)); + access->AccessUnserialize(Anope::ToString(level)); } Anope::string mask; @@ -948,7 +937,7 @@ static void LoadChannels() { READ(read_uint32(&tmpu32, f)); READ(read_uint16(&tmpu16, f)); - Memo *m = new Memo; + auto *m = new Memo; READ(read_int32(&tmp32, f)); m->time = tmp32; char sbuf[32]; @@ -1104,9 +1093,8 @@ static void LoadOper() XLineManager *akill, *sqline, *snline, *szline; akill = sqline = snline = szline = NULL; - for (std::list<XLineManager *>::iterator it = XLineManager::XLineManagers.begin(), it_end = XLineManager::XLineManagers.end(); it != it_end; ++it) + for (auto *xl : XLineManager::XLineManagers) { - XLineManager *xl = *it; if (xl->Type() == 'G') akill = xl; else if (xl->Type() == 'Q') @@ -1138,7 +1126,7 @@ static void LoadOper() if (!akill) continue; - XLine *x = new XLine(user + "@" + host, by, expires, reason, XLineManager::GenerateUID()); + auto *x = new XLine(user + "@" + host, by, expires, reason, XLineManager::GenerateUID()); x->created = seton; akill->AddXLine(x); } @@ -1158,7 +1146,7 @@ static void LoadOper() if (!snline) continue; - XLine *x = new XLine(mask, by, expires, reason, XLineManager::GenerateUID()); + auto *x = new XLine(mask, by, expires, reason, XLineManager::GenerateUID()); x->created = seton; snline->AddXLine(x); } @@ -1178,7 +1166,7 @@ static void LoadOper() if (!sqline) continue; - XLine *x = new XLine(mask, by, expires, reason, XLineManager::GenerateUID()); + auto *x = new XLine(mask, by, expires, reason, XLineManager::GenerateUID()); x->created = seton; sqline->AddXLine(x); } @@ -1198,7 +1186,7 @@ static void LoadOper() if (!szline) continue; - XLine *x = new XLine(mask, by, expires, reason, XLineManager::GenerateUID()); + auto *x = new XLine(mask, by, expires, reason, XLineManager::GenerateUID()); x->created = seton; szline->AddXLine(x); } @@ -1296,12 +1284,13 @@ static void LoadNews() close_db(f); } -class DBOld : public Module +class DBOld final + : public Module { PrimitiveExtensibleItem<uint32_t> mlock_on, mlock_off, mlock_limit; PrimitiveExtensibleItem<Anope::string> mlock_key; - public: +public: DBOld(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR), mlock_on(this, "mlock_on"), mlock_off(this, "mlock_off"), mlock_limit(this, "mlock_limit"), mlock_key(this, "mlock_key") { @@ -1313,7 +1302,7 @@ class DBOld : public Module throw ModuleException("Invalid hash method"); } - EventReturn OnLoadDatabase() anope_override + EventReturn OnLoadDatabase() override { LoadNicks(); LoadVHosts(); @@ -1326,11 +1315,10 @@ class DBOld : public Module return EVENT_STOP; } - void OnUplinkSync(Server *s) anope_override + void OnUplinkSync(Server *s) override { - for (registered_channel_map::iterator it = RegisteredChannelList->begin(), it_end = RegisteredChannelList->end(); it != it_end; ++it) + for (auto &[_, ci] : *RegisteredChannelList) { - ChannelInfo *ci = it->second; uint32_t *limit = mlock_limit.Get(ci); Anope::string *key = mlock_key.Get(ci); @@ -1354,6 +1342,7 @@ class DBOld : public Module if (ci->c) ci->c->CheckModes(); } + Anope::SaveDatabases(); } }; diff --git a/modules/database/db_redis.cpp b/modules/database/db_redis.cpp index 55843beac..4c633d684 100644 --- a/modules/database/db_redis.cpp +++ b/modules/database/db_redis.cpp @@ -14,116 +14,118 @@ using namespace Redis; class DatabaseRedis; static DatabaseRedis *me; -class Data : public Serialize::Data +class Data final + : public Serialize::Data { - public: +public: std::map<Anope::string, std::stringstream *> data; - ~Data() + ~Data() override { - for (std::map<Anope::string, std::stringstream *>::iterator it = data.begin(), it_end = data.end(); it != it_end; ++it) - delete it->second; + for (auto &[_, stream] : data) + delete stream; } - std::iostream& operator[](const Anope::string &key) anope_override + std::iostream &operator[](const Anope::string &key) override { - std::stringstream* &stream = data[key]; + std::stringstream *&stream = data[key]; if (!stream) stream = new std::stringstream(); return *stream; } - std::set<Anope::string> KeySet() const anope_override - { - std::set<Anope::string> keys; - for (std::map<Anope::string, std::stringstream *>::const_iterator it = this->data.begin(), it_end = this->data.end(); it != it_end; ++it) - keys.insert(it->first); - return keys; - } - - size_t Hash() const anope_override + size_t Hash() const override { size_t hash = 0; - for (std::map<Anope::string, std::stringstream *>::const_iterator it = this->data.begin(), it_end = this->data.end(); it != it_end; ++it) - if (!it->second->str().empty()) - hash ^= Anope::hash_cs()(it->second->str()); + for (const auto &[_, value] : this->data) + if (!value->str().empty()) + hash ^= Anope::hash_cs()(value->str()); return hash; } }; -class TypeLoader : public Interface +class TypeLoader final + : public Interface { Anope::string type; - public: +public: TypeLoader(Module *creator, const Anope::string &t) : Interface(creator), type(t) { } - void OnResult(const Reply &r) anope_override; + void OnResult(const Reply &r) override; }; -class ObjectLoader : public Interface +class ObjectLoader final + : public Interface { Anope::string type; int64_t id; - public: +public: ObjectLoader(Module *creator, const Anope::string &t, int64_t i) : Interface(creator), type(t), id(i) { } - void OnResult(const Reply &r) anope_override; + void OnResult(const Reply &r) override; }; -class IDInterface : public Interface +class IDInterface final + : public Interface { Reference<Serializable> o; - public: +public: IDInterface(Module *creator, Serializable *obj) : Interface(creator), o(obj) { } - void OnResult(const Reply &r) anope_override; + void OnResult(const Reply &r) override; }; -class Deleter : public Interface +class Deleter final + : public Interface { Anope::string type; int64_t id; - public: +public: Deleter(Module *creator, const Anope::string &t, int64_t i) : Interface(creator), type(t), id(i) { } - void OnResult(const Reply &r) anope_override; + void OnResult(const Reply &r) override; }; -class Updater : public Interface +class Updater final + : public Interface { Anope::string type; int64_t id; - public: +public: Updater(Module *creator, const Anope::string &t, int64_t i) : Interface(creator), type(t), id(i) { } - void OnResult(const Reply &r) anope_override; + void OnResult(const Reply &r) override; }; -class ModifiedObject : public Interface +class ModifiedObject final + : public Interface { Anope::string type; int64_t id; - public: +public: ModifiedObject(Module *creator, const Anope::string &t, int64_t i) : Interface(creator), type(t), id(i) { } - void OnResult(const Reply &r) anope_override; + void OnResult(const Reply &r) override; }; -class SubscriptionListener : public Interface +class SubscriptionListener final + : public Interface { - public: +public: SubscriptionListener(Module *creator) : Interface(creator) { } - void OnResult(const Reply &r) anope_override; + void OnResult(const Reply &r) override; }; -class DatabaseRedis : public Module, public Pipe +class DatabaseRedis final + : public Module + , public Pipe { SubscriptionListener sl; std::set<Serializable *> updated_items; - public: +public: ServiceReference<Provider> redis; DatabaseRedis(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR), sl(this) @@ -151,33 +153,31 @@ class DatabaseRedis : public Module, public Pipe obj->UpdateCache(data); std::vector<Anope::string> args; - args.push_back("HGETALL"); - args.push_back("hash:" + t->GetName() + ":" + stringify(obj->id)); + args.emplace_back("HGETALL"); + args.push_back("hash:" + t->GetName() + ":" + Anope::ToString(obj->id)); /* Get object attrs to clear before updating */ redis->SendCommand(new Updater(this, t->GetName(), obj->id), args); } } - void OnNotify() anope_override + void OnNotify() override { - for (std::set<Serializable *>::iterator it = this->updated_items.begin(), it_end = this->updated_items.end(); it != it_end; ++it) + for (auto *obj : this->updated_items) { - Serializable *s = *it; - - this->InsertObject(s); + this->InsertObject(obj); } this->updated_items.clear(); } - void OnReload(Configuration::Conf *conf) anope_override + void OnReload(Configuration::Conf *conf) override { Configuration::Block *block = conf->GetModule(this); this->redis = ServiceReference<Provider>("Redis::Provider", block->Get<const Anope::string>("engine", "redis/main")); } - EventReturn OnLoadDatabase() anope_override + EventReturn OnLoadDatabase() override { if (!redis) { @@ -185,10 +185,9 @@ class DatabaseRedis : public Module, public Pipe return EVENT_CONTINUE; } - const std::vector<Anope::string> type_order = Serialize::Type::GetTypeOrder(); - for (unsigned i = 0; i < type_order.size(); ++i) + for (const auto &type_order : Serialize::Type::GetTypeOrder()) { - Serialize::Type *sb = Serialize::Type::Find(type_order[i]); + Serialize::Type *sb = Serialize::Type::Find(type_order); this->OnSerializeTypeCreate(sb); } @@ -205,25 +204,25 @@ class DatabaseRedis : public Module, public Pipe return EVENT_STOP; } - void OnSerializeTypeCreate(Serialize::Type *sb) anope_override + void OnSerializeTypeCreate(Serialize::Type *sb) override { if (!redis) return; std::vector<Anope::string> args; - args.push_back("SMEMBERS"); + args.emplace_back("SMEMBERS"); args.push_back("ids:" + sb->GetName()); redis->SendCommand(new TypeLoader(this, sb->GetName()), args); } - void OnSerializableConstruct(Serializable *obj) anope_override + void OnSerializableConstruct(Serializable *obj) override { this->updated_items.insert(obj); this->Notify(); } - void OnSerializableDestruct(Serializable *obj) anope_override + void OnSerializableDestruct(Serializable *obj) override { Serialize::Type *t = obj->GetSerializableType(); @@ -240,8 +239,8 @@ class DatabaseRedis : public Module, public Pipe } std::vector<Anope::string> args; - args.push_back("HGETALL"); - args.push_back("hash:" + t->GetName() + ":" + stringify(obj->id)); + args.emplace_back("HGETALL"); + args.push_back("hash:" + t->GetName() + ":" + Anope::ToString(obj->id)); /* Get all of the attributes for this object */ redis->SendCommand(new Deleter(this, t->GetName(), obj->id), args); @@ -251,7 +250,7 @@ class DatabaseRedis : public Module, public Pipe this->Notify(); } - void OnSerializableUpdate(Serializable *obj) anope_override + void OnSerializableUpdate(Serializable *obj) override { this->updated_items.insert(obj); this->Notify(); @@ -266,26 +265,19 @@ void TypeLoader::OnResult(const Reply &r) return; } - for (unsigned i = 0; i < r.multi_bulk.size(); ++i) + for (auto *reply : r.multi_bulk) { - const Reply *reply = r.multi_bulk[i]; - if (reply->type != Reply::BULK) continue; - int64_t id; - try - { - id = convertTo<int64_t>(reply->bulk); - } - catch (const ConvertException &) - { + auto i = Anope::TryConvert<int64_t>(reply->bulk); + if (!i) continue; - } + auto id = i.value(); std::vector<Anope::string> args; - args.push_back("HGETALL"); - args.push_back("hash:" + this->type + ":" + stringify(id)); + args.emplace_back("HGETALL"); + args.push_back("hash:" + this->type + ":" + Anope::ToString(id)); me->redis->SendCommand(new ObjectLoader(me, this->type, id), args); } @@ -313,7 +305,7 @@ void ObjectLoader::OnResult(const Reply &r) data[key->bulk] << value->bulk; } - Serializable* &obj = st->objects[this->id]; + Serializable *&obj = st->objects[this->id]; obj = st->Unserialize(obj, data); if (obj) { @@ -332,7 +324,7 @@ void IDInterface::OnResult(const Reply &r) return; } - Serializable* &obj = o->GetSerializableType()->objects[r.i]; + Serializable *&obj = o->GetSerializableType()->objects[r.i]; if (obj) /* This shouldn't be possible */ obj->id = 0; @@ -358,16 +350,16 @@ void Deleter::OnResult(const Reply &r) me->redis->StartTransaction(); std::vector<Anope::string> args; - args.push_back("DEL"); - args.push_back("hash:" + this->type + ":" + stringify(this->id)); + args.emplace_back("DEL"); + args.push_back("hash:" + this->type + ":" + Anope::ToString(this->id)); /* Delete hash object */ me->redis->SendCommand(NULL, args); args.clear(); - args.push_back("SREM"); + args.emplace_back("SREM"); args.push_back("ids:" + this->type); - args.push_back(stringify(this->id)); + args.push_back(Anope::ToString(this->id)); /* Delete id from ids set */ me->redis->SendCommand(NULL, args); @@ -378,9 +370,9 @@ void Deleter::OnResult(const Reply &r) *value = r.multi_bulk[i + 1]; args.clear(); - args.push_back("SREM"); + args.emplace_back("SREM"); args.push_back("value:" + this->type + ":" + key->bulk + ":" + value->bulk); - args.push_back(stringify(this->id)); + args.push_back(Anope::ToString(this->id)); /* Delete value -> object id */ me->redis->SendCommand(NULL, args); @@ -421,9 +413,9 @@ void Updater::OnResult(const Reply &r) *value = r.multi_bulk[i + 1]; std::vector<Anope::string> args; - args.push_back("SREM"); + args.emplace_back("SREM"); args.push_back("value:" + this->type + ":" + key->bulk + ":" + value->bulk); - args.push_back(stringify(this->id)); + args.push_back(Anope::ToString(this->id)); /* Delete value -> object id */ me->redis->SendCommand(NULL, args); @@ -431,29 +423,25 @@ void Updater::OnResult(const Reply &r) /* Add object id to id set for this type */ std::vector<Anope::string> args; - args.push_back("SADD"); + args.emplace_back("SADD"); args.push_back("ids:" + this->type); - args.push_back(stringify(obj->id)); + args.push_back(Anope::ToString(obj->id)); me->redis->SendCommand(NULL, args); args.clear(); - args.push_back("HMSET"); - args.push_back("hash:" + this->type + ":" + stringify(obj->id)); + args.emplace_back("HMSET"); + args.push_back("hash:" + this->type + ":" + Anope::ToString(obj->id)); - typedef std::map<Anope::string, std::stringstream *> items; - for (items::iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it) + for (const auto &[key, value] : data.data) { - const Anope::string &key = it->first; - std::stringstream *value = it->second; - args.push_back(key); - args.push_back(value->str()); + args.emplace_back(value->str()); std::vector<Anope::string> args2; - args2.push_back("SADD"); + args2.emplace_back("SADD"); args2.push_back("value:" + this->type + ":" + key + ":" + value->str()); - args2.push_back(stringify(obj->id)); + args2.push_back(Anope::ToString(obj->id)); /* Add to value -> object id set */ me->redis->SendCommand(NULL, args2); @@ -504,16 +492,11 @@ void SubscriptionListener::OnResult(const Reply &r) if (s_type == NULL) return; - uint64_t obj_id; - try - { - obj_id = convertTo<uint64_t>(id); - } - catch (const ConvertException &) - { + auto oid = Anope::TryConvert<uint64_t>(id); + if (!oid.has_value()) return; - } + auto obj_id = oid.value(); if (op == "hset" || op == "hdel") { Serializable *s = s_type->objects[obj_id]; @@ -528,7 +511,7 @@ void SubscriptionListener::OnResult(const Reply &r) Log(LOG_DEBUG) << "redis: notify: got modify for object id " << obj_id << " of type " << type; std::vector<Anope::string> args; - args.push_back("HGETALL"); + args.emplace_back("HGETALL"); args.push_back("hash:" + type + ":" + id); me->redis->SendCommand(new ModifiedObject(me, type, obj_id), args); @@ -536,7 +519,7 @@ void SubscriptionListener::OnResult(const Reply &r) } else if (op == "del") { - Serializable* &s = s_type->objects[obj_id]; + Serializable *&s = s_type->objects[obj_id]; if (s == NULL) return; @@ -549,14 +532,10 @@ void SubscriptionListener::OnResult(const Reply &r) /* Transaction start */ me->redis->StartTransaction(); - typedef std::map<Anope::string, std::stringstream *> items; - for (items::iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it) + for (const auto &[k, value] : data.data) { - const Anope::string &k = it->first; - std::stringstream *value = it->second; - std::vector<Anope::string> args; - args.push_back("SREM"); + args.emplace_back("SREM"); args.push_back("value:" + type + ":" + k + ":" + value->str()); args.push_back(id); @@ -565,9 +544,9 @@ void SubscriptionListener::OnResult(const Reply &r) } std::vector<Anope::string> args; - args.push_back("SREM"); + args.emplace_back("SREM"); args.push_back("ids:" + type); - args.push_back(stringify(s->id)); + args.push_back(Anope::ToString(s->id)); /* Delete object from id set */ me->redis->SendCommand(NULL, args); @@ -590,7 +569,7 @@ void ModifiedObject::OnResult(const Reply &r) return; } - Serializable* &obj = st->objects[this->id]; + Serializable *&obj = st->objects[this->id]; /* Transaction start */ me->redis->StartTransaction(); @@ -602,16 +581,12 @@ void ModifiedObject::OnResult(const Reply &r) obj->Serialize(data); - typedef std::map<Anope::string, std::stringstream *> items; - for (items::iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it) + for (auto &[key, value] : data.data) { - const Anope::string &key = it->first; - std::stringstream *value = it->second; - std::vector<Anope::string> args; - args.push_back("SREM"); + args.emplace_back("SREM"); args.push_back("value:" + st->GetName() + ":" + key + ":" + value->str()); - args.push_back(stringify(this->id)); + args.push_back(Anope::ToString(this->id)); /* Delete value -> object id */ me->redis->SendCommand(NULL, args); @@ -635,25 +610,21 @@ void ModifiedObject::OnResult(const Reply &r) obj->UpdateCache(data); /* Insert new object values */ - typedef std::map<Anope::string, std::stringstream *> items; - for (items::iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it) + for (const auto &[key, value] : data.data) { - const Anope::string &key = it->first; - std::stringstream *value = it->second; - std::vector<Anope::string> args; - args.push_back("SADD"); + args.emplace_back("SADD"); args.push_back("value:" + st->GetName() + ":" + key + ":" + value->str()); - args.push_back(stringify(obj->id)); + args.push_back(Anope::ToString(obj->id)); /* Add to value -> object id set */ me->redis->SendCommand(NULL, args); } std::vector<Anope::string> args; - args.push_back("SADD"); + args.emplace_back("SADD"); args.push_back("ids:" + st->GetName()); - args.push_back(stringify(obj->id)); + args.push_back(Anope::ToString(obj->id)); /* Add to type -> id set */ me->redis->SendCommand(NULL, args); diff --git a/modules/database/db_sql.cpp b/modules/database/db_sql.cpp index c02edd8aa..d8fe89972 100644 --- a/modules/database/db_sql.cpp +++ b/modules/database/db_sql.cpp @@ -14,17 +14,18 @@ using namespace SQL; -class SQLSQLInterface : public Interface +class SQLSQLInterface + : public Interface { - public: +public: SQLSQLInterface(Module *o) : Interface(o) { } - void OnResult(const Result &r) anope_override + void OnResult(const Result &r) override { Log(LOG_DEBUG) << "SQL successfully executed query: " << r.finished_query; } - void OnError(const Result &r) anope_override + void OnError(const Result &r) override { if (!r.GetQuery().query.empty()) Log(LOG_DEBUG) << "Error executing query " << r.finished_query << ": " << r.GetError(); @@ -33,14 +34,15 @@ class SQLSQLInterface : public Interface } }; -class ResultSQLSQLInterface : public SQLSQLInterface +class ResultSQLSQLInterface final + : public SQLSQLInterface { Reference<Serializable> obj; public: ResultSQLSQLInterface(Module *o, Serializable *ob) : SQLSQLInterface(o), obj(ob) { } - void OnResult(const Result &r) anope_override + void OnResult(const Result &r) override { SQLSQLInterface::OnResult(r); if (r.GetID() > 0 && this->obj) @@ -48,14 +50,16 @@ public: delete this; } - void OnError(const Result &r) anope_override + void OnError(const Result &r) override { SQLSQLInterface::OnError(r); delete this; } }; -class DBSQL : public Module, public Pipe +class DBSQL final + : public Module + , public Pipe { ServiceReference<Provider> sql; SQLSQLInterface sqlinterface; @@ -63,10 +67,10 @@ class DBSQL : public Module, public Pipe bool import; std::set<Serializable *> updated_items; - bool shutting_down; - bool loading_databases; - bool loaded; - bool imported; + bool shutting_down = false; + bool loading_databases = false; + bool loaded = false; + bool imported = false; void RunBackground(const Query &q, Interface *iface = NULL) { @@ -89,8 +93,8 @@ class DBSQL : public Module, public Pipe this->sql->RunQuery(q); } - public: - DBSQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR), sql("", ""), sqlinterface(this), shutting_down(false), loading_databases(false), loaded(false), imported(false) +public: + DBSQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR), sql("", ""), sqlinterface(this) { @@ -98,12 +102,10 @@ class DBSQL : public Module, public Pipe throw ModuleException("db_sql can not be loaded after db_sql_live"); } - void OnNotify() anope_override + void OnNotify() override { - for (std::set<Serializable *>::iterator it = this->updated_items.begin(), it_end = this->updated_items.end(); it != it_end; ++it) + for (auto *obj : this->updated_items) { - Serializable *obj = *it; - if (this->sql) { Data data; @@ -127,15 +129,15 @@ class DBSQL : public Module, public Pipe if (this->imported) { - for (unsigned i = 0; i < create.size(); ++i) - this->RunBackground(create[i]); + for (const auto &query : create) + this->RunBackground(query); this->RunBackground(insert, new ResultSQLSQLInterface(this, obj)); } else { - for (unsigned i = 0; i < create.size(); ++i) - this->sql->RunQuery(create[i]); + for (const auto &query : create) + this->sql->RunQuery(query); /* We are importing objects from another database module, so don't do asynchronous * queries in case the core has to shut down, it will cut short the import @@ -151,7 +153,7 @@ class DBSQL : public Module, public Pipe this->imported = true; } - void OnReload(Configuration::Conf *conf) anope_override + void OnReload(Configuration::Conf *conf) override { Configuration::Block *block = conf->GetModule(this); this->sql = ServiceReference<Provider>("SQL::Provider", block->Get<const Anope::string>("engine")); @@ -159,18 +161,18 @@ class DBSQL : public Module, public Pipe this->import = block->Get<bool>("import"); } - void OnShutdown() anope_override + void OnShutdown() override { this->shutting_down = true; this->OnNotify(); } - void OnRestart() anope_override + void OnRestart() override { this->OnShutdown(); } - EventReturn OnLoadDatabase() anope_override + EventReturn OnLoadDatabase() override { if (!this->sql) { @@ -180,10 +182,9 @@ class DBSQL : public Module, public Pipe this->loading_databases = true; - const std::vector<Anope::string> type_order = Serialize::Type::GetTypeOrder(); - for (unsigned i = 0; i < type_order.size(); ++i) + for (const auto &type_order : Serialize::Type::GetTypeOrder()) { - Serialize::Type *sb = Serialize::Type::Find(type_order[i]); + Serialize::Type *sb = Serialize::Type::Find(type_order); this->OnSerializeTypeCreate(sb); } @@ -193,7 +194,7 @@ class DBSQL : public Module, public Pipe return EVENT_STOP; } - void OnSerializableConstruct(Serializable *obj) anope_override + void OnSerializableConstruct(Serializable *obj) override { if (this->shutting_down || this->loading_databases) return; @@ -202,17 +203,17 @@ class DBSQL : public Module, public Pipe this->Notify(); } - void OnSerializableDestruct(Serializable *obj) anope_override + void OnSerializableDestruct(Serializable *obj) override { if (this->shutting_down) return; Serialize::Type *s_type = obj->GetSerializableType(); if (s_type && obj->id > 0) - this->RunBackground("DELETE FROM `" + this->prefix + s_type->GetName() + "` WHERE `id` = " + stringify(obj->id)); + this->RunBackground("DELETE FROM `" + this->prefix + s_type->GetName() + "` WHERE `id` = " + Anope::ToString(obj->id)); this->updated_items.erase(obj); } - void OnSerializableUpdate(Serializable *obj) anope_override + void OnSerializableUpdate(Serializable *obj) override { if (this->shutting_down || obj->IsTSCached()) return; @@ -223,7 +224,7 @@ class DBSQL : public Module, public Pipe this->Notify(); } - void OnSerializeTypeCreate(Serialize::Type *sb) anope_override + void OnSerializeTypeCreate(Serialize::Type *sb) override { if (!this->loading_databases && !this->loaded) return; @@ -235,23 +236,18 @@ class DBSQL : public Module, public Pipe { Data data; - const std::map<Anope::string, Anope::string> &row = res.Row(j); - for (std::map<Anope::string, Anope::string>::const_iterator rit = row.begin(), rit_end = row.end(); rit != rit_end; ++rit) - data[rit->first] << rit->second; + for (const auto &[key, value] : res.Row(j)) + data[key] << value; Serializable *obj = sb->Unserialize(NULL, data); - try - { - if (obj) - obj->id = convertTo<unsigned int>(res.Get(j, "id")); - } - catch (const ConvertException &) - { - Log(this) << "Unable to convert id for object #" << j << " of type " << sb->GetName(); - } - if (obj) { + auto oid = Anope::TryConvert<unsigned int>(res.Get(j, "id")); + if (oid.has_value()) + obj->id = oid.value(); + else + Log(this) << "Unable to convert id for object #" << j << " of type " << sb->GetName(); + /* The Unserialize operation is destructive so rebuild the data for UpdateCache. * Also the old data may contain columns that we don't use, so we reserialize the * object to know for sure our cache is consistent diff --git a/modules/database/db_sql_live.cpp b/modules/database/db_sql_live.cpp index 407ad6cd2..ea1fb1a27 100644 --- a/modules/database/db_sql_live.cpp +++ b/modules/database/db_sql_live.cpp @@ -11,9 +11,11 @@ using namespace SQL; -class DBMySQL : public Module, public Pipe +class DBMySQL final + : public Module + , public Pipe { - private: +private: Anope::string prefix; ServiceReference<Provider> SQL; time_t lastwarn; @@ -35,7 +37,7 @@ class DBMySQL : public Module, public Pipe } else { - if (Anope::CurTime - Config->GetBlock("options")->Get<time_t>("updatetimeout", "5m") > lastwarn) + if (Anope::CurTime - Config->GetBlock("options")->Get<time_t>("updatetimeout", "2m") > lastwarn) { Log() << "Unable to locate SQL reference, going to readonly..."; Anope::ReadOnly = this->ro = true; @@ -71,7 +73,7 @@ class DBMySQL : public Module, public Pipe throw SQL::Exception("No SQL!"); } - public: +public: DBMySQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR), SQL("", "") { this->lastwarn = 0; @@ -83,15 +85,13 @@ class DBMySQL : public Module, public Pipe throw ModuleException("If db_sql_live is loaded it must be the first database module loaded."); } - void OnNotify() anope_override + void OnNotify() override { if (!this->CheckInit()) return; - for (std::set<Serializable *>::iterator it = this->updated_items.begin(), it_end = this->updated_items.end(); it != it_end; ++it) + for (auto *obj : this->updated_items) { - Serializable *obj = *it; - if (obj && this->SQL) { Data data; @@ -107,8 +107,8 @@ class DBMySQL : public Module, public Pipe continue; std::vector<Query> create = this->SQL->CreateTable(this->prefix + s_type->GetName(), data); - for (unsigned i = 0; i < create.size(); ++i) - this->RunQueryResult(create[i]); + for (const auto &query : create) + this->RunQueryResult(query); Result res = this->RunQueryResult(this->SQL->BuildInsert(this->prefix + s_type->GetName(), obj->id, data)); if (res.GetID() && obj->id != res.GetID()) @@ -123,30 +123,30 @@ class DBMySQL : public Module, public Pipe this->updated_items.clear(); } - EventReturn OnLoadDatabase() anope_override + EventReturn OnLoadDatabase() override { init = true; return EVENT_STOP; } - void OnShutdown() anope_override + void OnShutdown() override { init = false; } - void OnRestart() anope_override + void OnRestart() override { init = false; } - void OnReload(Configuration::Conf *conf) anope_override + void OnReload(Configuration::Conf *conf) override { Configuration::Block *block = conf->GetModule(this); this->SQL = ServiceReference<Provider>("SQL::Provider", block->Get<const Anope::string>("engine")); this->prefix = block->Get<const Anope::string>("prefix", "anope_db_"); } - void OnSerializableConstruct(Serializable *obj) anope_override + void OnSerializableConstruct(Serializable *obj) override { if (!this->CheckInit()) return; @@ -155,7 +155,7 @@ class DBMySQL : public Module, public Pipe this->Notify(); } - void OnSerializableDestruct(Serializable *obj) anope_override + void OnSerializableDestruct(Serializable *obj) override { if (!this->CheckInit()) return; @@ -163,13 +163,13 @@ class DBMySQL : public Module, public Pipe if (s_type) { if (obj->id > 0) - this->RunQuery("DELETE FROM `" + this->prefix + s_type->GetName() + "` WHERE `id` = " + stringify(obj->id)); + this->RunQuery("DELETE FROM `" + this->prefix + s_type->GetName() + "` WHERE `id` = " + Anope::ToString(obj->id)); s_type->objects.erase(obj->id); } this->updated_items.erase(obj); } - void OnSerializeCheck(Serialize::Type *obj) anope_override + void OnSerializeCheck(Serialize::Type *obj) override { if (!this->CheckInit() || obj->GetTimestamp() == Anope::CurTime) return; @@ -185,17 +185,16 @@ class DBMySQL : public Module, public Pipe { const std::map<Anope::string, Anope::string> &row = res.Row(i); - unsigned int id; - try - { - id = convertTo<unsigned int>(res.Get(i, "id")); - } - catch (const ConvertException &) + + + auto oid = Anope::TryConvert<unsigned int>(res.Get(i, "id")); + if (!oid.has_value()) { Log(LOG_DEBUG) << "Unable to convert id from " << obj->GetName(); continue; } + auto id = oid.value(); if (res.Get(i, "timestamp").empty()) { clear_null = true; @@ -207,8 +206,8 @@ class DBMySQL : public Module, public Pipe { Data data; - for (std::map<Anope::string, Anope::string>::const_iterator it = row.begin(), it_end = row.end(); it != it_end; ++it) - data[it->first] << it->second; + for (const auto &[key, value] : row) + data[key] << value; Serializable *s = NULL; std::map<uint64_t, Serializable *>::iterator it = obj->objects.find(id); @@ -237,7 +236,7 @@ class DBMySQL : public Module, public Pipe else { if (!s) - this->RunQuery("UPDATE `" + prefix + obj->GetName() + "` SET `timestamp` = " + this->SQL->FromUnixtime(obj->GetTimestamp()) + " WHERE `id` = " + stringify(id)); + this->RunQuery("UPDATE `" + prefix + obj->GetName() + "` SET `timestamp` = " + this->SQL->FromUnixtime(obj->GetTimestamp()) + " WHERE `id` = " + Anope::ToString(id)); else delete s; } @@ -251,7 +250,7 @@ class DBMySQL : public Module, public Pipe } } - void OnSerializableUpdate(Serializable *obj) anope_override + void OnSerializableUpdate(Serializable *obj) override { if (!this->CheckInit() || obj->IsTSCached()) return; |