// Anope IRC Services // // Copyright (C) 2003-2026 Anope Contributors // // Anope is free software. You can use, modify, and/or distribute it under the // terms of version 2 of the GNU General Public License. See docs/LICENSE.txt // for the complete terms of this license and docs/AUTHORS.txt for a list of // contributors. // // Based on the original code of Epona by Lara // Based on the original code of Services by Andy Church // // SPDX-License-Identifier: GPL-2.0-only #include "module.h" class LoadData final : public Serialize::Data { public: std::fstream *fs; Serializable::Id id = 0; std::map data; bool read = false; LoadData(std::fstream &fsref) : fs(&fsref) { } bool LoadInternal(const Anope::string &key, Anope::string &value) override { if (!read) { for (Anope::string token; std::getline(*this->fs, token.str());) { if (token.find("ID ") == 0) { this->id = Anope::Convert(token.substr(3), 0); continue; } else if (token.find("DATA ") != 0) break; size_t sp = token.find(' ', 5); // Skip DATA if (sp != Anope::string::npos) data[token.substr(5, sp - 5)] = token.substr(sp + 1); } read = true; } value = this->data[key]; return true; } bool StoreInternal(const Anope::string &key, const Anope::string &value) override { return false; // This module can only load data. } size_t Hash() const override { size_t hash = 0; for (const auto &[_, value] : this->data) if (!value.empty()) hash ^= Anope::hash_cs()(value); return hash; } void Reset() { id = 0; read = false; data.clear(); } }; class DBFlatFile final : public Module { private: bool loaded = false; public: DBFlatFile(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | DEPRECATED | VENDOR) { } EventReturn OnLoadDatabase() override { std::set tried_dbs; const auto db_name = Anope::ExpandData(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; } std::map > positions; for (Anope::string buf; std::getline(fd, buf.str());) if (buf.find("OBJECT ") == 0) positions[buf.substr(7)].push_back(fd.tellg()); LoadData ld(fd); for (const auto &type_order : Serialize::Type::GetTypeOrder()) { Serialize::Type *stype = Serialize::Type::Find(type_order); if (!stype || stype->GetOwner()) continue; for (const auto &position : positions[stype->GetName()]) { fd.clear(); fd.seekg(position); Serializable *obj = stype->Unserialize(NULL, ld); if (obj != NULL) obj->object_id = ld.id; ld.Reset(); } } fd.close(); loaded = true; return EVENT_STOP; } void OnSerializeTypeCreate(Serialize::Type *stype) override { if (!loaded) return; Anope::string db_name; if (stype->GetOwner()) db_name = Anope::ExpandData("module_" + stype->GetOwner()->name + ".db"); else db_name = Anope::ExpandData(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; } LoadData ld(fd); for (Anope::string buf; std::getline(fd, buf.str());) { if (buf == "OBJECT " + stype->GetName()) { stype->Unserialize(NULL, ld); ld.Reset(); } } fd.close(); } }; MODULE_INIT(DBFlatFile)