From 6302f980fe6dad83aab7d2dc1046dadea5ffd939 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 14 Dec 2015 16:52:24 -0500 Subject: New source tree structure for modules. From commands/cs_access => chanserv/access etc. --- modules/database/CMakeLists.txt | 1 + modules/database/db_flatfile.cpp | 206 ------ modules/database/db_old.cpp | 1428 -------------------------------------- modules/database/db_redis.cpp | 434 ------------ modules/database/db_sql.cpp | 372 ---------- modules/database/flatfile.cpp | 206 ++++++ modules/database/old.cpp | 1428 ++++++++++++++++++++++++++++++++++++++ modules/database/redis.cpp | 434 ++++++++++++ modules/database/sql.cpp | 372 ++++++++++ 9 files changed, 2441 insertions(+), 2440 deletions(-) create mode 100644 modules/database/CMakeLists.txt delete mode 100644 modules/database/db_flatfile.cpp delete mode 100644 modules/database/db_old.cpp delete mode 100644 modules/database/db_redis.cpp delete mode 100644 modules/database/db_sql.cpp create mode 100644 modules/database/flatfile.cpp create mode 100644 modules/database/old.cpp create mode 100644 modules/database/redis.cpp create mode 100644 modules/database/sql.cpp (limited to 'modules/database') diff --git a/modules/database/CMakeLists.txt b/modules/database/CMakeLists.txt new file mode 100644 index 000000000..cd225a94d --- /dev/null +++ b/modules/database/CMakeLists.txt @@ -0,0 +1 @@ +build_modules(${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/modules/database/db_flatfile.cpp b/modules/database/db_flatfile.cpp deleted file mode 100644 index 122134960..000000000 --- a/modules/database/db_flatfile.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * (C) 2003-2014 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 "module.h" - -class DBFlatFile : public Module - , public EventHook - , public EventHook -{ - /* Day the last backup was on */ - int last_day; - /* Backup file names */ - std::map > backups; - bool loaded; - - void BackupDatabase() - { - tm *tm = localtime(&Anope::CurTime); - - if (tm->tm_mday != last_day) - { - last_day = tm->tm_mday; - - const std::map &types = Serialize::TypeBase::GetTypes(); - - std::set dbs; - dbs.insert(Config->GetModule(this)->Get("database", "anope.db")); - - for (const std::pair &p : types) - { - Serialize::TypeBase *stype = p.second; - - if (stype->GetOwner()) - dbs.insert("module_" + stype->GetOwner()->name + ".db"); - } - - - for (std::set::const_iterator it = dbs.begin(), it_end = dbs.end(); it != it_end; ++it) - { - 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); - - /* 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; - if (rename(oldname.c_str(), newname.c_str())) - { - Anope::string err = Anope::LastError(); - Log(this) << "Unable to back up database " << *it << " (" << err << ")!"; - - if (!Config->GetModule(this)->Get("nobackupokay")) - { - Anope::Quitting = true; - Anope::QuitReason = "Unable to back up database " + *it + " (" + err + ")"; - } - - continue; - } - - backups[*it].push_back(newname); - - unsigned keepbackups = Config->GetModule(this)->Get("keepbackups"); - if (keepbackups > 0 && backups[*it].size() > keepbackups) - { - unlink(backups[*it].front().c_str()); - backups[*it].pop_front(); - } - } - } - } - - public: - DBFlatFile(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR) - , last_day(0) - , loaded(false) - { - - } - - EventReturn OnLoadDatabase() override - { - const Anope::string &db_name = Anope::DataDir + "/" + Config->GetModule(this)->Get("database", "anope.db"); - - std::fstream fd(db_name.c_str(), std::ios_base::in | std::ios_base::binary); - if (!fd.is_open()) - { - Log(this) << "Unable to open " << db_name << " for reading!"; - return EVENT_STOP; - } - - Serialize::TypeBase *type = nullptr; - Serialize::Object *obj = nullptr; - for (Anope::string buf; std::getline(fd, buf.str());) - { - if (buf.find("OBJECT ") == 0) - { - Anope::string t = buf.substr(7); - if (obj) - Log(LOG_DEBUG) << "obj != null but got OBJECT"; - if (type) - Log(LOG_DEBUG) << "type != null but got OBJECT"; - type = Serialize::TypeBase::Find(t); - obj = nullptr; - } - else if (buf.find("ID ") == 0) - { - if (!type || obj) - continue; - - try - { - Serialize::ID id = convertTo(buf.substr(3)); - obj = type->Require(id); - } - catch (const ConvertException &) - { - Log(LOG_DEBUG) << "Unable to parse object id " << buf.substr(3); - } - } - else if (buf.find("DATA ") == 0) - { - if (!type) - continue; - - if (!obj) - obj = type->Create(); - - size_t sp = buf.find(' ', 5); // Skip DATA - if (sp == Anope::string::npos) - continue; - - Anope::string key = buf.substr(5, sp - 5), value = buf.substr(sp + 1); - - Serialize::FieldBase *field = type->GetField(key); - if (field) - field->UnserializeFromString(obj, value); - } - else if (buf.find("END") == 0) - { - type = nullptr; - obj = nullptr; - } - } - - fd.close(); - - loaded = true; - return EVENT_STOP; - } - - - void OnSaveDatabase() override - { - BackupDatabase(); - - Anope::string db_name = Anope::DataDir + "/" + Config->GetModule(this)->Get("database", "anope.db"); - - if (Anope::IsFile(db_name)) - rename(db_name.c_str(), (db_name + ".tmp").c_str()); - - std::fstream f(db_name.c_str(), std::ios_base::out | std::ios_base::trunc | std::ios_base::binary); - - if (!f.is_open()) - { - Log(this) << "Unable to open " << db_name << " for writing"; - } - else - { - for (std::pair p : Serialize::objects) - { - Serialize::Object *object = p.second; - Serialize::TypeBase *s_type = object->GetSerializableType(); - - f << "OBJECT " << s_type->GetName() << "\n"; - f << "ID " << object->id << "\n"; - for (Serialize::FieldBase *field : s_type->fields) - f << "DATA " << field->GetName() << " " << field->SerializeToString(object) << "\n"; - f << "END\n"; - } - } - - if (!f.is_open() || !f.good()) - { - f.close(); - rename((db_name + ".tmp").c_str(), db_name.c_str()); - } - else - { - f.close(); - unlink((db_name + ".tmp").c_str()); - } - } -}; - -MODULE_INIT(DBFlatFile) - - diff --git a/modules/database/db_old.cpp b/modules/database/db_old.cpp deleted file mode 100644 index 3d9b92922..000000000 --- a/modules/database/db_old.cpp +++ /dev/null @@ -1,1428 +0,0 @@ -/* - * (C) 2003-2014 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 "module.h" -#include "modules/os_session.h" -#include "modules/bs_kick.h" -#include "modules/cs_mode.h" -#include "modules/bs_badwords.h" -#include "modules/os_news.h" -#include "modules/os_forbid.h" -#include "modules/cs_entrymsg.h" -#include "modules/ns_suspend.h" -#include "modules/cs_suspend.h" -#include "modules/cs_access.h" -#include "modules/ns_access.h" - -#define READ(x) \ -if (true) \ -{ \ - if ((x) < 0) \ - printf("Error, the database is broken, line %d, trying to continue... no guarantee.\n", __LINE__); \ -} \ -else \ - static_cast(0) - -#define getc_db(f) (fgetc((f)->fp)) -#define read_db(f, buf, len) (fread((buf), 1, (len), (f)->fp)) -#define read_buffer(buf, f) (read_db((f), (buf), sizeof(buf)) == sizeof(buf)) - -#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_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 */ -#define OLD_NI_KILL_IMMED 0x00000800 /* Kill immediately instead of in 60 sec */ -#define OLD_NI_MEMO_MAIL 0x00010000 /* User gets email on memo */ -#define OLD_NI_HIDE_STATUS 0x00020000 /* Don't show services access status */ -#define OLD_NI_SUSPENDED 0x00040000 /* Nickname is suspended */ -#define OLD_NI_AUTOOP 0x00080000 /* Autoop nickname in channels */ - -#define OLD_NS_NO_EXPIRE 0x0004 /* nick won't expire */ -#define OLD_NS_VERBOTEN 0x0002 - -#define OLD_CI_KEEPTOPIC 0x00000001 -#define OLD_CI_SECUREOPS 0x00000002 -#define OLD_CI_PRIVATE 0x00000004 -#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 -#define OLD_CI_MEMO_HARDMAX 0x00000400 -#define OLD_CI_OPNOTICE 0x00000800 -#define OLD_CI_SECUREFOUNDER 0x00001000 -#define OLD_CI_SIGNKICK 0x00002000 -#define OLD_CI_SIGNKICK_LEVEL 0x00004000 -#define OLD_CI_XOP 0x00008000 -#define OLD_CI_SUSPENDED 0x00010000 - -/* BotServ SET flags */ -#define OLD_BS_DONTKICKOPS 0x00000001 -#define OLD_BS_DONTKICKVOICES 0x00000002 -#define OLD_BS_FANTASY 0x00000004 -#define OLD_BS_SYMBIOSIS 0x00000008 -#define OLD_BS_GREET 0x00000010 -#define OLD_BS_NOBOT 0x00000020 - -/* BotServ Kickers flags */ -#define OLD_BS_KICK_BOLDS 0x80000000 -#define OLD_BS_KICK_COLORS 0x40000000 -#define OLD_BS_KICK_REVERSES 0x20000000 -#define OLD_BS_KICK_UNDERLINES 0x10000000 -#define OLD_BS_KICK_BADWORDS 0x08000000 -#define OLD_BS_KICK_CAPS 0x04000000 -#define OLD_BS_KICK_FLOOD 0x02000000 -#define OLD_BS_KICK_REPEAT 0x01000000 - -#define OLD_NEWS_LOGON 0 -#define OLD_NEWS_OPER 1 -#define OLD_NEWS_RANDOM 2 - -enum -{ - TTB_BOLDS, - TTB_COLORS, - TTB_REVERSES, - TTB_UNDERLINES, - TTB_BADWORDS, - TTB_CAPS, - TTB_FLOOD, - TTB_REPEAT, -}; - -static struct mlock_info -{ - char c; - uint32_t m; -} mlock_infos[] = { - {'i', 0x00000001}, - {'m', 0x00000002}, - {'n', 0x00000004}, - {'p', 0x00000008}, - {'s', 0x00000010}, - {'t', 0x00000020}, - {'R', 0x00000100}, - {'r', 0x00000200}, - {'c', 0x00000400}, - {'A', 0x00000800}, - {'K', 0x00002000}, - {'O', 0x00008000}, - {'Q', 0x00010000}, - {'S', 0x00020000}, - {'G', 0x00100000}, - {'C', 0x00200000}, - {'u', 0x00400000}, - {'z', 0x00800000}, - {'N', 0x01000000}, - {'M', 0x04000000} -}; - -static Anope::string hashm; - -enum -{ - LANG_EN_US, /* United States English */ - LANG_JA_JIS, /* Japanese (JIS encoding) */ - LANG_JA_EUC, /* Japanese (EUC encoding) */ - LANG_JA_SJIS, /* Japanese (SJIS encoding) */ - LANG_ES, /* Spanish */ - LANG_PT, /* Portugese */ - LANG_FR, /* French */ - LANG_TR, /* Turkish */ - LANG_IT, /* Italian */ - LANG_DE, /* German */ - LANG_CAT, /* Catalan */ - LANG_GR, /* Greek */ - LANG_NL, /* Dutch */ - LANG_RU, /* Russian */ - LANG_HUN, /* Hungarian */ - LANG_PL /* Polish */ -}; - -static void process_mlock(ChanServ::Channel *ci, uint32_t lock, bool status, uint32_t *limit, Anope::string *key) -{ - if (!mlocks) - return; - - for (unsigned i = 0; i < (sizeof(mlock_infos) / sizeof(mlock_info)); ++i) - if (lock & mlock_infos[i].m) - { - ChannelMode *cm = ModeManager::FindChannelModeByChar(mlock_infos[i].c); - if (cm) - { - if (limit && mlock_infos[i].c == 'l') - mlocks->SetMLock(ci, cm, status, stringify(*limit)); - else if (key && mlock_infos[i].c == 'k') - mlocks->SetMLock(ci, cm, status, *key); - else - mlocks->SetMLock(ci, cm, status); - } - } -} - -static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static const char Pad64 = '='; - -static void my_b64_encode(const Anope::string &src, Anope::string &target) -{ - size_t src_pos = 0, src_len = src.length(); - unsigned char input[3]; - - target.clear(); - - while (src_len - src_pos > 2) - { - input[0] = src[src_pos++]; - input[1] = src[src_pos++]; - input[2] = src[src_pos++]; - - target += Base64[input[0] >> 2]; - target += Base64[((input[0] & 0x03) << 4) + (input[1] >> 4)]; - target += Base64[((input[1] & 0x0f) << 2) + (input[2] >> 6)]; - target += Base64[input[2] & 0x3f]; - } - - /* Now we worry about padding */ - if (src_pos != src_len) - { - input[0] = input[1] = input[2] = 0; - for (size_t i = 0; i < src_len - src_pos; ++i) - input[i] = src[src_pos + i]; - - target += Base64[input[0] >> 2]; - target += Base64[((input[0] & 0x03) << 4) + (input[1] >> 4)]; - if (src_pos == src_len - 1) - target += Pad64; - else - target += Base64[((input[1] & 0x0f) << 2) + (input[2] >> 6)]; - target += Pad64; - } -} - -static Anope::string Hex(const char *data, size_t l) -{ - const char hextable[] = "0123456789abcdef"; - - std::string rv; - for (size_t i = 0; i < l; ++i) - { - unsigned char c = data[i]; - rv += hextable[c >> 4]; - rv += hextable[c & 0xF]; - } - return rv; -} - -static Anope::string GetLevelName(int level) -{ - switch (level) - { - case 0: - return "INVITE"; - case 1: - return "AKICK"; - case 2: - return "SET"; - case 3: - return "UNBAN"; - case 4: - return "AUTOOP"; - case 5: - return "AUTODEOP"; - case 6: - return "AUTOVOICE"; - case 7: - return "OP"; - case 8: - return "ACCESS_LIST"; - case 9: - return "CLEAR"; - case 10: - return "NOJOIN"; - case 11: - return "ACCESS_CHANGE"; - case 12: - return "MEMO"; - case 13: - return "ASSIGN"; - case 14: - return "BADWORDS"; - case 15: - return "NOKICK"; - case 16: - return "FANTASIA"; - case 17: - return "SAY"; - case 18: - return "GREET"; - case 19: - return "VOICEME"; - case 20: - return "VOICE"; - case 21: - return "GETKEY"; - case 22: - return "AUTOHALFOP"; - case 23: - return "AUTOPROTECT"; - case 24: - return "OPME"; - case 25: - return "HALFOPME"; - case 26: - return "HALFOP"; - case 27: - return "PROTECTME"; - case 28: - return "PROTECT"; - case 29: - return "KICKME"; - case 30: - return "KICK"; - case 31: - return "SIGNKICK"; - case 32: - return "BANME"; - case 33: - return "BAN"; - case 34: - return "TOPIC"; - case 35: - return "INFO"; - default: - return "INVALID"; - } -} - -static char *strscpy(char *d, const char *s, size_t len) -{ - char *d_orig = d; - - if (!len) - return d; - while (--len && (*d++ = *s++)); - *d = '\0'; - return d_orig; -} - -struct dbFILE -{ - int mode; /* 'r' for reading, 'w' for writing */ - FILE *fp; /* The normal file descriptor */ - char filename[1024]; /* Name of the database file */ -}; - -static dbFILE *open_db_read(const char *service, const char *filename, int version) -{ - dbFILE *f; - FILE *fp; - int myversion; - - f = new dbFILE; - strscpy(f->filename, (Anope::DataDir + "/" + filename).c_str(), sizeof(f->filename)); - f->mode = 'r'; - fp = fopen(f->filename, "rb"); - if (!fp) - { - Log() << "Can't read " << service << " database " << f->filename; - delete f; - return NULL; - } - f->fp = fp; - myversion = fgetc(fp) << 24 | fgetc(fp) << 16 | fgetc(fp) << 8 | fgetc(fp); - if (feof(fp)) - { - Log() << "Error reading version number on " << f->filename << ": End of file detected."; - delete f; - return NULL; - } - else if (myversion < version) - { - Log() << "Unsuported database version (" << myversion << ") on " << f->filename << "."; - delete f; - return NULL; - } - return f; -} - -void close_db(dbFILE *f) -{ - fclose(f->fp); - delete f; -} - -static int read_int16(int16_t *ret, dbFILE *f) -{ - int c1, c2; - - *ret = 0; - - c1 = fgetc(f->fp); - c2 = fgetc(f->fp); - if (c1 == EOF || c2 == EOF) - return -1; - *ret = c1 << 8 | c2; - return 0; -} - -static int read_uint16(uint16_t *ret, dbFILE *f) -{ - int c1, c2; - - *ret = 0; - - c1 = fgetc(f->fp); - c2 = fgetc(f->fp); - if (c1 == EOF || c2 == EOF) - return -1; - *ret = c1 << 8 | c2; - return 0; -} - -static int read_string(Anope::string &str, dbFILE *f) -{ - str.clear(); - uint16_t len; - - if (read_uint16(&len, f) < 0) - return -1; - if (len == 0) - return 0; - char *s = new char[len]; - if (len != fread(s, 1, len, f->fp)) - { - delete [] s; - return -1; - } - str = s; - delete [] s; - return 0; -} - -static int read_uint32(uint32_t *ret, dbFILE *f) -{ - int c1, c2, c3, c4; - - *ret = 0; - - c1 = fgetc(f->fp); - c2 = fgetc(f->fp); - c3 = fgetc(f->fp); - c4 = fgetc(f->fp); - if (c1 == EOF || c2 == EOF || c3 == EOF || c4 == EOF) - return -1; - *ret = c1 << 24 | c2 << 16 | c3 << 8 | c4; - return 0; -} - -int read_int32(int32_t *ret, dbFILE *f) -{ - int c1, c2, c3, c4; - - *ret = 0; - - c1 = fgetc(f->fp); - c2 = fgetc(f->fp); - c3 = fgetc(f->fp); - c4 = fgetc(f->fp); - if (c1 == EOF || c2 == EOF || c3 == EOF || c4 == EOF) - return -1; - *ret = c1 << 24 | c2 << 16 | c3 << 8 | c4; - return 0; -} - -static void LoadNicks() -{ - if (!NickServ::service) - return; - dbFILE *f = open_db_read("NickServ", "nick.db", 14); - if (f == NULL) - return; - for (int i = 0; i < 1024; ++i) - for (int c; (c = getc_db(f)) == 1;) - { - Anope::string buffer; - - READ(read_string(buffer, f)); - - NickServ::Account *nc = NickServ::account.Create(); - nc->SetDisplay(buffer); - - const Anope::string settings[] = { "killprotect", "kill_quick", "ns_secure", "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->UnsetS(settings[j].upper()); - - char pwbuf[32]; - READ(read_buffer(pwbuf, f)); - if (hashm == "plain") - { - Anope::string p; - my_b64_encode(pwbuf, p); - nc->SetPassword(p); - } - else if (hashm == "md5" || hashm == "oldmd5") - nc->SetPassword(Hex(pwbuf, 16)); - else if (hashm == "sha1") - nc->SetPassword(Hex(pwbuf, 20)); - else - nc->SetPassword(Hex(pwbuf, strlen(pwbuf))); - nc->SetPassword(hashm + ":" + nc->GetPassword()); - - READ(read_string(buffer, f)); - nc->SetEmail(buffer); - - READ(read_string(buffer, f)); - if (!buffer.empty()) - nc->Extend("greet", buffer); - - uint32_t u32; - READ(read_uint32(&u32, f)); - //nc->icq = u32; - - READ(read_string(buffer, f)); - //nc->url = buffer; - - READ(read_uint32(&u32, f)); - if (u32 & OLD_NI_KILLPROTECT) - nc->SetS("KILLPROTECT", true); - if (u32 & OLD_NI_SECURE) - nc->SetS("NS_SECURE", true); - if (u32 & OLD_NI_MSG) - nc->SetS("MSG", true); - if (u32 & OLD_NI_MEMO_HARDMAX) - nc->SetS("MEMO_HARDMAX", true); - if (u32 & OLD_NI_MEMO_SIGNON) - nc->SetS("MEMO_SIGNON", true); - if (u32 & OLD_NI_MEMO_RECEIVE) - nc->SetS("MEMO_RECEIVE", true); - if (u32 & OLD_NI_PRIVATE) - nc->SetS("NS_PRIVATE", true); - if (u32 & OLD_NI_HIDE_EMAIL) - nc->SetS("HIDE_EMAIL", true); - if (u32 & OLD_NI_HIDE_MASK) - nc->SetS("HIDE_MASK", true); - if (u32 & OLD_NI_HIDE_QUIT) - nc->SetS("HIDE_QUIT", true); - if (u32 & OLD_NI_KILL_QUICK) - nc->SetS("KILL_QUICK", true); - if (u32 & OLD_NI_KILL_IMMED) - nc->SetS("KILL_IMMED", true); - if (u32 & OLD_NI_MEMO_MAIL) - nc->SetS("MEMO_MAIL", true); - if (u32 & OLD_NI_HIDE_STATUS) - nc->SetS("HIDE_STATUS", true); - if (u32 & OLD_NI_SUSPENDED) - { - if (nssuspendinfo) - { - NSSuspendInfo *si = nssuspendinfo.Create(); - si->SetAccount(nc); - } - } - if (!(u32 & OLD_NI_AUTOOP)) - nc->SetS("AUTOOP", true); - - uint16_t u16; - READ(read_uint16(&u16, f)); - switch (u16) - { - case LANG_ES: - nc->SetLanguage("es_ES"); - break; - case LANG_PT: - nc->SetLanguage("pt_PT"); - break; - case LANG_FR: - nc->SetLanguage("fr_FR"); - break; - case LANG_TR: - nc->SetLanguage("tr_TR"); - break; - case LANG_IT: - nc->SetLanguage("it_IT"); - break; - case LANG_DE: - nc->SetLanguage("de_DE"); - break; - case LANG_CAT: - nc->SetLanguage("ca_ES"); // yes, iso639 defines catalan as CA - break; - case LANG_GR: - nc->SetLanguage("el_GR"); - break; - case LANG_NL: - nc->SetLanguage("nl_NL"); - break; - case LANG_RU: - nc->SetLanguage("ru_RU"); - break; - case LANG_HUN: - nc->SetLanguage("hu_HU"); - break; - case LANG_PL: - nc->SetLanguage("pl_PL"); - break; - case LANG_EN_US: - case LANG_JA_JIS: - case LANG_JA_EUC: - case LANG_JA_SJIS: // these seem to be unused - default: - nc->SetLanguage("en"); - } - - READ(read_uint16(&u16, f)); - for (uint16_t j = 0; j < u16; ++j) - { - READ(read_string(buffer, f)); - - if (nsaccess) - { - NickAccess *a = nsaccess.Create(); - a->SetAccount(nc); - a->SetMask(buffer); - } - } - - int16_t i16; - READ(read_int16(&i16, f)); - READ(read_int16(&i16, f)); - MemoServ::MemoInfo *mi = nc->GetMemos(); - if (mi) - mi->SetMemoMax(i16); - for (int16_t j = 0; j < i16; ++j) - { - MemoServ::Memo *m = MemoServ::service ? MemoServ::service->CreateMemo() : nullptr; - READ(read_uint32(&u32, f)); - uint16_t flags; - READ(read_uint16(&flags, f)); - int32_t tmp32; - READ(read_int32(&tmp32, f)); - if (m) - m->SetTime(tmp32); - char sbuf[32]; - READ(read_buffer(sbuf, f)); - if (m) - m->SetSender(sbuf); - Anope::string text; - READ(read_string(text, f)); - if (m) - m->SetText(text); - } - READ(read_uint16(&u16, f)); - READ(read_int16(&i16, f)); - - Log(LOG_DEBUG) << "Loaded NickServ::Account " << nc->GetDisplay(); - } - - for (int i = 0; i < 1024; ++i) - for (int c; (c = getc_db(f)) == 1;) - { - Anope::string nick, last_usermask, last_realname, last_quit; - time_t time_registered, last_seen; - - READ(read_string(nick, f)); - READ(read_string(last_usermask, f)); - READ(read_string(last_realname, f)); - READ(read_string(last_quit, f)); - - int32_t tmp32; - READ(read_int32(&tmp32, f)); - time_registered = tmp32; - READ(read_int32(&tmp32, f)); - last_seen = tmp32; - - uint16_t tmpu16; - READ(read_uint16(&tmpu16, f)); - - Anope::string core; - READ(read_string(core, f)); - NickServ::Account *nc = NickServ::FindAccount(core); - if (nc == NULL) - { - Log() << "Skipping coreless nick " << nick << " with core " << core; - continue; - } - - if (tmpu16 & OLD_NS_VERBOTEN) - { - if (!forbiddata) - { - delete nc; - continue; - } - - if (nc->GetDisplay().find_first_of("?*") != Anope::string::npos) - { - delete nc; - continue; - } - - ForbidData *d = forbiddata.Create(); - d->SetMask(nc->GetDisplay()); - d->SetCreator(last_usermask); - d->SetReason(last_realname); - d->SetType(FT_NICK); - delete nc; - continue; - } - - NickServ::Nick *na = NickServ::nick.Create(); - na->SetNick(nick); - na->SetAccount(nc); - na->SetLastUsermask(last_usermask); - na->SetLastRealname(last_realname); - na->SetLastQuit(last_quit); - na->SetTimeRegistered(time_registered); - na->SetLastSeen(last_seen); - - if (tmpu16 & OLD_NS_NO_EXPIRE) - na->SetS("NS_NO_EXPIRE", true); - - Log(LOG_DEBUG) << "Loaded NickServ::Nick " << na->GetNick(); - } - - close_db(f); /* End of section Ia */ -} - -static void LoadVHosts() -{ - dbFILE *f = open_db_read("HostServ", "hosts.db", 3); - if (f == NULL) - return; - - for (int c; (c = getc_db(f)) == 1;) - { - Anope::string nick, ident, host, creator; - int32_t vtime; - - READ(read_string(nick, f)); - READ(read_string(ident, f)); - READ(read_string(host, f)); - READ(read_string(creator, f)); - READ(read_int32(&vtime, f)); - - NickServ::Nick *na = NickServ::FindNick(nick); - if (na == NULL) - { - Log() << "Removing vhost for non-existent nick " << nick; - continue; - } - - na->SetVhost(ident, host, creator, vtime); - - Log() << "Loaded vhost for " << na->GetNick(); - } - - close_db(f); -} - -static void LoadBots() -{ - dbFILE *f = open_db_read("Botserv", "bot.db", 10); - if (f == NULL) - return; - - for (int c; (c = getc_db(f)) == 1;) - { - Anope::string nick, user, host, real; - int16_t flags, chancount; - int32_t created; - - READ(read_string(nick, f)); - READ(read_string(user, f)); - READ(read_string(host, f)); - READ(read_string(real, f)); - READ(read_int16(&flags, f)); - READ(read_int32(&created, f)); - READ(read_int16(&chancount, f)); - - ServiceBot *bi = ServiceBot::Find(nick, true); - //XXX - // if (!bi) - // bi = new ServiceBot(nick, user, host, real); - bi->bi->SetCreated(created); - - if (flags & OLD_BI_PRIVATE) - bi->bi->SetOperOnly(true); - - Log(LOG_DEBUG) << "Loaded bot " << bi->nick; - } - - close_db(f); -} - -static void LoadChannels() -{ - if (!ChanServ::service) - return; - - ServiceReference forbid("ForbidService", "forbid"); - dbFILE *f = open_db_read("ChanServ", "chan.db", 16); - if (f == NULL) - return; - - for (int i = 0; i < 256; ++i) - for (int c; (c = getc_db(f)) == 1;) - { - Anope::string buffer; - char namebuf[64]; - READ(read_buffer(namebuf, f)); - ChanServ::Channel *ci = ChanServ::channel.Create(); - ci->SetName(namebuf); - - const Anope::string settings[] = { "keeptopic", "peace", "cs_private", "restricted", "cs_secure", "secureops", "securefounder", - "signkick", "signkick_level", "topiclock", "persist", "noautoop", "cs_keepmodes" }; - for (unsigned j = 0; j < sizeof(settings) / sizeof(Anope::string); ++j) - ci->UnsetS(settings[j].upper()); - - READ(read_string(buffer, f)); - ci->SetFounder(NickServ::FindAccount(buffer)); - - READ(read_string(buffer, f)); - ci->SetSuccessor(NickServ::FindAccount(buffer)); - - char pwbuf[32]; - READ(read_buffer(pwbuf, f)); - - Anope::string desc; - READ(read_string(desc, f)); - ci->SetDesc(desc); - READ(read_string(buffer, f)); - READ(read_string(buffer, f)); - - int32_t tmp32; - READ(read_int32(&tmp32, f)); - ci->SetTimeRegistered(tmp32); - - READ(read_int32(&tmp32, f)); - ci->SetLastUsed(tmp32); - - Anope::string last_topic; - READ(read_string(last_topic, f)); - ci->SetLastTopic(last_topic); - - READ(read_buffer(pwbuf, f)); - ci->SetLastTopicSetter(pwbuf); - - READ(read_int32(&tmp32, f)); - ci->SetLastTopicTime(tmp32); - - uint32_t tmpu32; - READ(read_uint32(&tmpu32, f)); - // Temporary flags cleanup - tmpu32 &= ~0x80000000; - if (tmpu32 & OLD_CI_KEEPTOPIC) - ci->SetS("KEEPTOPIC", true); - if (tmpu32 & OLD_CI_SECUREOPS) - ci->SetS("SECUREOPS", true); - if (tmpu32 & OLD_CI_PRIVATE) - ci->SetS("CS_PRIVATE", true); - if (tmpu32 & OLD_CI_TOPICLOCK) - ci->SetS("TOPICLOCK", true); - if (tmpu32 & OLD_CI_RESTRICTED) - ci->SetS("RESTRICTED", true); - if (tmpu32 & OLD_CI_PEACE) - ci->SetS("PEACE", true); - if (tmpu32 & OLD_CI_SECURE) - ci->SetS("CS_SECURE", true); - if (tmpu32 & OLD_CI_NO_EXPIRE) - ci->SetS("CS_NO_EXPIRE", true); - if (tmpu32 & OLD_CI_MEMO_HARDMAX) - ci->SetS("MEMO_HARDMAX", true); - if (tmpu32 & OLD_CI_SECUREFOUNDER) - ci->SetS("SECUREFOUNDER", true); - if (tmpu32 & OLD_CI_SIGNKICK) - ci->SetS("SIGNKICK", true); - if (tmpu32 & OLD_CI_SIGNKICK_LEVEL) - ci->SetS("SIGNKICK_LEVEL", true); - - Anope::string forbidby, forbidreason; - READ(read_string(forbidby, f)); - READ(read_string(forbidreason, f)); - if (tmpu32 & OLD_CI_SUSPENDED) - { - if (cssuspendinfo) - { - CSSuspendInfo *si = cssuspendinfo.Create(); - si->SetChannel(ci); - si->SetBy(forbidby); - } - } - bool forbid_chan = tmpu32 & OLD_CI_VERBOTEN; - - int16_t tmp16; - READ(read_int16(&tmp16, f)); - ci->SetBanType(tmp16); - - READ(read_int16(&tmp16, f)); - if (tmp16 > 36) - tmp16 = 36; - for (int16_t j = 0; j < tmp16; ++j) - { - int16_t level; - READ(read_int16(&level, f)); - - if (level == ChanServ::ACCESS_INVALID) - level = ChanServ::ACCESS_FOUNDER; - - if (j == 10 && level < 0) // NOJOIN - ci->UnsetS("RESTRICTED"); // If CSDefRestricted was enabled this can happen - - ci->SetLevel(GetLevelName(j), level); - } - - bool xop = tmpu32 & OLD_CI_XOP; - uint16_t tmpu16; - READ(read_uint16(&tmpu16, f)); - for (uint16_t j = 0; j < tmpu16; ++j) - { - uint16_t in_use; - READ(read_uint16(&in_use, f)); - if (in_use) - { - ChanServ::ChanAccess *access = NULL; - - if (xop) - { - if (xopchanaccess) - access = xopchanaccess.Create(); - } - else - { - if (accesschanaccess) - access = accesschanaccess.Create(); - } - - if (access) - access->SetChannel(ci); - - int16_t level; - READ(read_int16(&level, f)); - if (access) - { - if (xop) - { - switch (level) - { - case 3: - access->AccessUnserialize("VOP"); - break; - case 4: - access->AccessUnserialize("HOP"); - break; - case 5: - access->AccessUnserialize("AOP"); - break; - case 10: - access->AccessUnserialize("SOP"); - break; - } - } - else - access->AccessUnserialize(stringify(level)); - } - - Anope::string mask; - READ(read_string(mask, f)); - if (access) - { - access->SetMask(mask); - NickServ::Nick *na = NickServ::FindNick(mask); - if (na) - na->SetAccount(na->GetAccount()); - } - - READ(read_int32(&tmp32, f)); - if (access) - { - access->SetLastSeen(tmp32); - access->SetCreator("Unknown"); - access->SetCreated(Anope::CurTime); - } - } - } - - READ(read_uint16(&tmpu16, f)); - for (uint16_t j = 0; j < tmpu16; ++j) - { - uint16_t flags; - READ(read_uint16(&flags, f)); - if (flags & 0x0001) - { - Anope::string mask, reason, creator; - READ(read_string(mask, f)); - READ(read_string(reason, f)); - READ(read_string(creator, f)); - READ(read_int32(&tmp32, f)); - - ci->AddAkick(creator, mask, reason, tmp32); - } - } - - READ(read_uint32(&tmpu32, f)); // mlock on - ci->Extend("mlock_on", tmpu32); - READ(read_uint32(&tmpu32, f)); // mlock off - ci->Extend("mlock_off", tmpu32); - READ(read_uint32(&tmpu32, f)); // mlock limit - ci->Extend("mlock_limit", tmpu32); - READ(read_string(buffer, f)); // key - ci->Extend("mlock_key", buffer); - READ(read_string(buffer, f)); // +f - READ(read_string(buffer, f)); // +L - - READ(read_int16(&tmp16, f)); - READ(read_int16(&tmp16, f)); - MemoServ::MemoInfo *mi = ci->GetMemos(); - if (mi) - mi->SetMemoMax(tmp16); - for (int16_t j = 0; j < tmp16; ++j) - { - READ(read_uint32(&tmpu32, f)); - READ(read_uint16(&tmpu16, f)); - MemoServ::Memo *m = MemoServ::service ? MemoServ::service->CreateMemo() : nullptr; - READ(read_int32(&tmp32, f)); - if (m) - m->SetTime(tmp32); - char sbuf[32]; - READ(read_buffer(sbuf, f)); - if (m) - m->SetSender(sbuf); - Anope::string text; - READ(read_string(text, f)); - if (m) - m->SetText(text); - } - - READ(read_string(buffer, f)); - if (!buffer.empty()) - { - if (entrymsg) - { - EntryMsg *e = entrymsg.Create(); - e->SetChannel(ci); - e->SetCreator("Unknown"); - e->SetMessage(buffer); - e->SetWhen(Anope::CurTime); - } - } - - READ(read_string(buffer, f)); - ci->SetBot(ServiceBot::Find(buffer, true)); - - READ(read_int32(&tmp32, f)); - if (tmp32 & OLD_BS_DONTKICKOPS) - ci->SetS("BS_DONTKICKOPS", true); - if (tmp32 & OLD_BS_DONTKICKVOICES) - ci->SetS("BS_DONTKICKVOICES", true); - if (tmp32 & OLD_BS_FANTASY) - ci->SetS("BS_FANTASY", true); - if (tmp32 & OLD_BS_GREET) - ci->SetS("BS_GREET", true); - if (tmp32 & OLD_BS_NOBOT) - ci->SetS("BS_NOBOT", true); - - KickerData *kd = GetKickerData(ci); - if (kd) - { - kd->SetBolds(tmp32 & OLD_BS_KICK_BOLDS); - kd->SetColors(tmp32 & OLD_BS_KICK_COLORS); - kd->SetReverses(tmp32 & OLD_BS_KICK_REVERSES); - kd->SetUnderlines(tmp32 & OLD_BS_KICK_UNDERLINES); - kd->SetBadwords(tmp32 & OLD_BS_KICK_BADWORDS); - kd->SetCaps(tmp32 & OLD_BS_KICK_CAPS); - kd->SetFlood(tmp32 & OLD_BS_KICK_FLOOD); - kd->SetRepeat(tmp32 & OLD_BS_KICK_REPEAT); - } - - READ(read_int16(&tmp16, f)); - for (int16_t j = 0; j < tmp16; ++j) - { - int16_t ttb; - READ(read_int16(&ttb, f)); - switch (j) - { - case TTB_BOLDS: - kd->SetTTBBolds(ttb); - break; - case TTB_COLORS: - kd->SetTTBColors(ttb); - break; - case TTB_REVERSES: - kd->SetTTBReverses(ttb); - break; - case TTB_UNDERLINES: - kd->SetTTBUnderlines(ttb); - break; - case TTB_BADWORDS: - kd->SetTTBBadwords(ttb); - break; - case TTB_CAPS: - kd->SetTTBCaps(ttb); - break; - case TTB_FLOOD: - kd->SetTTBFlood(ttb); - break; - case TTB_REPEAT: - kd->SetTTBRepeat(ttb); - break; - } - } - - READ(read_int16(&tmp16, f)); - if (kd) - kd->SetCapsMin(tmp16); - READ(read_int16(&tmp16, f)); - if (kd) - kd->SetCapsPercent(tmp16); - READ(read_int16(&tmp16, f)); - if (kd) - kd->SetFloodLines(tmp16); - READ(read_int16(&tmp16, f)); - if (kd) - kd->SetFloodSecs(tmp16); - READ(read_int16(&tmp16, f)); - if (kd) - kd->SetRepeatTimes(tmp16); - - READ(read_uint16(&tmpu16, f)); - for (uint16_t j = 0; j < tmpu16; ++j) - { - uint16_t in_use; - READ(read_uint16(&in_use, f)); - if (in_use) - { - READ(read_string(buffer, f)); - uint16_t type; - READ(read_uint16(&type, f)); - - BadWordType bwtype = BW_ANY; - if (type == 1) - bwtype = BW_SINGLE; - else if (type == 2) - bwtype = BW_START; - else if (type == 3) - bwtype = BW_END; - - if (badwords) - badwords->AddBadWord(ci, buffer, bwtype); - } - } - - if (forbid_chan) - { - if (!forbiddata) - { - delete ci; - continue; - } - - if (ci->GetName().find_first_of("?*") != Anope::string::npos) - { - delete ci; - continue; - } - - ForbidData *d = forbiddata.Create(); - d->SetMask(ci->GetName()); - d->SetCreator(forbidby); - d->SetReason(forbidreason); - d->SetType(FT_CHAN); - delete ci; - continue; - } - - Log(LOG_DEBUG) << "Loaded channel " << ci->GetName(); - } - - close_db(f); -} - -static void LoadOper() -{ - dbFILE *f = open_db_read("OperServ", "oper.db", 13); - if (f == NULL) - return; - - XLineManager *akill, *sqline, *snline, *szline; - akill = sqline = snline = szline = NULL; - - for (XLineManager *xl : XLineManager::XLineManagers) - { - if (xl->Type() == 'G') - akill = xl; - else if (xl->Type() == 'Q') - sqline = xl; - else if (xl->Type() == 'N') - snline = xl; - else if (xl->Type() == 'Z') - szline = xl; - } - - int32_t tmp32; - READ(read_int32(&tmp32, f)); - READ(read_int32(&tmp32, f)); - - int16_t capacity; - read_int16(&capacity, f); // AKill count - for (int16_t i = 0; i < capacity; ++i) - { - Anope::string user, host, by, reason; - int32_t seton, expires; - - READ(read_string(user, f)); - READ(read_string(host, f)); - READ(read_string(by, f)); - READ(read_string(reason, f)); - READ(read_int32(&seton, f)); - READ(read_int32(&expires, f)); - - if (!akill) - continue; - - XLine *x = new XLine(user + "@" + host, by, expires, reason, XLineManager::GenerateUID()); - x->SetCreated(seton); - akill->AddXLine(x); - } - - read_int16(&capacity, f); // SNLines - for (int16_t i = 0; i < capacity; ++i) - { - Anope::string mask, by, reason; - int32_t seton, expires; - - READ(read_string(mask, f)); - READ(read_string(by, f)); - READ(read_string(reason, f)); - READ(read_int32(&seton, f)); - READ(read_int32(&expires, f)); - - if (!snline) - continue; - - XLine *x = new XLine(mask, by, expires, reason, XLineManager::GenerateUID()); - x->SetCreated(seton); - snline->AddXLine(x); - } - - read_int16(&capacity, f); // SQLines - for (int16_t i = 0; i < capacity; ++i) - { - Anope::string mask, by, reason; - int32_t seton, expires; - - READ(read_string(mask, f)); - READ(read_string(by, f)); - READ(read_string(reason, f)); - READ(read_int32(&seton, f)); - READ(read_int32(&expires, f)); - - if (!sqline) - continue; - - XLine *x = new XLine(mask, by, expires, reason, XLineManager::GenerateUID()); - x->SetCreated(seton); - sqline->AddXLine(x); - } - - read_int16(&capacity, f); // SZLines - for (int16_t i = 0; i < capacity; ++i) - { - Anope::string mask, by, reason; - int32_t seton, expires; - - READ(read_string(mask, f)); - READ(read_string(by, f)); - READ(read_string(reason, f)); - READ(read_int32(&seton, f)); - READ(read_int32(&expires, f)); - - if (!szline) - continue; - - XLine *x = new XLine(mask, by, expires, reason, XLineManager::GenerateUID()); - x->SetCreated(seton); - szline->AddXLine(x); - } - - close_db(f); -} - -static void LoadExceptions() -{ - if (!session_service) - return; - - dbFILE *f = open_db_read("OperServ", "exception.db", 9); - if (f == NULL) - return; - - int16_t num; - READ(read_int16(&num, f)); - for (int i = 0; i < num; ++i) - { - Anope::string mask, reason; - int16_t limit; - char who[32]; - int32_t time, expires; - - READ(read_string(mask, f)); - READ(read_int16(&limit, f)); - READ(read_buffer(who, f)); - READ(read_string(reason, f)); - READ(read_int32(&time, f)); - READ(read_int32(&expires, f)); - - if (exception && session_service) - { - Exception *e = exception.Create(); - e->SetMask(mask); - e->SetLimit(limit); - e->SetWho(who); - e->SetTime(time); - e->SetExpires(expires); - e->SetReason(reason); - } - } - - close_db(f); -} - -static void LoadNews() -{ - if (!newsitem) - return; - - dbFILE *f = open_db_read("OperServ", "news.db", 9); - - if (f == NULL) - return; - - int16_t n; - READ(read_int16(&n, f)); - - for (int16_t i = 0; i < n; i++) - { - int16_t type; - NewsItem *ni = newsitem.Create(); - - READ(read_int16(&type, f)); - - switch (type) - { - case OLD_NEWS_LOGON: - ni->SetNewsType(NEWS_LOGON); - break; - case OLD_NEWS_OPER: - ni->SetNewsType(NEWS_OPER); - break; - case OLD_NEWS_RANDOM: - ni->SetNewsType(NEWS_RANDOM); - break; - } - - int32_t unused; - READ(read_int32(&unused, f)); - - Anope::string text; - READ(read_string(text, f)); - ni->SetText(text); - - char who[32]; - READ(read_buffer(who, f)); - ni->SetWho(who); - - int32_t tmp; - READ(read_int32(&tmp, f)); - ni->SetTime(tmp); - } - - close_db(f); -} - -class DBOld : public Module - , public EventHook - , public EventHook -{ - ExtensibleItem mlock_on, mlock_off, mlock_limit; - ExtensibleItem mlock_key; - - 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") - { - hashm = Config->GetModule(this)->Get("hash"); - - if (hashm != "md5" && hashm != "oldmd5" && hashm != "sha1" && hashm != "plain" && hashm != "sha256") - throw ModuleException("Invalid hash method"); - } - - EventReturn OnLoadDatabase() override - { - LoadNicks(); - LoadVHosts(); - LoadBots(); - LoadChannels(); - LoadOper(); - LoadExceptions(); - LoadNews(); - - return EVENT_STOP; - } - - void OnUplinkSync(Server *s) override - { - if (!ChanServ::service) - return; - for (auto& it : ChanServ::service->GetChannels()) - { - ChanServ::Channel *ci = it.second; - uint32_t *limit = mlock_limit.Get(ci); - Anope::string *key = mlock_key.Get(ci); - - uint32_t *u = mlock_on.Get(ci); - if (u) - { - process_mlock(ci, *u, true, limit, key); - mlock_on.Unset(ci); - } - - u = mlock_off.Get(ci); - if (u) - { - process_mlock(ci, *u, false, limit, key); - mlock_off.Unset(ci); - } - - mlock_limit.Unset(ci); - mlock_key.Unset(ci); - - if (ci->c) - ci->c->CheckModes(); - } - } -}; - -MODULE_INIT(DBOld) - diff --git a/modules/database/db_redis.cpp b/modules/database/db_redis.cpp deleted file mode 100644 index 72f25a16e..000000000 --- a/modules/database/db_redis.cpp +++ /dev/null @@ -1,434 +0,0 @@ -/* - * - * (C) 2003-2014 Anope Team - * Contact us at team@anope.org - * - * Please read COPYING and README for further details. - * - * - */ - -#include "module.h" -#include "modules/redis.h" - -using namespace Redis; - -class DatabaseRedis; -static DatabaseRedis *me; - -class TypeLoader : public Interface -{ - Serialize::TypeBase *type; - - public: - TypeLoader(Module *creator, Serialize::TypeBase *t) : Interface(creator), type(t) { } - - void OnResult(const Reply &r) override; -}; - -class ObjectLoader : public Interface -{ - Serialize::Object *obj; - - public: - ObjectLoader(Module *creator, Serialize::Object *s) : Interface(creator), obj(s) { } - - void OnResult(const Reply &r) override; -}; - -class FieldLoader : public Interface -{ - Serialize::Object *obj; - Serialize::FieldBase *field; - - public: - FieldLoader(Module *creator, Serialize::Object *o, Serialize::FieldBase *f) : Interface(creator), obj(o), field(f) { } - - void OnResult(const Reply &) override; -}; - -class SubscriptionListener : public Interface -{ - public: - SubscriptionListener(Module *creator) : Interface(creator) { } - - void OnResult(const Reply &r) override; -}; - -class DatabaseRedis : public Module - , public EventHook - , public EventHook -{ - SubscriptionListener sl; - - public: - ServiceReference redis; - - DatabaseRedis(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR) - , sl(this) - { - me = this; - } - - void OnReload(Configuration::Conf *conf) override - { - Configuration::Block *block = conf->GetModule(this); - this->redis = ServiceReference("Redis::Provider", block->Get("engine", "redis/main")); - } - - EventReturn OnLoadDatabase() override - { - if (!redis) - return EVENT_STOP; - - const std::map &types = Serialize::TypeBase::GetTypes(); - for (const std::pair &p : types) - this->OnSerializeTypeCreate(p.second); - - while (redis->BlockAndProcess()); - - redis->Subscribe(&this->sl, "anope"); - - return EVENT_STOP; - } - - void OnSerializeTypeCreate(Serialize::TypeBase *sb) - { - std::vector args = { "SMEMBERS", "ids:" + sb->GetName() }; - - redis->SendCommand(new TypeLoader(this, sb), args); - } - - EventReturn OnSerializeList(Serialize::TypeBase *type, std::vector &ids) override - { - return EVENT_CONTINUE; - } - - EventReturn OnSerializeFind(Serialize::TypeBase *type, Serialize::FieldBase *field, const Anope::string &value, Serialize::ID &id) override - { - return EVENT_CONTINUE; - } - - EventReturn OnSerializeGet(Serialize::Object *object, Serialize::FieldBase *field, Anope::string &value) override - { - return EVENT_CONTINUE; - } - - EventReturn OnSerializeGetRefs(Serialize::Object *object, Serialize::TypeBase *type, std::vector &) override - { - return EVENT_CONTINUE; - } - - EventReturn OnSerializeDeref(Serialize::ID id, Serialize::TypeBase *type) override - { - return EVENT_CONTINUE; - } - - EventReturn OnSerializeGetSerializable(Serialize::Object *object, Serialize::FieldBase *field, Anope::string &type, Serialize::ID &value) override - { - return EVENT_CONTINUE; - } - - EventReturn OnSerializeSet(Serialize::Object *object, Serialize::FieldBase *field, const Anope::string &value) override - { - std::vector args; - - redis->StartTransaction(); - - const Anope::string &old = field->SerializeToString(object); - args = { "SREM", "lookup:" + object->GetSerializableType()->GetName() + ":" + field->GetName() + ":" + old, stringify(object->id) }; - redis->SendCommand(nullptr, args); - - // add object to type set - args = { "SADD", "ids:" + object->GetSerializableType()->GetName(), stringify(object->id) }; - redis->SendCommand(nullptr, args); - - // add key to key set - args = { "SADD", "keys:" + stringify(object->id), field->GetName() }; - redis->SendCommand(nullptr, args); - - // set value - args = { "SET", "values:" + stringify(object->id) + ":" + field->GetName(), value }; - redis->SendCommand(nullptr, args); - - // lookup - args = { "SADD", "lookup:" + object->GetSerializableType()->GetName() + ":" + field->GetName() + ":" + value, stringify(object->id) }; - redis->SendCommand(nullptr, args); - - redis->CommitTransaction(); - - return EVENT_CONTINUE; - } - - EventReturn OnSerializeSetSerializable(Serialize::Object *object, Serialize::FieldBase *field, Serialize::Object *value) override - { - return OnSerializeSet(object, field, stringify(value->id)); - } - - EventReturn OnSerializeUnset(Serialize::Object *object, Serialize::FieldBase *field) override - { - std::vector args; - - redis->StartTransaction(); - - const Anope::string &old = field->SerializeToString(object); - args = { "SREM", "lookup:" + object->GetSerializableType()->GetName() + ":" + field->GetName() + ":" + old, stringify(object->id) }; - redis->SendCommand(nullptr, args); - - // remove field from set - args = { "SREM", "keys:" + stringify(object->id), field->GetName() }; - redis->SendCommand(nullptr, args); - - redis->CommitTransaction(); - - return EVENT_CONTINUE; - } - - EventReturn OnSerializeUnsetSerializable(Serialize::Object *object, Serialize::FieldBase *field) override - { - return OnSerializeUnset(object, field); - } - - EventReturn OnSerializeHasField(Serialize::Object *object, Serialize::FieldBase *field) override - { - return EVENT_CONTINUE; - } - - EventReturn OnSerializableGetId(Serialize::ID &id) override - { - std::vector args = { "INCR", "id" }; - - auto f = [&](const Reply &r) - { - id = r.i; - }; - - FInterface inter(this, f); - redis->SendCommand(&inter, args); - while (redis->BlockAndProcess()); - return EVENT_ALLOW; - } - - void OnSerializableCreate(Serialize::Object *) override - { - } - - void OnSerializableDelete(Serialize::Object *obj) override - { - std::vector args; - - redis->StartTransaction(); - - for (Serialize::FieldBase *field : obj->GetSerializableType()->fields) - { - Anope::string value = field->SerializeToString(obj); - - args = { "SREM", "lookup:" + obj->GetSerializableType()->GetName() + ":" + field->GetName() + ":" + value, stringify(obj->id) }; - redis->SendCommand(nullptr, args); - - args = { "DEL", "values:" + stringify(obj->id) + ":" + field->GetName() }; - redis->SendCommand(nullptr, args); - - args = { "SREM", "keys:" + stringify(obj->id), field->GetName() }; - redis->SendCommand(nullptr, args); - } - - args = { "SREM", "ids:" + obj->GetSerializableType()->GetName(), stringify(obj->id) }; - redis->SendCommand(nullptr, args); - - redis->CommitTransaction(); - } -}; - -void TypeLoader::OnResult(const Reply &r) -{ - if (r.type != Reply::MULTI_BULK || !me->redis) - { - delete this; - return; - } - - for (unsigned i = 0; i < r.multi_bulk.size(); ++i) - { - const Reply *reply = r.multi_bulk[i]; - - if (reply->type != Reply::BULK) - continue; - - int64_t id; - try - { - id = convertTo(reply->bulk); - } - catch (const ConvertException &) - { - continue; - } - - Serialize::Object *obj = type->Require(id); - if (obj == nullptr) - { - Log(LOG_DEBUG) << "redis: Unable to require object #" << id << " of type " << type->GetName(); - continue; - } - - std::vector args = { "SMEMBERS", "keys:" + stringify(id) }; - - me->redis->SendCommand(new ObjectLoader(me, obj), args); - } - - delete this; -} - -void ObjectLoader::OnResult(const Reply &r) -{ - if (r.type != Reply::MULTI_BULK || r.multi_bulk.empty() || !me->redis) - { - delete this; - return; - } - - Serialize::TypeBase *type = obj->GetSerializableType(); - - for (Reply *reply : r.multi_bulk) - { - const Anope::string &key = reply->bulk; - Serialize::FieldBase *field = type->GetField(key); - - if (field == nullptr) - continue; - - std::vector args = { "GET", "values:" + stringify(obj->id) + ":" + key }; - - me->redis->SendCommand(new FieldLoader(me, obj, field), args); - } - - delete this; -} - -void FieldLoader::OnResult(const Reply &r) -{ - Log(LOG_DEBUG_2) << "redis: Setting field " << field->GetName() << " of object #" << obj->id << " of type " << obj->GetSerializableType()->GetName() << " to " << r.bulk; - field->UnserializeFromString(obj, r.bulk); - - delete this; -} - -void SubscriptionListener::OnResult(const Reply &r) -{ - /* - * message - * anope - * message - * - * set 4 email adam@anope.org - * unset 4 email - * create 4 NickCore - * delete 4 - */ - - const Anope::string &message = r.multi_bulk[2]->bulk; - Anope::string command; - spacesepstream sep(message); - - sep.GetToken(command); - - if (command == "set" || command == "unset") - { - Anope::string sid, key, value; - - sep.GetToken(sid); - sep.GetToken(key); - value = sep.GetRemaining(); - - Serialize::ID id; - try - { - id = convertTo(sid); - } - catch (const ConvertException &ex) - { - Log(LOG_DEBUG) << "redis: unable to get id for SL update key " << sid; - return; - } - - Serialize::Object *obj = Serialize::GetID(id); - if (obj == nullptr) - { - Log(LOG_DEBUG) << "redis: pmessage for unknown object #" << id; - return; - } - - Serialize::FieldBase *field = obj->GetSerializableType()->GetField(key); - if (field == nullptr) - { - Log(LOG_DEBUG) << "redis: pmessage for unknown field of object #" << id << ": " << key; - return; - } - - Log(LOG_DEBUG_2) << "redis: Setting field " << field->GetName() << " of object #" << obj->id << " of type " << obj->GetSerializableType()->GetName() << " to " << value; - field->UnserializeFromString(obj, value); - } - else if (command == "create") - { - Anope::string sid, stype; - - sep.GetToken(sid); - sep.GetToken(stype); - - Serialize::ID id; - try - { - id = convertTo(sid); - } - catch (const ConvertException &ex) - { - Log(LOG_DEBUG) << "redis: unable to get id for SL update key " << sid; - return; - } - - Serialize::TypeBase *type = Serialize::TypeBase::Find(stype); - if (type == nullptr) - { - Log(LOG_DEBUG) << "redis: pmessage create for nonexistant type " << stype; - return; - } - - Serialize::Object *obj = type->Require(id); - if (obj == nullptr) - { - Log(LOG_DEBUG) << "redis: require for pmessage create type " << type->GetName() << " id #" << id << " returned nullptr"; - return; - } - } - else if (command == "delete") - { - Anope::string sid; - - sep.GetToken(sid); - - Serialize::ID id; - try - { - id = convertTo(sid); - } - catch (const ConvertException &ex) - { - Log(LOG_DEBUG) << "redis: unable to get id for SL update key " << sid; - return; - } - - Serialize::Object *obj = Serialize::GetID(id); - if (obj == nullptr) - { - Log(LOG_DEBUG) << "redis: message for unknown object #" << id; - return; - } - - obj->Delete(); - } - else - Log(LOG_DEBUG) << "redis: unknown message: " << message; -} - -MODULE_INIT(DatabaseRedis) diff --git a/modules/database/db_sql.cpp b/modules/database/db_sql.cpp deleted file mode 100644 index fed0050e4..000000000 --- a/modules/database/db_sql.cpp +++ /dev/null @@ -1,372 +0,0 @@ -#include "module.h" -#include "modules/sql.h" - -using namespace SQL; - -class DBMySQL : public Module, public Pipe - , public EventHook -{ - private: - bool transaction = false; - bool inited = false; - Anope::string prefix; - ServiceReference SQL; - - Result Run(const Query &query) - { - if (!SQL) - ;//XXX - - if (!inited) - { - inited = true; - for (const Query &q : SQL->InitSchema(prefix)) - SQL->RunQuery(q); - } - - return SQL->RunQuery(query); - } - - void StartTransaction() - { - if (!SQL || transaction) - return; - - Run(SQL->BeginTransaction()); - - transaction = true; - Notify(); - } - - void Commit() - { - if (!SQL || !transaction) - return; - - Run(SQL->Commit()); - - transaction = false; - } - - public: - DBMySQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR) - { - } - - void OnNotify() override - { - Commit(); - Serialize::Clear(); - } - - void OnReload(Configuration::Conf *conf) override - { - Configuration::Block *block = conf->GetModule(this); - this->SQL = ServiceReference("SQL::Provider", block->Get("engine")); - this->prefix = block->Get("prefix", "anope_db_"); - inited = false; - } - - EventReturn OnSerializeList(Serialize::TypeBase *type, std::vector &ids) override - { - StartTransaction(); - - ids.clear(); - - Query query = "SELECT `id` FROM `" + prefix + type->GetName() + "`"; - Result res = Run(query); - for (int i = 0; i < res.Rows(); ++i) - { - Serialize::ID id = convertTo(res.Get(i, "id")); - ids.push_back(id); - } - - return EVENT_ALLOW; - } - - EventReturn OnSerializeFind(Serialize::TypeBase *type, Serialize::FieldBase *field, const Anope::string &value, Serialize::ID &id) override - { - if (!SQL) - return EVENT_CONTINUE; - - StartTransaction(); - - for (Query &q : SQL->CreateTable(prefix, type->GetName())) - Run(q); - - for (Query &q : SQL->AlterTable(prefix, type->GetName(), field->GetName(), false)) - Run(q); - - for (const Query &q : SQL->CreateIndex(prefix + type->GetName(), field->GetName())) - Run(q); - - Query query("SELECT `id` FROM `" + prefix + type->GetName() + "` WHERE `" + field->GetName() + "` = @value@"); - query.SetValue("value", value); - Result res = Run(query); - if (res.Rows()) - try - { - id = convertTo(res.Get(0, "id")); - return EVENT_ALLOW; - } - catch (const ConvertException &) - { - } - return EVENT_CONTINUE; - } - - private: - bool GetValue(Serialize::Object *object, Serialize::FieldBase *field, SQL::Result::Value &v) - { - StartTransaction(); - - Query query = "SELECT `" + field->GetName() + "` FROM `" + prefix + object->GetSerializableType()->GetName() + "` WHERE `id` = @id@"; - query.SetValue("id", object->id); - Result res = Run(query); - - if (res.Rows() == 0) - return false; - - v = res.GetValue(0, field->GetName()); - return true; - } - - public: - EventReturn OnSerializeGet(Serialize::Object *object, Serialize::FieldBase *field, Anope::string &value) override - { - SQL::Result::Value v; - - if (!GetValue(object, field, v)) - { - field->CacheMiss(object); - return EVENT_CONTINUE; - } - - value = v.value; - return EVENT_ALLOW; - } - - EventReturn OnSerializeGetRefs(Serialize::Object *object, Serialize::TypeBase *type, std::vector &edges) override - { - StartTransaction(); - - edges.clear(); - - Query query; - if (type) - query = "SELECT field," + prefix + "edges.id,other_id,j1.type,j2.type AS other_type FROM `" + prefix + "edges` " - "JOIN `" + prefix + "objects` AS j1 ON " + prefix + "edges.id = j1.id " - "JOIN `" + prefix + "objects` AS j2 ON " + prefix + "edges.other_id = j2.id " - "WHERE " - " (" + prefix + "edges.id = @id@ AND j2.type = @other_type@) " - "OR" - " (other_id = @id@ AND j1.type = @other_type@)"; - else - query = "SELECT field," + prefix + "edges.id,other_id,j1.type,j2.type AS other_type FROM `" + prefix + "edges` " - "JOIN `" + prefix + "objects` AS j1 ON " + prefix + "edges.id = j1.id " - "JOIN `" + prefix + "objects` AS j2 ON " + prefix + "edges.other_id = j2.id " - "WHERE " + prefix + "edges.id = @id@ OR other_id = @id@"; - - query.SetValue("type", object->GetSerializableType()->GetName()); - query.SetValue("id", object->id); - if (type) - query.SetValue("other_type", type->GetName()); - - Result res = Run(query); - for (int i = 0; i < res.Rows(); ++i) - { - Serialize::ID id = convertTo(res.Get(i, "id")); - - if (id == object->id) - { - // we want other type, this is my edge - Anope::string t = res.Get(i, "other_type"); - Anope::string f = res.Get(i, "field"); - id = convertTo(res.Get(i, "other_id")); - - //XXX sanity checks - Serialize::FieldBase *obj_field = object->GetSerializableType()->GetField(f); - - Serialize::TypeBase *obj_type = Serialize::TypeBase::Find(t); - Serialize::Object *other = obj_type->Require(id); - - edges.emplace_back(other, obj_field, true); - } - else - { - // edge to me - Anope::string t = res.Get(i, "type"); - Anope::string f = res.Get(i, "field"); - - //XXX sanity checks - Serialize::TypeBase *obj_type = Serialize::TypeBase::Find(t); - Serialize::FieldBase *obj_field = obj_type->GetField(f); - Serialize::Object *other = obj_type->Require(id); - - // other type, other field, - edges.emplace_back(other, obj_field, false); - } - } - - return EVENT_ALLOW; - } - - EventReturn OnSerializeDeref(Serialize::ID id, Serialize::TypeBase *type) override - { - StartTransaction(); - - Query query = "SELECT `id` FROM `" + prefix + type->GetName() + "` WHERE `id` = @id@"; - query.SetValue("id", id); - Result res = Run(query); - if (res.Rows() == 0) - return EVENT_CONTINUE; - return EVENT_ALLOW; - } - - EventReturn OnSerializeGetSerializable(Serialize::Object *object, Serialize::FieldBase *field, Anope::string &type, Serialize::ID &value) override - { - StartTransaction(); - - Query query = "SELECT `" + field->GetName() + "`,j1.type AS " + field->GetName() + "_type FROM `" + prefix + object->GetSerializableType()->GetName() + "` " - "JOIN `" + prefix + "objects` AS j1 ON " + prefix + object->GetSerializableType()->GetName() + "." + field->GetName() + " = j1.id " - "WHERE " + prefix + object->GetSerializableType()->GetName() + ".id = @id@"; - query.SetValue("id", object->id); - Result res = Run(query); - - if (res.Rows() == 0) - return EVENT_CONTINUE; - - type = res.Get(0, field->GetName() + "_type"); - try - { - value = convertTo(res.Get(0, field->GetName())); - } - catch (const ConvertException &ex) - { - return EVENT_STOP; - } - - return EVENT_ALLOW; - } - - private: - void DoSet(Serialize::Object *object, Serialize::FieldBase *field, bool is_object, const Anope::string *value) - { - if (!SQL) - return; - - StartTransaction(); - - for (Query &q : SQL->CreateTable(prefix, object->GetSerializableType()->GetName())) - Run(q); - - for (Query &q : SQL->AlterTable(prefix, object->GetSerializableType()->GetName(), field->GetName(), is_object)) - Run(q); - - Query q; - q.SetValue("id", object->id); - if (value) - q.SetValue(field->GetName(), *value); - else - q.SetNull(field->GetName()); - - for (Query &q2 : SQL->Replace(prefix + object->GetSerializableType()->GetName(), q, { "id" })) - Run(q2); - } - - public: - EventReturn OnSerializeSet(Serialize::Object *object, Serialize::FieldBase *field, const Anope::string &value) override - { - DoSet(object, field, false, &value); - return EVENT_STOP; - } - - EventReturn OnSerializeSetSerializable(Serialize::Object *object, Serialize::FieldBase *field, Serialize::Object *value) override - { - if (!SQL) - return EVENT_CONTINUE; - - StartTransaction(); - - if (value) - { - Anope::string v = stringify(value->id); - DoSet(object, field, true, &v); - - Query query; - query.SetValue("field", field->GetName()); - query.SetValue("id", object->id); - query.SetValue("other_id", value->id); - - for (Query &q : SQL->Replace(prefix + "edges", query, { "id", "field" })) - Run(q); - } - else - { - DoSet(object, field, true, nullptr); - - Query query("DELETE FROM `" + prefix + "edges` WHERE `id` = @id@ AND `field` = @field@"); - query.SetValue("id", object->id); - query.SetValue("field", field->GetName()); - Run(query); - } - - return EVENT_STOP; - } - - EventReturn OnSerializeUnset(Serialize::Object *object, Serialize::FieldBase *field) override - { - DoSet(object, field, false, nullptr); - field->CacheMiss(object); - return EVENT_STOP; - } - - EventReturn OnSerializeUnsetSerializable(Serialize::Object *object, Serialize::FieldBase *field) override - { - DoSet(object, field, true, nullptr); - field->CacheMiss(object); - - Query query("DELETE FROM `" + prefix + "edges` WHERE `id` = @id@ AND `field` = @field@"); - query.SetValue("id", object->id); - query.SetValue("field", field->GetName()); - Run(query); - - return EVENT_STOP; - } - - EventReturn OnSerializeHasField(Serialize::Object *object, Serialize::FieldBase *field) override - { - SQL::Result::Value v; - - return GetValue(object, field, v) && !v.null ? EVENT_STOP : EVENT_CONTINUE; - } - - EventReturn OnSerializableGetId(Serialize::ID &id) override - { - StartTransaction(); - - id = SQL->GetID(prefix); - return EVENT_ALLOW; - } - - void OnSerializableCreate(Serialize::Object *object) override - { - StartTransaction(); - - Query q = Query("INSERT INTO `" + prefix + "objects` (`id`,`type`) VALUES (@id@, @type@)"); - q.SetValue("id", object->id); - q.SetValue("type", object->GetSerializableType()->GetName()); - Run(q); - } - - void OnSerializableDelete(Serialize::Object *object) override - { - StartTransaction(); - - Query query("DELETE FROM `" + prefix + object->GetSerializableType()->GetName() + "` WHERE `id` = " + stringify(object->id)); - Run(query); - } -}; - -MODULE_INIT(DBMySQL) - diff --git a/modules/database/flatfile.cpp b/modules/database/flatfile.cpp new file mode 100644 index 000000000..122134960 --- /dev/null +++ b/modules/database/flatfile.cpp @@ -0,0 +1,206 @@ +/* + * (C) 2003-2014 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 "module.h" + +class DBFlatFile : public Module + , public EventHook + , public EventHook +{ + /* Day the last backup was on */ + int last_day; + /* Backup file names */ + std::map > backups; + bool loaded; + + void BackupDatabase() + { + tm *tm = localtime(&Anope::CurTime); + + if (tm->tm_mday != last_day) + { + last_day = tm->tm_mday; + + const std::map &types = Serialize::TypeBase::GetTypes(); + + std::set dbs; + dbs.insert(Config->GetModule(this)->Get("database", "anope.db")); + + for (const std::pair &p : types) + { + Serialize::TypeBase *stype = p.second; + + if (stype->GetOwner()) + dbs.insert("module_" + stype->GetOwner()->name + ".db"); + } + + + for (std::set::const_iterator it = dbs.begin(), it_end = dbs.end(); it != it_end; ++it) + { + 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); + + /* 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; + if (rename(oldname.c_str(), newname.c_str())) + { + Anope::string err = Anope::LastError(); + Log(this) << "Unable to back up database " << *it << " (" << err << ")!"; + + if (!Config->GetModule(this)->Get("nobackupokay")) + { + Anope::Quitting = true; + Anope::QuitReason = "Unable to back up database " + *it + " (" + err + ")"; + } + + continue; + } + + backups[*it].push_back(newname); + + unsigned keepbackups = Config->GetModule(this)->Get("keepbackups"); + if (keepbackups > 0 && backups[*it].size() > keepbackups) + { + unlink(backups[*it].front().c_str()); + backups[*it].pop_front(); + } + } + } + } + + public: + DBFlatFile(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR) + , last_day(0) + , loaded(false) + { + + } + + EventReturn OnLoadDatabase() override + { + const Anope::string &db_name = Anope::DataDir + "/" + Config->GetModule(this)->Get("database", "anope.db"); + + std::fstream fd(db_name.c_str(), std::ios_base::in | std::ios_base::binary); + if (!fd.is_open()) + { + Log(this) << "Unable to open " << db_name << " for reading!"; + return EVENT_STOP; + } + + Serialize::TypeBase *type = nullptr; + Serialize::Object *obj = nullptr; + for (Anope::string buf; std::getline(fd, buf.str());) + { + if (buf.find("OBJECT ") == 0) + { + Anope::string t = buf.substr(7); + if (obj) + Log(LOG_DEBUG) << "obj != null but got OBJECT"; + if (type) + Log(LOG_DEBUG) << "type != null but got OBJECT"; + type = Serialize::TypeBase::Find(t); + obj = nullptr; + } + else if (buf.find("ID ") == 0) + { + if (!type || obj) + continue; + + try + { + Serialize::ID id = convertTo(buf.substr(3)); + obj = type->Require(id); + } + catch (const ConvertException &) + { + Log(LOG_DEBUG) << "Unable to parse object id " << buf.substr(3); + } + } + else if (buf.find("DATA ") == 0) + { + if (!type) + continue; + + if (!obj) + obj = type->Create(); + + size_t sp = buf.find(' ', 5); // Skip DATA + if (sp == Anope::string::npos) + continue; + + Anope::string key = buf.substr(5, sp - 5), value = buf.substr(sp + 1); + + Serialize::FieldBase *field = type->GetField(key); + if (field) + field->UnserializeFromString(obj, value); + } + else if (buf.find("END") == 0) + { + type = nullptr; + obj = nullptr; + } + } + + fd.close(); + + loaded = true; + return EVENT_STOP; + } + + + void OnSaveDatabase() override + { + BackupDatabase(); + + Anope::string db_name = Anope::DataDir + "/" + Config->GetModule(this)->Get("database", "anope.db"); + + if (Anope::IsFile(db_name)) + rename(db_name.c_str(), (db_name + ".tmp").c_str()); + + std::fstream f(db_name.c_str(), std::ios_base::out | std::ios_base::trunc | std::ios_base::binary); + + if (!f.is_open()) + { + Log(this) << "Unable to open " << db_name << " for writing"; + } + else + { + for (std::pair p : Serialize::objects) + { + Serialize::Object *object = p.second; + Serialize::TypeBase *s_type = object->GetSerializableType(); + + f << "OBJECT " << s_type->GetName() << "\n"; + f << "ID " << object->id << "\n"; + for (Serialize::FieldBase *field : s_type->fields) + f << "DATA " << field->GetName() << " " << field->SerializeToString(object) << "\n"; + f << "END\n"; + } + } + + if (!f.is_open() || !f.good()) + { + f.close(); + rename((db_name + ".tmp").c_str(), db_name.c_str()); + } + else + { + f.close(); + unlink((db_name + ".tmp").c_str()); + } + } +}; + +MODULE_INIT(DBFlatFile) + + diff --git a/modules/database/old.cpp b/modules/database/old.cpp new file mode 100644 index 000000000..3d9b92922 --- /dev/null +++ b/modules/database/old.cpp @@ -0,0 +1,1428 @@ +/* + * (C) 2003-2014 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 "module.h" +#include "modules/os_session.h" +#include "modules/bs_kick.h" +#include "modules/cs_mode.h" +#include "modules/bs_badwords.h" +#include "modules/os_news.h" +#include "modules/os_forbid.h" +#include "modules/cs_entrymsg.h" +#include "modules/ns_suspend.h" +#include "modules/cs_suspend.h" +#include "modules/cs_access.h" +#include "modules/ns_access.h" + +#define READ(x) \ +if (true) \ +{ \ + if ((x) < 0) \ + printf("Error, the database is broken, line %d, trying to continue... no guarantee.\n", __LINE__); \ +} \ +else \ + static_cast(0) + +#define getc_db(f) (fgetc((f)->fp)) +#define read_db(f, buf, len) (fread((buf), 1, (len), (f)->fp)) +#define read_buffer(buf, f) (read_db((f), (buf), sizeof(buf)) == sizeof(buf)) + +#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_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 */ +#define OLD_NI_KILL_IMMED 0x00000800 /* Kill immediately instead of in 60 sec */ +#define OLD_NI_MEMO_MAIL 0x00010000 /* User gets email on memo */ +#define OLD_NI_HIDE_STATUS 0x00020000 /* Don't show services access status */ +#define OLD_NI_SUSPENDED 0x00040000 /* Nickname is suspended */ +#define OLD_NI_AUTOOP 0x00080000 /* Autoop nickname in channels */ + +#define OLD_NS_NO_EXPIRE 0x0004 /* nick won't expire */ +#define OLD_NS_VERBOTEN 0x0002 + +#define OLD_CI_KEEPTOPIC 0x00000001 +#define OLD_CI_SECUREOPS 0x00000002 +#define OLD_CI_PRIVATE 0x00000004 +#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 +#define OLD_CI_MEMO_HARDMAX 0x00000400 +#define OLD_CI_OPNOTICE 0x00000800 +#define OLD_CI_SECUREFOUNDER 0x00001000 +#define OLD_CI_SIGNKICK 0x00002000 +#define OLD_CI_SIGNKICK_LEVEL 0x00004000 +#define OLD_CI_XOP 0x00008000 +#define OLD_CI_SUSPENDED 0x00010000 + +/* BotServ SET flags */ +#define OLD_BS_DONTKICKOPS 0x00000001 +#define OLD_BS_DONTKICKVOICES 0x00000002 +#define OLD_BS_FANTASY 0x00000004 +#define OLD_BS_SYMBIOSIS 0x00000008 +#define OLD_BS_GREET 0x00000010 +#define OLD_BS_NOBOT 0x00000020 + +/* BotServ Kickers flags */ +#define OLD_BS_KICK_BOLDS 0x80000000 +#define OLD_BS_KICK_COLORS 0x40000000 +#define OLD_BS_KICK_REVERSES 0x20000000 +#define OLD_BS_KICK_UNDERLINES 0x10000000 +#define OLD_BS_KICK_BADWORDS 0x08000000 +#define OLD_BS_KICK_CAPS 0x04000000 +#define OLD_BS_KICK_FLOOD 0x02000000 +#define OLD_BS_KICK_REPEAT 0x01000000 + +#define OLD_NEWS_LOGON 0 +#define OLD_NEWS_OPER 1 +#define OLD_NEWS_RANDOM 2 + +enum +{ + TTB_BOLDS, + TTB_COLORS, + TTB_REVERSES, + TTB_UNDERLINES, + TTB_BADWORDS, + TTB_CAPS, + TTB_FLOOD, + TTB_REPEAT, +}; + +static struct mlock_info +{ + char c; + uint32_t m; +} mlock_infos[] = { + {'i', 0x00000001}, + {'m', 0x00000002}, + {'n', 0x00000004}, + {'p', 0x00000008}, + {'s', 0x00000010}, + {'t', 0x00000020}, + {'R', 0x00000100}, + {'r', 0x00000200}, + {'c', 0x00000400}, + {'A', 0x00000800}, + {'K', 0x00002000}, + {'O', 0x00008000}, + {'Q', 0x00010000}, + {'S', 0x00020000}, + {'G', 0x00100000}, + {'C', 0x00200000}, + {'u', 0x00400000}, + {'z', 0x00800000}, + {'N', 0x01000000}, + {'M', 0x04000000} +}; + +static Anope::string hashm; + +enum +{ + LANG_EN_US, /* United States English */ + LANG_JA_JIS, /* Japanese (JIS encoding) */ + LANG_JA_EUC, /* Japanese (EUC encoding) */ + LANG_JA_SJIS, /* Japanese (SJIS encoding) */ + LANG_ES, /* Spanish */ + LANG_PT, /* Portugese */ + LANG_FR, /* French */ + LANG_TR, /* Turkish */ + LANG_IT, /* Italian */ + LANG_DE, /* German */ + LANG_CAT, /* Catalan */ + LANG_GR, /* Greek */ + LANG_NL, /* Dutch */ + LANG_RU, /* Russian */ + LANG_HUN, /* Hungarian */ + LANG_PL /* Polish */ +}; + +static void process_mlock(ChanServ::Channel *ci, uint32_t lock, bool status, uint32_t *limit, Anope::string *key) +{ + if (!mlocks) + return; + + for (unsigned i = 0; i < (sizeof(mlock_infos) / sizeof(mlock_info)); ++i) + if (lock & mlock_infos[i].m) + { + ChannelMode *cm = ModeManager::FindChannelModeByChar(mlock_infos[i].c); + if (cm) + { + if (limit && mlock_infos[i].c == 'l') + mlocks->SetMLock(ci, cm, status, stringify(*limit)); + else if (key && mlock_infos[i].c == 'k') + mlocks->SetMLock(ci, cm, status, *key); + else + mlocks->SetMLock(ci, cm, status); + } + } +} + +static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +static void my_b64_encode(const Anope::string &src, Anope::string &target) +{ + size_t src_pos = 0, src_len = src.length(); + unsigned char input[3]; + + target.clear(); + + while (src_len - src_pos > 2) + { + input[0] = src[src_pos++]; + input[1] = src[src_pos++]; + input[2] = src[src_pos++]; + + target += Base64[input[0] >> 2]; + target += Base64[((input[0] & 0x03) << 4) + (input[1] >> 4)]; + target += Base64[((input[1] & 0x0f) << 2) + (input[2] >> 6)]; + target += Base64[input[2] & 0x3f]; + } + + /* Now we worry about padding */ + if (src_pos != src_len) + { + input[0] = input[1] = input[2] = 0; + for (size_t i = 0; i < src_len - src_pos; ++i) + input[i] = src[src_pos + i]; + + target += Base64[input[0] >> 2]; + target += Base64[((input[0] & 0x03) << 4) + (input[1] >> 4)]; + if (src_pos == src_len - 1) + target += Pad64; + else + target += Base64[((input[1] & 0x0f) << 2) + (input[2] >> 6)]; + target += Pad64; + } +} + +static Anope::string Hex(const char *data, size_t l) +{ + const char hextable[] = "0123456789abcdef"; + + std::string rv; + for (size_t i = 0; i < l; ++i) + { + unsigned char c = data[i]; + rv += hextable[c >> 4]; + rv += hextable[c & 0xF]; + } + return rv; +} + +static Anope::string GetLevelName(int level) +{ + switch (level) + { + case 0: + return "INVITE"; + case 1: + return "AKICK"; + case 2: + return "SET"; + case 3: + return "UNBAN"; + case 4: + return "AUTOOP"; + case 5: + return "AUTODEOP"; + case 6: + return "AUTOVOICE"; + case 7: + return "OP"; + case 8: + return "ACCESS_LIST"; + case 9: + return "CLEAR"; + case 10: + return "NOJOIN"; + case 11: + return "ACCESS_CHANGE"; + case 12: + return "MEMO"; + case 13: + return "ASSIGN"; + case 14: + return "BADWORDS"; + case 15: + return "NOKICK"; + case 16: + return "FANTASIA"; + case 17: + return "SAY"; + case 18: + return "GREET"; + case 19: + return "VOICEME"; + case 20: + return "VOICE"; + case 21: + return "GETKEY"; + case 22: + return "AUTOHALFOP"; + case 23: + return "AUTOPROTECT"; + case 24: + return "OPME"; + case 25: + return "HALFOPME"; + case 26: + return "HALFOP"; + case 27: + return "PROTECTME"; + case 28: + return "PROTECT"; + case 29: + return "KICKME"; + case 30: + return "KICK"; + case 31: + return "SIGNKICK"; + case 32: + return "BANME"; + case 33: + return "BAN"; + case 34: + return "TOPIC"; + case 35: + return "INFO"; + default: + return "INVALID"; + } +} + +static char *strscpy(char *d, const char *s, size_t len) +{ + char *d_orig = d; + + if (!len) + return d; + while (--len && (*d++ = *s++)); + *d = '\0'; + return d_orig; +} + +struct dbFILE +{ + int mode; /* 'r' for reading, 'w' for writing */ + FILE *fp; /* The normal file descriptor */ + char filename[1024]; /* Name of the database file */ +}; + +static dbFILE *open_db_read(const char *service, const char *filename, int version) +{ + dbFILE *f; + FILE *fp; + int myversion; + + f = new dbFILE; + strscpy(f->filename, (Anope::DataDir + "/" + filename).c_str(), sizeof(f->filename)); + f->mode = 'r'; + fp = fopen(f->filename, "rb"); + if (!fp) + { + Log() << "Can't read " << service << " database " << f->filename; + delete f; + return NULL; + } + f->fp = fp; + myversion = fgetc(fp) << 24 | fgetc(fp) << 16 | fgetc(fp) << 8 | fgetc(fp); + if (feof(fp)) + { + Log() << "Error reading version number on " << f->filename << ": End of file detected."; + delete f; + return NULL; + } + else if (myversion < version) + { + Log() << "Unsuported database version (" << myversion << ") on " << f->filename << "."; + delete f; + return NULL; + } + return f; +} + +void close_db(dbFILE *f) +{ + fclose(f->fp); + delete f; +} + +static int read_int16(int16_t *ret, dbFILE *f) +{ + int c1, c2; + + *ret = 0; + + c1 = fgetc(f->fp); + c2 = fgetc(f->fp); + if (c1 == EOF || c2 == EOF) + return -1; + *ret = c1 << 8 | c2; + return 0; +} + +static int read_uint16(uint16_t *ret, dbFILE *f) +{ + int c1, c2; + + *ret = 0; + + c1 = fgetc(f->fp); + c2 = fgetc(f->fp); + if (c1 == EOF || c2 == EOF) + return -1; + *ret = c1 << 8 | c2; + return 0; +} + +static int read_string(Anope::string &str, dbFILE *f) +{ + str.clear(); + uint16_t len; + + if (read_uint16(&len, f) < 0) + return -1; + if (len == 0) + return 0; + char *s = new char[len]; + if (len != fread(s, 1, len, f->fp)) + { + delete [] s; + return -1; + } + str = s; + delete [] s; + return 0; +} + +static int read_uint32(uint32_t *ret, dbFILE *f) +{ + int c1, c2, c3, c4; + + *ret = 0; + + c1 = fgetc(f->fp); + c2 = fgetc(f->fp); + c3 = fgetc(f->fp); + c4 = fgetc(f->fp); + if (c1 == EOF || c2 == EOF || c3 == EOF || c4 == EOF) + return -1; + *ret = c1 << 24 | c2 << 16 | c3 << 8 | c4; + return 0; +} + +int read_int32(int32_t *ret, dbFILE *f) +{ + int c1, c2, c3, c4; + + *ret = 0; + + c1 = fgetc(f->fp); + c2 = fgetc(f->fp); + c3 = fgetc(f->fp); + c4 = fgetc(f->fp); + if (c1 == EOF || c2 == EOF || c3 == EOF || c4 == EOF) + return -1; + *ret = c1 << 24 | c2 << 16 | c3 << 8 | c4; + return 0; +} + +static void LoadNicks() +{ + if (!NickServ::service) + return; + dbFILE *f = open_db_read("NickServ", "nick.db", 14); + if (f == NULL) + return; + for (int i = 0; i < 1024; ++i) + for (int c; (c = getc_db(f)) == 1;) + { + Anope::string buffer; + + READ(read_string(buffer, f)); + + NickServ::Account *nc = NickServ::account.Create(); + nc->SetDisplay(buffer); + + const Anope::string settings[] = { "killprotect", "kill_quick", "ns_secure", "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->UnsetS(settings[j].upper()); + + char pwbuf[32]; + READ(read_buffer(pwbuf, f)); + if (hashm == "plain") + { + Anope::string p; + my_b64_encode(pwbuf, p); + nc->SetPassword(p); + } + else if (hashm == "md5" || hashm == "oldmd5") + nc->SetPassword(Hex(pwbuf, 16)); + else if (hashm == "sha1") + nc->SetPassword(Hex(pwbuf, 20)); + else + nc->SetPassword(Hex(pwbuf, strlen(pwbuf))); + nc->SetPassword(hashm + ":" + nc->GetPassword()); + + READ(read_string(buffer, f)); + nc->SetEmail(buffer); + + READ(read_string(buffer, f)); + if (!buffer.empty()) + nc->Extend("greet", buffer); + + uint32_t u32; + READ(read_uint32(&u32, f)); + //nc->icq = u32; + + READ(read_string(buffer, f)); + //nc->url = buffer; + + READ(read_uint32(&u32, f)); + if (u32 & OLD_NI_KILLPROTECT) + nc->SetS("KILLPROTECT", true); + if (u32 & OLD_NI_SECURE) + nc->SetS("NS_SECURE", true); + if (u32 & OLD_NI_MSG) + nc->SetS("MSG", true); + if (u32 & OLD_NI_MEMO_HARDMAX) + nc->SetS("MEMO_HARDMAX", true); + if (u32 & OLD_NI_MEMO_SIGNON) + nc->SetS("MEMO_SIGNON", true); + if (u32 & OLD_NI_MEMO_RECEIVE) + nc->SetS("MEMO_RECEIVE", true); + if (u32 & OLD_NI_PRIVATE) + nc->SetS("NS_PRIVATE", true); + if (u32 & OLD_NI_HIDE_EMAIL) + nc->SetS("HIDE_EMAIL", true); + if (u32 & OLD_NI_HIDE_MASK) + nc->SetS("HIDE_MASK", true); + if (u32 & OLD_NI_HIDE_QUIT) + nc->SetS("HIDE_QUIT", true); + if (u32 & OLD_NI_KILL_QUICK) + nc->SetS("KILL_QUICK", true); + if (u32 & OLD_NI_KILL_IMMED) + nc->SetS("KILL_IMMED", true); + if (u32 & OLD_NI_MEMO_MAIL) + nc->SetS("MEMO_MAIL", true); + if (u32 & OLD_NI_HIDE_STATUS) + nc->SetS("HIDE_STATUS", true); + if (u32 & OLD_NI_SUSPENDED) + { + if (nssuspendinfo) + { + NSSuspendInfo *si = nssuspendinfo.Create(); + si->SetAccount(nc); + } + } + if (!(u32 & OLD_NI_AUTOOP)) + nc->SetS("AUTOOP", true); + + uint16_t u16; + READ(read_uint16(&u16, f)); + switch (u16) + { + case LANG_ES: + nc->SetLanguage("es_ES"); + break; + case LANG_PT: + nc->SetLanguage("pt_PT"); + break; + case LANG_FR: + nc->SetLanguage("fr_FR"); + break; + case LANG_TR: + nc->SetLanguage("tr_TR"); + break; + case LANG_IT: + nc->SetLanguage("it_IT"); + break; + case LANG_DE: + nc->SetLanguage("de_DE"); + break; + case LANG_CAT: + nc->SetLanguage("ca_ES"); // yes, iso639 defines catalan as CA + break; + case LANG_GR: + nc->SetLanguage("el_GR"); + break; + case LANG_NL: + nc->SetLanguage("nl_NL"); + break; + case LANG_RU: + nc->SetLanguage("ru_RU"); + break; + case LANG_HUN: + nc->SetLanguage("hu_HU"); + break; + case LANG_PL: + nc->SetLanguage("pl_PL"); + break; + case LANG_EN_US: + case LANG_JA_JIS: + case LANG_JA_EUC: + case LANG_JA_SJIS: // these seem to be unused + default: + nc->SetLanguage("en"); + } + + READ(read_uint16(&u16, f)); + for (uint16_t j = 0; j < u16; ++j) + { + READ(read_string(buffer, f)); + + if (nsaccess) + { + NickAccess *a = nsaccess.Create(); + a->SetAccount(nc); + a->SetMask(buffer); + } + } + + int16_t i16; + READ(read_int16(&i16, f)); + READ(read_int16(&i16, f)); + MemoServ::MemoInfo *mi = nc->GetMemos(); + if (mi) + mi->SetMemoMax(i16); + for (int16_t j = 0; j < i16; ++j) + { + MemoServ::Memo *m = MemoServ::service ? MemoServ::service->CreateMemo() : nullptr; + READ(read_uint32(&u32, f)); + uint16_t flags; + READ(read_uint16(&flags, f)); + int32_t tmp32; + READ(read_int32(&tmp32, f)); + if (m) + m->SetTime(tmp32); + char sbuf[32]; + READ(read_buffer(sbuf, f)); + if (m) + m->SetSender(sbuf); + Anope::string text; + READ(read_string(text, f)); + if (m) + m->SetText(text); + } + READ(read_uint16(&u16, f)); + READ(read_int16(&i16, f)); + + Log(LOG_DEBUG) << "Loaded NickServ::Account " << nc->GetDisplay(); + } + + for (int i = 0; i < 1024; ++i) + for (int c; (c = getc_db(f)) == 1;) + { + Anope::string nick, last_usermask, last_realname, last_quit; + time_t time_registered, last_seen; + + READ(read_string(nick, f)); + READ(read_string(last_usermask, f)); + READ(read_string(last_realname, f)); + READ(read_string(last_quit, f)); + + int32_t tmp32; + READ(read_int32(&tmp32, f)); + time_registered = tmp32; + READ(read_int32(&tmp32, f)); + last_seen = tmp32; + + uint16_t tmpu16; + READ(read_uint16(&tmpu16, f)); + + Anope::string core; + READ(read_string(core, f)); + NickServ::Account *nc = NickServ::FindAccount(core); + if (nc == NULL) + { + Log() << "Skipping coreless nick " << nick << " with core " << core; + continue; + } + + if (tmpu16 & OLD_NS_VERBOTEN) + { + if (!forbiddata) + { + delete nc; + continue; + } + + if (nc->GetDisplay().find_first_of("?*") != Anope::string::npos) + { + delete nc; + continue; + } + + ForbidData *d = forbiddata.Create(); + d->SetMask(nc->GetDisplay()); + d->SetCreator(last_usermask); + d->SetReason(last_realname); + d->SetType(FT_NICK); + delete nc; + continue; + } + + NickServ::Nick *na = NickServ::nick.Create(); + na->SetNick(nick); + na->SetAccount(nc); + na->SetLastUsermask(last_usermask); + na->SetLastRealname(last_realname); + na->SetLastQuit(last_quit); + na->SetTimeRegistered(time_registered); + na->SetLastSeen(last_seen); + + if (tmpu16 & OLD_NS_NO_EXPIRE) + na->SetS("NS_NO_EXPIRE", true); + + Log(LOG_DEBUG) << "Loaded NickServ::Nick " << na->GetNick(); + } + + close_db(f); /* End of section Ia */ +} + +static void LoadVHosts() +{ + dbFILE *f = open_db_read("HostServ", "hosts.db", 3); + if (f == NULL) + return; + + for (int c; (c = getc_db(f)) == 1;) + { + Anope::string nick, ident, host, creator; + int32_t vtime; + + READ(read_string(nick, f)); + READ(read_string(ident, f)); + READ(read_string(host, f)); + READ(read_string(creator, f)); + READ(read_int32(&vtime, f)); + + NickServ::Nick *na = NickServ::FindNick(nick); + if (na == NULL) + { + Log() << "Removing vhost for non-existent nick " << nick; + continue; + } + + na->SetVhost(ident, host, creator, vtime); + + Log() << "Loaded vhost for " << na->GetNick(); + } + + close_db(f); +} + +static void LoadBots() +{ + dbFILE *f = open_db_read("Botserv", "bot.db", 10); + if (f == NULL) + return; + + for (int c; (c = getc_db(f)) == 1;) + { + Anope::string nick, user, host, real; + int16_t flags, chancount; + int32_t created; + + READ(read_string(nick, f)); + READ(read_string(user, f)); + READ(read_string(host, f)); + READ(read_string(real, f)); + READ(read_int16(&flags, f)); + READ(read_int32(&created, f)); + READ(read_int16(&chancount, f)); + + ServiceBot *bi = ServiceBot::Find(nick, true); + //XXX + // if (!bi) + // bi = new ServiceBot(nick, user, host, real); + bi->bi->SetCreated(created); + + if (flags & OLD_BI_PRIVATE) + bi->bi->SetOperOnly(true); + + Log(LOG_DEBUG) << "Loaded bot " << bi->nick; + } + + close_db(f); +} + +static void LoadChannels() +{ + if (!ChanServ::service) + return; + + ServiceReference forbid("ForbidService", "forbid"); + dbFILE *f = open_db_read("ChanServ", "chan.db", 16); + if (f == NULL) + return; + + for (int i = 0; i < 256; ++i) + for (int c; (c = getc_db(f)) == 1;) + { + Anope::string buffer; + char namebuf[64]; + READ(read_buffer(namebuf, f)); + ChanServ::Channel *ci = ChanServ::channel.Create(); + ci->SetName(namebuf); + + const Anope::string settings[] = { "keeptopic", "peace", "cs_private", "restricted", "cs_secure", "secureops", "securefounder", + "signkick", "signkick_level", "topiclock", "persist", "noautoop", "cs_keepmodes" }; + for (unsigned j = 0; j < sizeof(settings) / sizeof(Anope::string); ++j) + ci->UnsetS(settings[j].upper()); + + READ(read_string(buffer, f)); + ci->SetFounder(NickServ::FindAccount(buffer)); + + READ(read_string(buffer, f)); + ci->SetSuccessor(NickServ::FindAccount(buffer)); + + char pwbuf[32]; + READ(read_buffer(pwbuf, f)); + + Anope::string desc; + READ(read_string(desc, f)); + ci->SetDesc(desc); + READ(read_string(buffer, f)); + READ(read_string(buffer, f)); + + int32_t tmp32; + READ(read_int32(&tmp32, f)); + ci->SetTimeRegistered(tmp32); + + READ(read_int32(&tmp32, f)); + ci->SetLastUsed(tmp32); + + Anope::string last_topic; + READ(read_string(last_topic, f)); + ci->SetLastTopic(last_topic); + + READ(read_buffer(pwbuf, f)); + ci->SetLastTopicSetter(pwbuf); + + READ(read_int32(&tmp32, f)); + ci->SetLastTopicTime(tmp32); + + uint32_t tmpu32; + READ(read_uint32(&tmpu32, f)); + // Temporary flags cleanup + tmpu32 &= ~0x80000000; + if (tmpu32 & OLD_CI_KEEPTOPIC) + ci->SetS("KEEPTOPIC", true); + if (tmpu32 & OLD_CI_SECUREOPS) + ci->SetS("SECUREOPS", true); + if (tmpu32 & OLD_CI_PRIVATE) + ci->SetS("CS_PRIVATE", true); + if (tmpu32 & OLD_CI_TOPICLOCK) + ci->SetS("TOPICLOCK", true); + if (tmpu32 & OLD_CI_RESTRICTED) + ci->SetS("RESTRICTED", true); + if (tmpu32 & OLD_CI_PEACE) + ci->SetS("PEACE", true); + if (tmpu32 & OLD_CI_SECURE) + ci->SetS("CS_SECURE", true); + if (tmpu32 & OLD_CI_NO_EXPIRE) + ci->SetS("CS_NO_EXPIRE", true); + if (tmpu32 & OLD_CI_MEMO_HARDMAX) + ci->SetS("MEMO_HARDMAX", true); + if (tmpu32 & OLD_CI_SECUREFOUNDER) + ci->SetS("SECUREFOUNDER", true); + if (tmpu32 & OLD_CI_SIGNKICK) + ci->SetS("SIGNKICK", true); + if (tmpu32 & OLD_CI_SIGNKICK_LEVEL) + ci->SetS("SIGNKICK_LEVEL", true); + + Anope::string forbidby, forbidreason; + READ(read_string(forbidby, f)); + READ(read_string(forbidreason, f)); + if (tmpu32 & OLD_CI_SUSPENDED) + { + if (cssuspendinfo) + { + CSSuspendInfo *si = cssuspendinfo.Create(); + si->SetChannel(ci); + si->SetBy(forbidby); + } + } + bool forbid_chan = tmpu32 & OLD_CI_VERBOTEN; + + int16_t tmp16; + READ(read_int16(&tmp16, f)); + ci->SetBanType(tmp16); + + READ(read_int16(&tmp16, f)); + if (tmp16 > 36) + tmp16 = 36; + for (int16_t j = 0; j < tmp16; ++j) + { + int16_t level; + READ(read_int16(&level, f)); + + if (level == ChanServ::ACCESS_INVALID) + level = ChanServ::ACCESS_FOUNDER; + + if (j == 10 && level < 0) // NOJOIN + ci->UnsetS("RESTRICTED"); // If CSDefRestricted was enabled this can happen + + ci->SetLevel(GetLevelName(j), level); + } + + bool xop = tmpu32 & OLD_CI_XOP; + uint16_t tmpu16; + READ(read_uint16(&tmpu16, f)); + for (uint16_t j = 0; j < tmpu16; ++j) + { + uint16_t in_use; + READ(read_uint16(&in_use, f)); + if (in_use) + { + ChanServ::ChanAccess *access = NULL; + + if (xop) + { + if (xopchanaccess) + access = xopchanaccess.Create(); + } + else + { + if (accesschanaccess) + access = accesschanaccess.Create(); + } + + if (access) + access->SetChannel(ci); + + int16_t level; + READ(read_int16(&level, f)); + if (access) + { + if (xop) + { + switch (level) + { + case 3: + access->AccessUnserialize("VOP"); + break; + case 4: + access->AccessUnserialize("HOP"); + break; + case 5: + access->AccessUnserialize("AOP"); + break; + case 10: + access->AccessUnserialize("SOP"); + break; + } + } + else + access->AccessUnserialize(stringify(level)); + } + + Anope::string mask; + READ(read_string(mask, f)); + if (access) + { + access->SetMask(mask); + NickServ::Nick *na = NickServ::FindNick(mask); + if (na) + na->SetAccount(na->GetAccount()); + } + + READ(read_int32(&tmp32, f)); + if (access) + { + access->SetLastSeen(tmp32); + access->SetCreator("Unknown"); + access->SetCreated(Anope::CurTime); + } + } + } + + READ(read_uint16(&tmpu16, f)); + for (uint16_t j = 0; j < tmpu16; ++j) + { + uint16_t flags; + READ(read_uint16(&flags, f)); + if (flags & 0x0001) + { + Anope::string mask, reason, creator; + READ(read_string(mask, f)); + READ(read_string(reason, f)); + READ(read_string(creator, f)); + READ(read_int32(&tmp32, f)); + + ci->AddAkick(creator, mask, reason, tmp32); + } + } + + READ(read_uint32(&tmpu32, f)); // mlock on + ci->Extend("mlock_on", tmpu32); + READ(read_uint32(&tmpu32, f)); // mlock off + ci->Extend("mlock_off", tmpu32); + READ(read_uint32(&tmpu32, f)); // mlock limit + ci->Extend("mlock_limit", tmpu32); + READ(read_string(buffer, f)); // key + ci->Extend("mlock_key", buffer); + READ(read_string(buffer, f)); // +f + READ(read_string(buffer, f)); // +L + + READ(read_int16(&tmp16, f)); + READ(read_int16(&tmp16, f)); + MemoServ::MemoInfo *mi = ci->GetMemos(); + if (mi) + mi->SetMemoMax(tmp16); + for (int16_t j = 0; j < tmp16; ++j) + { + READ(read_uint32(&tmpu32, f)); + READ(read_uint16(&tmpu16, f)); + MemoServ::Memo *m = MemoServ::service ? MemoServ::service->CreateMemo() : nullptr; + READ(read_int32(&tmp32, f)); + if (m) + m->SetTime(tmp32); + char sbuf[32]; + READ(read_buffer(sbuf, f)); + if (m) + m->SetSender(sbuf); + Anope::string text; + READ(read_string(text, f)); + if (m) + m->SetText(text); + } + + READ(read_string(buffer, f)); + if (!buffer.empty()) + { + if (entrymsg) + { + EntryMsg *e = entrymsg.Create(); + e->SetChannel(ci); + e->SetCreator("Unknown"); + e->SetMessage(buffer); + e->SetWhen(Anope::CurTime); + } + } + + READ(read_string(buffer, f)); + ci->SetBot(ServiceBot::Find(buffer, true)); + + READ(read_int32(&tmp32, f)); + if (tmp32 & OLD_BS_DONTKICKOPS) + ci->SetS("BS_DONTKICKOPS", true); + if (tmp32 & OLD_BS_DONTKICKVOICES) + ci->SetS("BS_DONTKICKVOICES", true); + if (tmp32 & OLD_BS_FANTASY) + ci->SetS("BS_FANTASY", true); + if (tmp32 & OLD_BS_GREET) + ci->SetS("BS_GREET", true); + if (tmp32 & OLD_BS_NOBOT) + ci->SetS("BS_NOBOT", true); + + KickerData *kd = GetKickerData(ci); + if (kd) + { + kd->SetBolds(tmp32 & OLD_BS_KICK_BOLDS); + kd->SetColors(tmp32 & OLD_BS_KICK_COLORS); + kd->SetReverses(tmp32 & OLD_BS_KICK_REVERSES); + kd->SetUnderlines(tmp32 & OLD_BS_KICK_UNDERLINES); + kd->SetBadwords(tmp32 & OLD_BS_KICK_BADWORDS); + kd->SetCaps(tmp32 & OLD_BS_KICK_CAPS); + kd->SetFlood(tmp32 & OLD_BS_KICK_FLOOD); + kd->SetRepeat(tmp32 & OLD_BS_KICK_REPEAT); + } + + READ(read_int16(&tmp16, f)); + for (int16_t j = 0; j < tmp16; ++j) + { + int16_t ttb; + READ(read_int16(&ttb, f)); + switch (j) + { + case TTB_BOLDS: + kd->SetTTBBolds(ttb); + break; + case TTB_COLORS: + kd->SetTTBColors(ttb); + break; + case TTB_REVERSES: + kd->SetTTBReverses(ttb); + break; + case TTB_UNDERLINES: + kd->SetTTBUnderlines(ttb); + break; + case TTB_BADWORDS: + kd->SetTTBBadwords(ttb); + break; + case TTB_CAPS: + kd->SetTTBCaps(ttb); + break; + case TTB_FLOOD: + kd->SetTTBFlood(ttb); + break; + case TTB_REPEAT: + kd->SetTTBRepeat(ttb); + break; + } + } + + READ(read_int16(&tmp16, f)); + if (kd) + kd->SetCapsMin(tmp16); + READ(read_int16(&tmp16, f)); + if (kd) + kd->SetCapsPercent(tmp16); + READ(read_int16(&tmp16, f)); + if (kd) + kd->SetFloodLines(tmp16); + READ(read_int16(&tmp16, f)); + if (kd) + kd->SetFloodSecs(tmp16); + READ(read_int16(&tmp16, f)); + if (kd) + kd->SetRepeatTimes(tmp16); + + READ(read_uint16(&tmpu16, f)); + for (uint16_t j = 0; j < tmpu16; ++j) + { + uint16_t in_use; + READ(read_uint16(&in_use, f)); + if (in_use) + { + READ(read_string(buffer, f)); + uint16_t type; + READ(read_uint16(&type, f)); + + BadWordType bwtype = BW_ANY; + if (type == 1) + bwtype = BW_SINGLE; + else if (type == 2) + bwtype = BW_START; + else if (type == 3) + bwtype = BW_END; + + if (badwords) + badwords->AddBadWord(ci, buffer, bwtype); + } + } + + if (forbid_chan) + { + if (!forbiddata) + { + delete ci; + continue; + } + + if (ci->GetName().find_first_of("?*") != Anope::string::npos) + { + delete ci; + continue; + } + + ForbidData *d = forbiddata.Create(); + d->SetMask(ci->GetName()); + d->SetCreator(forbidby); + d->SetReason(forbidreason); + d->SetType(FT_CHAN); + delete ci; + continue; + } + + Log(LOG_DEBUG) << "Loaded channel " << ci->GetName(); + } + + close_db(f); +} + +static void LoadOper() +{ + dbFILE *f = open_db_read("OperServ", "oper.db", 13); + if (f == NULL) + return; + + XLineManager *akill, *sqline, *snline, *szline; + akill = sqline = snline = szline = NULL; + + for (XLineManager *xl : XLineManager::XLineManagers) + { + if (xl->Type() == 'G') + akill = xl; + else if (xl->Type() == 'Q') + sqline = xl; + else if (xl->Type() == 'N') + snline = xl; + else if (xl->Type() == 'Z') + szline = xl; + } + + int32_t tmp32; + READ(read_int32(&tmp32, f)); + READ(read_int32(&tmp32, f)); + + int16_t capacity; + read_int16(&capacity, f); // AKill count + for (int16_t i = 0; i < capacity; ++i) + { + Anope::string user, host, by, reason; + int32_t seton, expires; + + READ(read_string(user, f)); + READ(read_string(host, f)); + READ(read_string(by, f)); + READ(read_string(reason, f)); + READ(read_int32(&seton, f)); + READ(read_int32(&expires, f)); + + if (!akill) + continue; + + XLine *x = new XLine(user + "@" + host, by, expires, reason, XLineManager::GenerateUID()); + x->SetCreated(seton); + akill->AddXLine(x); + } + + read_int16(&capacity, f); // SNLines + for (int16_t i = 0; i < capacity; ++i) + { + Anope::string mask, by, reason; + int32_t seton, expires; + + READ(read_string(mask, f)); + READ(read_string(by, f)); + READ(read_string(reason, f)); + READ(read_int32(&seton, f)); + READ(read_int32(&expires, f)); + + if (!snline) + continue; + + XLine *x = new XLine(mask, by, expires, reason, XLineManager::GenerateUID()); + x->SetCreated(seton); + snline->AddXLine(x); + } + + read_int16(&capacity, f); // SQLines + for (int16_t i = 0; i < capacity; ++i) + { + Anope::string mask, by, reason; + int32_t seton, expires; + + READ(read_string(mask, f)); + READ(read_string(by, f)); + READ(read_string(reason, f)); + READ(read_int32(&seton, f)); + READ(read_int32(&expires, f)); + + if (!sqline) + continue; + + XLine *x = new XLine(mask, by, expires, reason, XLineManager::GenerateUID()); + x->SetCreated(seton); + sqline->AddXLine(x); + } + + read_int16(&capacity, f); // SZLines + for (int16_t i = 0; i < capacity; ++i) + { + Anope::string mask, by, reason; + int32_t seton, expires; + + READ(read_string(mask, f)); + READ(read_string(by, f)); + READ(read_string(reason, f)); + READ(read_int32(&seton, f)); + READ(read_int32(&expires, f)); + + if (!szline) + continue; + + XLine *x = new XLine(mask, by, expires, reason, XLineManager::GenerateUID()); + x->SetCreated(seton); + szline->AddXLine(x); + } + + close_db(f); +} + +static void LoadExceptions() +{ + if (!session_service) + return; + + dbFILE *f = open_db_read("OperServ", "exception.db", 9); + if (f == NULL) + return; + + int16_t num; + READ(read_int16(&num, f)); + for (int i = 0; i < num; ++i) + { + Anope::string mask, reason; + int16_t limit; + char who[32]; + int32_t time, expires; + + READ(read_string(mask, f)); + READ(read_int16(&limit, f)); + READ(read_buffer(who, f)); + READ(read_string(reason, f)); + READ(read_int32(&time, f)); + READ(read_int32(&expires, f)); + + if (exception && session_service) + { + Exception *e = exception.Create(); + e->SetMask(mask); + e->SetLimit(limit); + e->SetWho(who); + e->SetTime(time); + e->SetExpires(expires); + e->SetReason(reason); + } + } + + close_db(f); +} + +static void LoadNews() +{ + if (!newsitem) + return; + + dbFILE *f = open_db_read("OperServ", "news.db", 9); + + if (f == NULL) + return; + + int16_t n; + READ(read_int16(&n, f)); + + for (int16_t i = 0; i < n; i++) + { + int16_t type; + NewsItem *ni = newsitem.Create(); + + READ(read_int16(&type, f)); + + switch (type) + { + case OLD_NEWS_LOGON: + ni->SetNewsType(NEWS_LOGON); + break; + case OLD_NEWS_OPER: + ni->SetNewsType(NEWS_OPER); + break; + case OLD_NEWS_RANDOM: + ni->SetNewsType(NEWS_RANDOM); + break; + } + + int32_t unused; + READ(read_int32(&unused, f)); + + Anope::string text; + READ(read_string(text, f)); + ni->SetText(text); + + char who[32]; + READ(read_buffer(who, f)); + ni->SetWho(who); + + int32_t tmp; + READ(read_int32(&tmp, f)); + ni->SetTime(tmp); + } + + close_db(f); +} + +class DBOld : public Module + , public EventHook + , public EventHook +{ + ExtensibleItem mlock_on, mlock_off, mlock_limit; + ExtensibleItem mlock_key; + + 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") + { + hashm = Config->GetModule(this)->Get("hash"); + + if (hashm != "md5" && hashm != "oldmd5" && hashm != "sha1" && hashm != "plain" && hashm != "sha256") + throw ModuleException("Invalid hash method"); + } + + EventReturn OnLoadDatabase() override + { + LoadNicks(); + LoadVHosts(); + LoadBots(); + LoadChannels(); + LoadOper(); + LoadExceptions(); + LoadNews(); + + return EVENT_STOP; + } + + void OnUplinkSync(Server *s) override + { + if (!ChanServ::service) + return; + for (auto& it : ChanServ::service->GetChannels()) + { + ChanServ::Channel *ci = it.second; + uint32_t *limit = mlock_limit.Get(ci); + Anope::string *key = mlock_key.Get(ci); + + uint32_t *u = mlock_on.Get(ci); + if (u) + { + process_mlock(ci, *u, true, limit, key); + mlock_on.Unset(ci); + } + + u = mlock_off.Get(ci); + if (u) + { + process_mlock(ci, *u, false, limit, key); + mlock_off.Unset(ci); + } + + mlock_limit.Unset(ci); + mlock_key.Unset(ci); + + if (ci->c) + ci->c->CheckModes(); + } + } +}; + +MODULE_INIT(DBOld) + diff --git a/modules/database/redis.cpp b/modules/database/redis.cpp new file mode 100644 index 000000000..72f25a16e --- /dev/null +++ b/modules/database/redis.cpp @@ -0,0 +1,434 @@ +/* + * + * (C) 2003-2014 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for further details. + * + * + */ + +#include "module.h" +#include "modules/redis.h" + +using namespace Redis; + +class DatabaseRedis; +static DatabaseRedis *me; + +class TypeLoader : public Interface +{ + Serialize::TypeBase *type; + + public: + TypeLoader(Module *creator, Serialize::TypeBase *t) : Interface(creator), type(t) { } + + void OnResult(const Reply &r) override; +}; + +class ObjectLoader : public Interface +{ + Serialize::Object *obj; + + public: + ObjectLoader(Module *creator, Serialize::Object *s) : Interface(creator), obj(s) { } + + void OnResult(const Reply &r) override; +}; + +class FieldLoader : public Interface +{ + Serialize::Object *obj; + Serialize::FieldBase *field; + + public: + FieldLoader(Module *creator, Serialize::Object *o, Serialize::FieldBase *f) : Interface(creator), obj(o), field(f) { } + + void OnResult(const Reply &) override; +}; + +class SubscriptionListener : public Interface +{ + public: + SubscriptionListener(Module *creator) : Interface(creator) { } + + void OnResult(const Reply &r) override; +}; + +class DatabaseRedis : public Module + , public EventHook + , public EventHook +{ + SubscriptionListener sl; + + public: + ServiceReference redis; + + DatabaseRedis(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR) + , sl(this) + { + me = this; + } + + void OnReload(Configuration::Conf *conf) override + { + Configuration::Block *block = conf->GetModule(this); + this->redis = ServiceReference("Redis::Provider", block->Get("engine", "redis/main")); + } + + EventReturn OnLoadDatabase() override + { + if (!redis) + return EVENT_STOP; + + const std::map &types = Serialize::TypeBase::GetTypes(); + for (const std::pair &p : types) + this->OnSerializeTypeCreate(p.second); + + while (redis->BlockAndProcess()); + + redis->Subscribe(&this->sl, "anope"); + + return EVENT_STOP; + } + + void OnSerializeTypeCreate(Serialize::TypeBase *sb) + { + std::vector args = { "SMEMBERS", "ids:" + sb->GetName() }; + + redis->SendCommand(new TypeLoader(this, sb), args); + } + + EventReturn OnSerializeList(Serialize::TypeBase *type, std::vector &ids) override + { + return EVENT_CONTINUE; + } + + EventReturn OnSerializeFind(Serialize::TypeBase *type, Serialize::FieldBase *field, const Anope::string &value, Serialize::ID &id) override + { + return EVENT_CONTINUE; + } + + EventReturn OnSerializeGet(Serialize::Object *object, Serialize::FieldBase *field, Anope::string &value) override + { + return EVENT_CONTINUE; + } + + EventReturn OnSerializeGetRefs(Serialize::Object *object, Serialize::TypeBase *type, std::vector &) override + { + return EVENT_CONTINUE; + } + + EventReturn OnSerializeDeref(Serialize::ID id, Serialize::TypeBase *type) override + { + return EVENT_CONTINUE; + } + + EventReturn OnSerializeGetSerializable(Serialize::Object *object, Serialize::FieldBase *field, Anope::string &type, Serialize::ID &value) override + { + return EVENT_CONTINUE; + } + + EventReturn OnSerializeSet(Serialize::Object *object, Serialize::FieldBase *field, const Anope::string &value) override + { + std::vector args; + + redis->StartTransaction(); + + const Anope::string &old = field->SerializeToString(object); + args = { "SREM", "lookup:" + object->GetSerializableType()->GetName() + ":" + field->GetName() + ":" + old, stringify(object->id) }; + redis->SendCommand(nullptr, args); + + // add object to type set + args = { "SADD", "ids:" + object->GetSerializableType()->GetName(), stringify(object->id) }; + redis->SendCommand(nullptr, args); + + // add key to key set + args = { "SADD", "keys:" + stringify(object->id), field->GetName() }; + redis->SendCommand(nullptr, args); + + // set value + args = { "SET", "values:" + stringify(object->id) + ":" + field->GetName(), value }; + redis->SendCommand(nullptr, args); + + // lookup + args = { "SADD", "lookup:" + object->GetSerializableType()->GetName() + ":" + field->GetName() + ":" + value, stringify(object->id) }; + redis->SendCommand(nullptr, args); + + redis->CommitTransaction(); + + return EVENT_CONTINUE; + } + + EventReturn OnSerializeSetSerializable(Serialize::Object *object, Serialize::FieldBase *field, Serialize::Object *value) override + { + return OnSerializeSet(object, field, stringify(value->id)); + } + + EventReturn OnSerializeUnset(Serialize::Object *object, Serialize::FieldBase *field) override + { + std::vector args; + + redis->StartTransaction(); + + const Anope::string &old = field->SerializeToString(object); + args = { "SREM", "lookup:" + object->GetSerializableType()->GetName() + ":" + field->GetName() + ":" + old, stringify(object->id) }; + redis->SendCommand(nullptr, args); + + // remove field from set + args = { "SREM", "keys:" + stringify(object->id), field->GetName() }; + redis->SendCommand(nullptr, args); + + redis->CommitTransaction(); + + return EVENT_CONTINUE; + } + + EventReturn OnSerializeUnsetSerializable(Serialize::Object *object, Serialize::FieldBase *field) override + { + return OnSerializeUnset(object, field); + } + + EventReturn OnSerializeHasField(Serialize::Object *object, Serialize::FieldBase *field) override + { + return EVENT_CONTINUE; + } + + EventReturn OnSerializableGetId(Serialize::ID &id) override + { + std::vector args = { "INCR", "id" }; + + auto f = [&](const Reply &r) + { + id = r.i; + }; + + FInterface inter(this, f); + redis->SendCommand(&inter, args); + while (redis->BlockAndProcess()); + return EVENT_ALLOW; + } + + void OnSerializableCreate(Serialize::Object *) override + { + } + + void OnSerializableDelete(Serialize::Object *obj) override + { + std::vector args; + + redis->StartTransaction(); + + for (Serialize::FieldBase *field : obj->GetSerializableType()->fields) + { + Anope::string value = field->SerializeToString(obj); + + args = { "SREM", "lookup:" + obj->GetSerializableType()->GetName() + ":" + field->GetName() + ":" + value, stringify(obj->id) }; + redis->SendCommand(nullptr, args); + + args = { "DEL", "values:" + stringify(obj->id) + ":" + field->GetName() }; + redis->SendCommand(nullptr, args); + + args = { "SREM", "keys:" + stringify(obj->id), field->GetName() }; + redis->SendCommand(nullptr, args); + } + + args = { "SREM", "ids:" + obj->GetSerializableType()->GetName(), stringify(obj->id) }; + redis->SendCommand(nullptr, args); + + redis->CommitTransaction(); + } +}; + +void TypeLoader::OnResult(const Reply &r) +{ + if (r.type != Reply::MULTI_BULK || !me->redis) + { + delete this; + return; + } + + for (unsigned i = 0; i < r.multi_bulk.size(); ++i) + { + const Reply *reply = r.multi_bulk[i]; + + if (reply->type != Reply::BULK) + continue; + + int64_t id; + try + { + id = convertTo(reply->bulk); + } + catch (const ConvertException &) + { + continue; + } + + Serialize::Object *obj = type->Require(id); + if (obj == nullptr) + { + Log(LOG_DEBUG) << "redis: Unable to require object #" << id << " of type " << type->GetName(); + continue; + } + + std::vector args = { "SMEMBERS", "keys:" + stringify(id) }; + + me->redis->SendCommand(new ObjectLoader(me, obj), args); + } + + delete this; +} + +void ObjectLoader::OnResult(const Reply &r) +{ + if (r.type != Reply::MULTI_BULK || r.multi_bulk.empty() || !me->redis) + { + delete this; + return; + } + + Serialize::TypeBase *type = obj->GetSerializableType(); + + for (Reply *reply : r.multi_bulk) + { + const Anope::string &key = reply->bulk; + Serialize::FieldBase *field = type->GetField(key); + + if (field == nullptr) + continue; + + std::vector args = { "GET", "values:" + stringify(obj->id) + ":" + key }; + + me->redis->SendCommand(new FieldLoader(me, obj, field), args); + } + + delete this; +} + +void FieldLoader::OnResult(const Reply &r) +{ + Log(LOG_DEBUG_2) << "redis: Setting field " << field->GetName() << " of object #" << obj->id << " of type " << obj->GetSerializableType()->GetName() << " to " << r.bulk; + field->UnserializeFromString(obj, r.bulk); + + delete this; +} + +void SubscriptionListener::OnResult(const Reply &r) +{ + /* + * message + * anope + * message + * + * set 4 email adam@anope.org + * unset 4 email + * create 4 NickCore + * delete 4 + */ + + const Anope::string &message = r.multi_bulk[2]->bulk; + Anope::string command; + spacesepstream sep(message); + + sep.GetToken(command); + + if (command == "set" || command == "unset") + { + Anope::string sid, key, value; + + sep.GetToken(sid); + sep.GetToken(key); + value = sep.GetRemaining(); + + Serialize::ID id; + try + { + id = convertTo(sid); + } + catch (const ConvertException &ex) + { + Log(LOG_DEBUG) << "redis: unable to get id for SL update key " << sid; + return; + } + + Serialize::Object *obj = Serialize::GetID(id); + if (obj == nullptr) + { + Log(LOG_DEBUG) << "redis: pmessage for unknown object #" << id; + return; + } + + Serialize::FieldBase *field = obj->GetSerializableType()->GetField(key); + if (field == nullptr) + { + Log(LOG_DEBUG) << "redis: pmessage for unknown field of object #" << id << ": " << key; + return; + } + + Log(LOG_DEBUG_2) << "redis: Setting field " << field->GetName() << " of object #" << obj->id << " of type " << obj->GetSerializableType()->GetName() << " to " << value; + field->UnserializeFromString(obj, value); + } + else if (command == "create") + { + Anope::string sid, stype; + + sep.GetToken(sid); + sep.GetToken(stype); + + Serialize::ID id; + try + { + id = convertTo(sid); + } + catch (const ConvertException &ex) + { + Log(LOG_DEBUG) << "redis: unable to get id for SL update key " << sid; + return; + } + + Serialize::TypeBase *type = Serialize::TypeBase::Find(stype); + if (type == nullptr) + { + Log(LOG_DEBUG) << "redis: pmessage create for nonexistant type " << stype; + return; + } + + Serialize::Object *obj = type->Require(id); + if (obj == nullptr) + { + Log(LOG_DEBUG) << "redis: require for pmessage create type " << type->GetName() << " id #" << id << " returned nullptr"; + return; + } + } + else if (command == "delete") + { + Anope::string sid; + + sep.GetToken(sid); + + Serialize::ID id; + try + { + id = convertTo(sid); + } + catch (const ConvertException &ex) + { + Log(LOG_DEBUG) << "redis: unable to get id for SL update key " << sid; + return; + } + + Serialize::Object *obj = Serialize::GetID(id); + if (obj == nullptr) + { + Log(LOG_DEBUG) << "redis: message for unknown object #" << id; + return; + } + + obj->Delete(); + } + else + Log(LOG_DEBUG) << "redis: unknown message: " << message; +} + +MODULE_INIT(DatabaseRedis) diff --git a/modules/database/sql.cpp b/modules/database/sql.cpp new file mode 100644 index 000000000..fed0050e4 --- /dev/null +++ b/modules/database/sql.cpp @@ -0,0 +1,372 @@ +#include "module.h" +#include "modules/sql.h" + +using namespace SQL; + +class DBMySQL : public Module, public Pipe + , public EventHook +{ + private: + bool transaction = false; + bool inited = false; + Anope::string prefix; + ServiceReference SQL; + + Result Run(const Query &query) + { + if (!SQL) + ;//XXX + + if (!inited) + { + inited = true; + for (const Query &q : SQL->InitSchema(prefix)) + SQL->RunQuery(q); + } + + return SQL->RunQuery(query); + } + + void StartTransaction() + { + if (!SQL || transaction) + return; + + Run(SQL->BeginTransaction()); + + transaction = true; + Notify(); + } + + void Commit() + { + if (!SQL || !transaction) + return; + + Run(SQL->Commit()); + + transaction = false; + } + + public: + DBMySQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR) + { + } + + void OnNotify() override + { + Commit(); + Serialize::Clear(); + } + + void OnReload(Configuration::Conf *conf) override + { + Configuration::Block *block = conf->GetModule(this); + this->SQL = ServiceReference("SQL::Provider", block->Get("engine")); + this->prefix = block->Get("prefix", "anope_db_"); + inited = false; + } + + EventReturn OnSerializeList(Serialize::TypeBase *type, std::vector &ids) override + { + StartTransaction(); + + ids.clear(); + + Query query = "SELECT `id` FROM `" + prefix + type->GetName() + "`"; + Result res = Run(query); + for (int i = 0; i < res.Rows(); ++i) + { + Serialize::ID id = convertTo(res.Get(i, "id")); + ids.push_back(id); + } + + return EVENT_ALLOW; + } + + EventReturn OnSerializeFind(Serialize::TypeBase *type, Serialize::FieldBase *field, const Anope::string &value, Serialize::ID &id) override + { + if (!SQL) + return EVENT_CONTINUE; + + StartTransaction(); + + for (Query &q : SQL->CreateTable(prefix, type->GetName())) + Run(q); + + for (Query &q : SQL->AlterTable(prefix, type->GetName(), field->GetName(), false)) + Run(q); + + for (const Query &q : SQL->CreateIndex(prefix + type->GetName(), field->GetName())) + Run(q); + + Query query("SELECT `id` FROM `" + prefix + type->GetName() + "` WHERE `" + field->GetName() + "` = @value@"); + query.SetValue("value", value); + Result res = Run(query); + if (res.Rows()) + try + { + id = convertTo(res.Get(0, "id")); + return EVENT_ALLOW; + } + catch (const ConvertException &) + { + } + return EVENT_CONTINUE; + } + + private: + bool GetValue(Serialize::Object *object, Serialize::FieldBase *field, SQL::Result::Value &v) + { + StartTransaction(); + + Query query = "SELECT `" + field->GetName() + "` FROM `" + prefix + object->GetSerializableType()->GetName() + "` WHERE `id` = @id@"; + query.SetValue("id", object->id); + Result res = Run(query); + + if (res.Rows() == 0) + return false; + + v = res.GetValue(0, field->GetName()); + return true; + } + + public: + EventReturn OnSerializeGet(Serialize::Object *object, Serialize::FieldBase *field, Anope::string &value) override + { + SQL::Result::Value v; + + if (!GetValue(object, field, v)) + { + field->CacheMiss(object); + return EVENT_CONTINUE; + } + + value = v.value; + return EVENT_ALLOW; + } + + EventReturn OnSerializeGetRefs(Serialize::Object *object, Serialize::TypeBase *type, std::vector &edges) override + { + StartTransaction(); + + edges.clear(); + + Query query; + if (type) + query = "SELECT field," + prefix + "edges.id,other_id,j1.type,j2.type AS other_type FROM `" + prefix + "edges` " + "JOIN `" + prefix + "objects` AS j1 ON " + prefix + "edges.id = j1.id " + "JOIN `" + prefix + "objects` AS j2 ON " + prefix + "edges.other_id = j2.id " + "WHERE " + " (" + prefix + "edges.id = @id@ AND j2.type = @other_type@) " + "OR" + " (other_id = @id@ AND j1.type = @other_type@)"; + else + query = "SELECT field," + prefix + "edges.id,other_id,j1.type,j2.type AS other_type FROM `" + prefix + "edges` " + "JOIN `" + prefix + "objects` AS j1 ON " + prefix + "edges.id = j1.id " + "JOIN `" + prefix + "objects` AS j2 ON " + prefix + "edges.other_id = j2.id " + "WHERE " + prefix + "edges.id = @id@ OR other_id = @id@"; + + query.SetValue("type", object->GetSerializableType()->GetName()); + query.SetValue("id", object->id); + if (type) + query.SetValue("other_type", type->GetName()); + + Result res = Run(query); + for (int i = 0; i < res.Rows(); ++i) + { + Serialize::ID id = convertTo(res.Get(i, "id")); + + if (id == object->id) + { + // we want other type, this is my edge + Anope::string t = res.Get(i, "other_type"); + Anope::string f = res.Get(i, "field"); + id = convertTo(res.Get(i, "other_id")); + + //XXX sanity checks + Serialize::FieldBase *obj_field = object->GetSerializableType()->GetField(f); + + Serialize::TypeBase *obj_type = Serialize::TypeBase::Find(t); + Serialize::Object *other = obj_type->Require(id); + + edges.emplace_back(other, obj_field, true); + } + else + { + // edge to me + Anope::string t = res.Get(i, "type"); + Anope::string f = res.Get(i, "field"); + + //XXX sanity checks + Serialize::TypeBase *obj_type = Serialize::TypeBase::Find(t); + Serialize::FieldBase *obj_field = obj_type->GetField(f); + Serialize::Object *other = obj_type->Require(id); + + // other type, other field, + edges.emplace_back(other, obj_field, false); + } + } + + return EVENT_ALLOW; + } + + EventReturn OnSerializeDeref(Serialize::ID id, Serialize::TypeBase *type) override + { + StartTransaction(); + + Query query = "SELECT `id` FROM `" + prefix + type->GetName() + "` WHERE `id` = @id@"; + query.SetValue("id", id); + Result res = Run(query); + if (res.Rows() == 0) + return EVENT_CONTINUE; + return EVENT_ALLOW; + } + + EventReturn OnSerializeGetSerializable(Serialize::Object *object, Serialize::FieldBase *field, Anope::string &type, Serialize::ID &value) override + { + StartTransaction(); + + Query query = "SELECT `" + field->GetName() + "`,j1.type AS " + field->GetName() + "_type FROM `" + prefix + object->GetSerializableType()->GetName() + "` " + "JOIN `" + prefix + "objects` AS j1 ON " + prefix + object->GetSerializableType()->GetName() + "." + field->GetName() + " = j1.id " + "WHERE " + prefix + object->GetSerializableType()->GetName() + ".id = @id@"; + query.SetValue("id", object->id); + Result res = Run(query); + + if (res.Rows() == 0) + return EVENT_CONTINUE; + + type = res.Get(0, field->GetName() + "_type"); + try + { + value = convertTo(res.Get(0, field->GetName())); + } + catch (const ConvertException &ex) + { + return EVENT_STOP; + } + + return EVENT_ALLOW; + } + + private: + void DoSet(Serialize::Object *object, Serialize::FieldBase *field, bool is_object, const Anope::string *value) + { + if (!SQL) + return; + + StartTransaction(); + + for (Query &q : SQL->CreateTable(prefix, object->GetSerializableType()->GetName())) + Run(q); + + for (Query &q : SQL->AlterTable(prefix, object->GetSerializableType()->GetName(), field->GetName(), is_object)) + Run(q); + + Query q; + q.SetValue("id", object->id); + if (value) + q.SetValue(field->GetName(), *value); + else + q.SetNull(field->GetName()); + + for (Query &q2 : SQL->Replace(prefix + object->GetSerializableType()->GetName(), q, { "id" })) + Run(q2); + } + + public: + EventReturn OnSerializeSet(Serialize::Object *object, Serialize::FieldBase *field, const Anope::string &value) override + { + DoSet(object, field, false, &value); + return EVENT_STOP; + } + + EventReturn OnSerializeSetSerializable(Serialize::Object *object, Serialize::FieldBase *field, Serialize::Object *value) override + { + if (!SQL) + return EVENT_CONTINUE; + + StartTransaction(); + + if (value) + { + Anope::string v = stringify(value->id); + DoSet(object, field, true, &v); + + Query query; + query.SetValue("field", field->GetName()); + query.SetValue("id", object->id); + query.SetValue("other_id", value->id); + + for (Query &q : SQL->Replace(prefix + "edges", query, { "id", "field" })) + Run(q); + } + else + { + DoSet(object, field, true, nullptr); + + Query query("DELETE FROM `" + prefix + "edges` WHERE `id` = @id@ AND `field` = @field@"); + query.SetValue("id", object->id); + query.SetValue("field", field->GetName()); + Run(query); + } + + return EVENT_STOP; + } + + EventReturn OnSerializeUnset(Serialize::Object *object, Serialize::FieldBase *field) override + { + DoSet(object, field, false, nullptr); + field->CacheMiss(object); + return EVENT_STOP; + } + + EventReturn OnSerializeUnsetSerializable(Serialize::Object *object, Serialize::FieldBase *field) override + { + DoSet(object, field, true, nullptr); + field->CacheMiss(object); + + Query query("DELETE FROM `" + prefix + "edges` WHERE `id` = @id@ AND `field` = @field@"); + query.SetValue("id", object->id); + query.SetValue("field", field->GetName()); + Run(query); + + return EVENT_STOP; + } + + EventReturn OnSerializeHasField(Serialize::Object *object, Serialize::FieldBase *field) override + { + SQL::Result::Value v; + + return GetValue(object, field, v) && !v.null ? EVENT_STOP : EVENT_CONTINUE; + } + + EventReturn OnSerializableGetId(Serialize::ID &id) override + { + StartTransaction(); + + id = SQL->GetID(prefix); + return EVENT_ALLOW; + } + + void OnSerializableCreate(Serialize::Object *object) override + { + StartTransaction(); + + Query q = Query("INSERT INTO `" + prefix + "objects` (`id`,`type`) VALUES (@id@, @type@)"); + q.SetValue("id", object->id); + q.SetValue("type", object->GetSerializableType()->GetName()); + Run(q); + } + + void OnSerializableDelete(Serialize::Object *object) override + { + StartTransaction(); + + Query query("DELETE FROM `" + prefix + object->GetSerializableType()->GetName() + "` WHERE `id` = " + stringify(object->id)); + Run(query); + } +}; + +MODULE_INIT(DBMySQL) + -- cgit