diff options
author | Adam <Adam@anope.org> | 2011-08-05 05:35:31 -0400 |
---|---|---|
committer | Adam <Adam@anope.org> | 2011-08-05 05:35:31 -0400 |
commit | e66063e6304538d34c40460ca0aa2be5ddb6bdec (patch) | |
tree | f50fe31097160f8f794669809e4f4ef87f477672 /modules/database | |
parent | 9ec18a3b020932eee6242c878149c484f49b13cb (diff) |
Rewrote the example configurations and split them
up into seperate files for each pseudo client.
Also reorganized how the modules are stored, and
made most of the old "extra" modules "core"
Diffstat (limited to 'modules/database')
-rw-r--r-- | modules/database/db_mysql.cpp | 1566 | ||||
-rw-r--r-- | modules/database/db_mysql_live.cpp | 287 | ||||
-rw-r--r-- | modules/database/db_plain.cpp | 908 |
3 files changed, 2761 insertions, 0 deletions
diff --git a/modules/database/db_mysql.cpp b/modules/database/db_mysql.cpp new file mode 100644 index 000000000..59ab412b3 --- /dev/null +++ b/modules/database/db_mysql.cpp @@ -0,0 +1,1566 @@ +#include "module.h" +#include "../extra/sql.h" +#include "../commands/os_session.h" + +static Anope::string ToString(const std::vector<Anope::string> &strings) +{ + Anope::string ret; + + for (unsigned i = 0; i < strings.size(); ++i) + ret += " " + strings[i]; + + if (!ret.empty()) + ret.erase(ret.begin()); + + return ret; +} + +static std::vector<Anope::string> MakeVector(const Anope::string &buf) +{ + Anope::string s; + spacesepstream sep(buf); + std::vector<Anope::string> params; + + while (sep.GetToken(s)) + { + if (s[0] == ':') + { + s.erase(s.begin()); + if (!s.empty() && !sep.StreamEnd()) + params.push_back(s + " " + sep.GetRemaining()); + else if (!s.empty()) + params.push_back(s); + } + else + params.push_back(s); + } + + return params; +} + +static NickAlias *CurNick = NULL; +static NickCore *CurCore = NULL; +static ChannelInfo *CurChannel = NULL; +static BotInfo *CurBot = NULL; + +static void Write(const Anope::string &data); +static void WriteNickMetadata(const Anope::string &key, const Anope::string &data); +static void WriteCoreMetadata(const Anope::string &key, const Anope::string &data); +static void WriteChannelMetadata(const Anope::string &key, const Anope::string &data); +static void WriteBotMetadata(const Anope::string &key, const Anope::string &data); + +class CommandSQLSync : public Command +{ + public: + CommandSQLSync(Module *creator) : Command(creator, "SQLSYNC", 0, 0) + { + this->SetDesc(_("Import your databases to SQL")); + this->SetSyntax(""); + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms); + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("This command syncs your databases with SQL. You should\n" + "only have to execute this command once, when you initially\n" + "import your databases into SQL.")); + return true; + } +}; + +class MySQLInterface : public SQLInterface +{ + public: + MySQLInterface(Module *o) : SQLInterface(o) { } + + void OnResult(const SQLResult &r); + + void OnError(const SQLResult &r); +}; + +class DBMySQL; +static DBMySQL *me; +class DBMySQL : public Module +{ + private: + CommandSQLSync commandsqlsync; + MySQLInterface sqlinterface; + service_reference<SQLProvider> SQL; + + public: + service_reference<SessionService> SessionInterface; + time_t lastwarn; + bool ro; + + void RunQuery(const SQLQuery &query) + { + if (SQL) + { + if (readonly && this->ro) + { + readonly = this->ro = false; + BotInfo *bi = findbot(Config->OperServ); + if (bi) + ircdproto->SendGlobops(bi, "Found SQL again, going out of readonly mode..."); + } + + SQL->Run(&sqlinterface, query); + } + else + { + if (Anope::CurTime - Config->UpdateTimeout > lastwarn) + { + BotInfo *bi = findbot(Config->OperServ); + if (bi) + ircdproto->SendGlobops(bi, "Unable to locate SQL reference, is m_mysql loaded? Going to readonly..."); + readonly = this->ro = true; + this->lastwarn = Anope::CurTime; + } + } + } + + DBMySQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE), commandsqlsync(this), sqlinterface(this), SQL("mysql/main"), SessionInterface("session") + { + me = this; + + this->lastwarn = 0; + this->ro = false; + + Implementation i[] = { + I_OnLoadDatabase, I_OnServerConnect + }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + ModuleManager::RegisterService(&commandsqlsync); + + if (CurrentUplink) + OnServerConnect(); + } + + void OnServerConnect() + { + Implementation i[] = { + /* Misc */ + I_OnSaveDatabase, I_OnPostCommand, + /* NickServ */ + I_OnNickAddAccess, I_OnNickEraseAccess, I_OnNickClearAccess, + I_OnDelCore, I_OnNickForbidden, I_OnNickGroup, + I_OnNickRegister, I_OnChangeCoreDisplay, + I_OnNickSuspended, I_OnDelNick, + /* ChanServ */ + I_OnAccessAdd, I_OnAccessDel, I_OnAccessClear, I_OnLevelChange, + I_OnChanForbidden, I_OnDelChan, I_OnChanRegistered, I_OnChanSuspend, + I_OnAkickAdd, I_OnAkickDel, I_OnMLock, I_OnUnMLock, + /* BotServ */ + I_OnBotCreate, I_OnBotChange, I_OnBotDelete, + I_OnBotAssign, I_OnBotUnAssign, + I_OnBadWordAdd, I_OnBadWordDel, + /* MemoServ */ + I_OnMemoSend, I_OnMemoDel, + /* OperServ */ + I_OnExceptionAdd, I_OnExceptionDel, + I_OnAddXLine, I_OnDelXLine, + /* HostServ */ + I_OnSetVhost, I_OnDeleteVhost + }; + ModuleManager::Attach(i, this, 40); + } + + EventReturn OnLoadDatabase() + { + if (!SQL) + { + Log() << "Error, unable to find service reference for SQL, is m_mysql loaded and configured properly?"; + return EVENT_CONTINUE; + } + + SQLQuery query; + + query = "SELECT * FROM `anope_ns_core`"; + SQLResult r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + NickCore *nc = new NickCore(r.Get(i, "display")); + nc->pass = r.Get(i, "pass"); + nc->email = r.Get(i, "email"); + nc->greet = r.Get(i, "greet"); + + spacesepstream sep(r.Get(i, "flags")); + Anope::string buf; + std::vector<Anope::string> flags; + while (sep.GetToken(buf)) + flags.push_back(buf); + nc->FromString(flags); + + nc->language = r.Get(i, "language"); + nc->memos.memomax = r.Get(i, "memomax").is_number_only() ? convertTo<int16>(r.Get(i, "memomax")) : 20; + } + + query = "SELECT * FROM `anope_ns_access`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + NickCore *nc = findcore(r.Get(i, "display")); + if (!nc) + { + Log() << "MySQL: Got NickCore access entry for nonexistant core " << r.Get(i, "display"); + continue; + } + + nc->AddAccess(r.Get(i, "access")); + } + + query = "SELECT * FROM `anope_ns_core_metadata`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + NickCore *nc = findcore(r.Get(i, "display")); + if (!nc) + { + Log() << "MySQL: Got NickCore access entry for nonexistant core " << r.Get(i, "display"); + continue; + } + + EventReturn MOD_RESULT;; + std::vector<Anope::string> Params = MakeVector(r.Get(i, "value")); + FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(nc, r.Get(i, "name"), Params)); + } + + query = "SELECT * FROM `anope_ns_alias`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + NickCore *nc = findcore(r.Get(i, "display")); + if (!nc) + { + Log() << "MySQL: Got NickAlias for nick " << r.Get(i, "nick") << " with nonexistant core " << r.Get(i, "display"); + continue; + } + + NickAlias *na = new NickAlias(r.Get(i, "nick"), nc); + na->last_quit = r.Get(i, "last_quit"); + na->last_realname = r.Get(i, "last_realname"); + na->last_usermask = r.Get(i, "last_usermask"); + na->time_registered = r.Get(i, "time_registered").is_pos_number_only() ? convertTo<time_t>(r.Get(i, "time_registered")) : Anope::CurTime; + na->last_seen = r.Get(i, "last_seen").is_pos_number_only() ? convertTo<time_t>(r.Get(i, "last_seen")) : Anope::CurTime; + + spacesepstream sep(r.Get(i, "flags")); + Anope::string buf; + std::vector<Anope::string> flags; + while (sep.GetToken(buf)) + flags.push_back(buf); + + na->FromString(flags); + } + + query = "SELECT * FROM `anope_ns_alias_metadata`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + NickAlias *na = findnick(r.Get(i, "nick")); + if (!na) + { + Log() << "MySQL: Got metadata for nonexistant nick " << r.Get(i, "nick"); + continue; + } + + EventReturn MOD_RESULT; + std::vector<Anope::string> Params = MakeVector(r.Get(i, "value")); + FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(na, na->nick, Params)); + } + + query = "SELECT * FROM `anope_hs_core`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + NickAlias *na = findnick(r.Get(i, "nick")); + if (!na) + { + Log() << "MySQL: Got vhost entry for nonexistant nick " << r.Get(i, "nick"); + continue; + } + + time_t creation = r.Get(i, "time").is_pos_number_only() ? convertTo<time_t>(r.Get(i, "time")) : Anope::CurTime; + na->hostinfo.SetVhost(r.Get(i, "vident"), r.Get(i, "vhost"), r.Get(i, "creator"), creation); + } + + query = "SELECT * FROM `anope_bs_core`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + BotInfo *bi = findbot(r.Get(i, "nick")); + if (!bi) + bi = new BotInfo(r.Get(i, "nick"), r.Get(i, "user"), r.Get(i, "host")); + bi->realname = r.Get(i, "rname"); + bi->created = r.Get(i, "created").is_pos_number_only() ? convertTo<time_t>(r.Get(i, "created")) : Anope::CurTime; + + spacesepstream sep(r.Get(i, "flags")); + Anope::string buf; + std::vector<Anope::string> flags; + while (sep.GetToken(buf)) + flags.push_back(buf); + + bi->FromString(flags); + } + + query = "SELECT * FROM `anope_bs_info_metadata`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + BotInfo *bi = findbot(r.Get(i, "botname")); + if (!bi) + { + Log() << "MySQL: BotInfo metadata for nonexistant bot " << r.Get(i, "botname"); + continue; + } + + EventReturn MOD_RESULT; + std::vector<Anope::string> Params = MakeVector(r.Get(i, "value")); + FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(bi, bi->nick, Params)); + } + + query = "SELECT * FROM `anope_cs_info`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + NickCore *nc = NULL; + + if (!r.Get(i, "founder").empty()) + { + nc = findcore(r.Get(i, "founder")); + if (!nc) + { + Log() << "MySQL: Channel " << r.Get(i, "name") << " with nonexistant founder " << r.Get(i, "founder"); + continue; + } + + ChannelInfo *ci = new ChannelInfo(r.Get(i, "name")); + ci->SetFounder(nc); + if (!r.Get(i, "successor").empty()) + ci->successor = findcore(r.Get(i, "successor")); + ci->desc = r.Get(i, "descr"); + ci->time_registered = r.Get(i, "time_registered").is_pos_number_only() ? convertTo<time_t>(r.Get(i, "time_registered")) : Anope::CurTime; + ci->last_used = r.Get(i, "last_used").is_pos_number_only() ? convertTo<time_t>(r.Get(i, "last_used")) : Anope::CurTime; + ci->last_topic = r.Get(i, "last_topic"); + ci->last_topic_setter = r.Get(i, "last_topic_setter"); + ci->last_topic_time = r.Get(i, "last_topic_time").is_number_only() ? convertTo<int>(r.Get(i, "last_topic_time")) : Anope::CurTime; + ci->bantype = r.Get(i, "bantype").is_number_only() ? convertTo<int>(r.Get(i, "bantype")) : 2; + ci->memos.memomax = r.Get(i, "memomax").is_number_only() ? convertTo<int16>(r.Get(i, "memomax")) : 20; + ci->capsmin = r.Get(i, "capsmin").is_number_only() ? convertTo<int>(r.Get(i, "capsmin")) : 0; + ci->capspercent = r.Get(i, "capspercent").is_number_only() ? convertTo<int>(r.Get(i, "capspercent")) : 0; + ci->floodlines = r.Get(i, "floodlines").is_number_only() ? convertTo<int>(r.Get(i, "floodlines")) : 0; + ci->floodsecs = r.Get(i, "floodsecs").is_number_only() ? convertTo<int>(r.Get(i, "floodsecs")) : 0; + ci->repeattimes = r.Get(i, "repeattimes").is_number_only() ? convertTo<int>(r.Get(i, "repeattimes")) : 0; + ci->bi = findbot(r.Get(i, "botnick")); + if (ci->bi && !r.Get(i, "botflags").empty()) + { + spacesepstream sep(r.Get(i, "botflags")); + Anope::string buf; + std::vector<Anope::string> flags; + while (sep.GetToken(buf)) + flags.push_back(buf); + + ci->botflags.FromString(flags); + } + + if (!r.Get(i, "flags").empty()) + { + spacesepstream sep(r.Get(i, "flags")); + Anope::string buf; + std::vector<Anope::string> flags; + while (sep.GetToken(buf)) + flags.push_back(buf); + + ci->FromString(flags); + } + } + } + + query = "SELECT * FROM `anope_cs_ttb`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); + if (!ci) + { + Log() << "MySQL: Channel ttb for nonexistant channel " << r.Get(i, "channel"); + continue; + } + + ci->ttb[atoi(r.Get(i, "ttb_id").c_str())] = atoi(r.Get(i, "value").c_str()); + } + + query = "SELECT * FROM `anope_bs_badwords`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); + if (!ci) + { + Log() << "MySQL: Channel badwords entry for nonexistant channel " << r.Get(i, "channel"); + continue; + } + + BadWordType BWTYPE = BW_ANY; + if (r.Get(i, "type").equals_cs("SINGLE")) + BWTYPE = BW_SINGLE; + else if (r.Get(i, "type").equals_cs("START")) + BWTYPE = BW_START; + else if (r.Get(i, "type").equals_cs("END")) + BWTYPE = BW_END; + ci->AddBadWord(r.Get(i, "word"), BWTYPE); + } + + query = "SELECT * FROM `anope_cs_access`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); + if (!ci) + { + Log() << "MySQL: Channel access entry for nonexistant channel " << r.Get(i, "channel"); + continue; + } + + const Anope::string &provider = r.Get(i, "provider"), &data = r.Get(i, "data"); + service_reference<AccessProvider> ap(provider); + if (!ap) + { + Log() << "MySQL: Access entry for " << ci->name << " using nonexistant provider " << provider; + continue; + } + + ChanAccess *access = ap->Create(); + access->ci = ci; + access->mask = r.Get(i, "mask"); + access->creator = r.Get(i, "creator"); + access->last_seen = r.Get(i, "last_seen").is_pos_number_only() ? convertTo<time_t>(r.Get(i, "last_seen")) : Anope::CurTime; + access->created = r.Get(i, "created").is_pos_number_only() ? convertTo<time_t>(r.Get(i, "created")) : Anope::CurTime; + access->Unserialize(data); + ci->AddAccess(access); + } + + query = "SELECT * FROM `anope_cs_akick`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); + if (!ci) + { + Log() << "MySQL: Channel access entry for nonexistant channel " << r.Get(i, "channel"); + continue; + } + + NickCore *nc = NULL; + spacesepstream sep(r.Get(i, "flags")); + Anope::string flag, mask; + while (sep.GetToken(flag)) + { + if (flag.equals_cs("ISNICK")) + nc = findcore(r.Get(i, "mask")); + + AutoKick *ak; + if (nc) + ak = ci->AddAkick(r.Get(i, "creator"), nc, r.Get(i, "reason"), atol(r.Get(i, "created").c_str()), atol(r.Get(i, "last_used").c_str())); + else + ak = ci->AddAkick(r.Get(i, "creator"), r.Get(i, "mask"), r.Get(i, "reason"), atol(r.Get(i, "created").c_str()), atol(r.Get(i, "last_used").c_str())); + if (nc) + ak->SetFlag(AK_ISNICK); + } + } + + query = "SELECT * FROM `anope_cs_levels`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); + if (!ci) + { + Log() << "MySQL: Channel level entry for nonexistant channel " << r.Get(i, "channel"); + continue; + } + + ci->levels[atoi(r.Get(i, "position").c_str())] = atoi(r.Get(i, "level").c_str()); + } + + query = "SELECT * FROM `anope_cs_info_metadata`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); + if (!ci) + { + Log() << "MySQL: Channel metadata for nonexistant channel " << r.Get(i, "channel"); + continue; + } + + EventReturn MOD_RESULT; + std::vector<Anope::string> Params = MakeVector(r.Get(i, "value")); + FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(ci, ci->name, Params)); + } + + query = "SELECT * FROM `anope_cs_mlock`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); + if (!ci) + { + Log() << "MySQL: Channel mlock for nonexistant channel " << r.Get(i, "channel"); + continue; + } + + std::vector<Anope::string> mlocks; + ci->GetExtRegular("db_mlock", mlocks); + + Anope::string modestring = r.Get(i, "status") + " " + r.Get(i, "mode") + " " + r.Get(i, "setter") + " " + r.Get(i, "created") + " " + r.Get(i, "param"); + + mlocks.push_back(modestring); + + ci->Extend("db_mlock", new ExtensibleItemRegular<std::vector<Anope::string> >(mlocks)); + } + + query = "SELECT * FROM `anope_ms_info`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + MemoInfo *mi = NULL; + if (r.Get(i, "serv").equals_cs("NICK")) + { + NickCore *nc = findcore(r.Get(i, "receiver")); + if (nc) + mi = &nc->memos; + } + else if (r.Get(i, "serv").equals_cs("CHAN")) + { + ChannelInfo *ci = cs_findchan(r.Get(i, "receiver")); + if (ci) + mi = &ci->memos; + } + if (mi) + { + Memo *m = new Memo(); + mi->memos.push_back(m); + m->sender = r.Get(i, "sender"); + m->time = r.Get(i, "time").is_pos_number_only() ? convertTo<time_t>(r.Get(i, "time")) : Anope::CurTime; + m->text = r.Get(i, "text"); + + if (!r.Get(i, "flags").empty()) + { + spacesepstream sep(r.Get(i, "flags")); + Anope::string buf; + std::vector<Anope::string> flags; + while (sep.GetToken(buf)) + flags.push_back(buf); + + m->FromString(flags); + } + } + } + + for (std::list<XLineManager *>::iterator it = XLineManager::XLineManagers.begin(), it_end = XLineManager::XLineManagers.end(); it != it_end; ++it) + { + XLineManager *xm = *it; + + query = "SELECT * FROM `anope_os_xlines` WHERE `type` = @type"; + query.setValue("type", Anope::string(xm->Type())); + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + Anope::string user = r.Get(i, "user"); + Anope::string host = r.Get(i, "host"); + Anope::string by = r.Get(i, "xby"); + Anope::string reason = r.Get(i, "reason"); + time_t seton = r.Get(i, "seton").is_pos_number_only() ? convertTo<time_t>(r.Get(i, "seton")) : Anope::CurTime; + time_t expires = r.Get(i, "expires").is_pos_number_only() ? convertTo<time_t>(r.Get(i, "expires")) : Anope::CurTime; + + XLine *x = xm->Add(user + "@" + host, by, expires, reason); + if (x) + x->Created = seton; + } + } + + query = "SELECT * FROM `anope_os_exceptions`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + Anope::string mask = r.Get(i, "mask"); + unsigned limit = convertTo<unsigned>(r.Get(i, "slimit")); + Anope::string creator = r.Get(i, "who"); + Anope::string reason = r.Get(i, "reason"); + time_t expires = convertTo<time_t>(r.Get(i, "expires")); + + if (SessionInterface) + { + Exception *e = new Exception(); + e->mask = mask; + e->limit = limit; + e->who = creator; + e->reason = reason; + e->time = expires; + SessionInterface->AddException(e); + } + } + + query = "SELECT * FROM `anope_extra`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + std::vector<Anope::string> params = MakeVector(r.Get(i, "data")); + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnDatabaseRead, OnDatabaseRead(params)); + } + + query = "SELECT * FROM `anope_ns_core_metadata`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + NickCore *nc = findcore(r.Get(i, "nick")); + if (!nc) + continue; + if (r.Get(i, "name") == "MEMO_IGNORE") + nc->memos.ignores.push_back(r.Get(i, "value").ci_str()); + } + + query = "SELECT * FROM `anope_cs_info_metadata`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); + if (!ci) + continue; + if (r.Get(i, "name") == "MEMO_IGNORE") + ci->memos.ignores.push_back(r.Get(i, "value").ci_str()); + } + + return EVENT_STOP; + } + + EventReturn OnSaveDatabase() + { + SQLQuery query; + + query = "TRUNCATE TABLE `anope_os_core`"; + this->RunQuery(query); + + query = "INSERT INTO `anope_os_core` (maxusercnt, maxusertime, akills_count, snlines_count, sqlines_count, szlines_count) VALUES(@maxusercnt, @maxusertime, @akills_count, @snlines_count, @sqlines_count, @szlines_count)"; + query.setValue("maxusercnt", maxusercnt); + query.setValue("maxusertime", maxusertime); + this->RunQuery(query); + + for (nickcore_map::const_iterator it = NickCoreList.begin(), it_end = NickCoreList.end(); it != it_end; ++it) + { + CurCore = it->second; + FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteCoreMetadata, CurCore)); + } + + for (nickalias_map::const_iterator it = NickAliasList.begin(), it_end = NickAliasList.end(); it != it_end; ++it) + { + CurNick = it->second; + FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteNickMetadata, CurNick)); + } + + for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end; ++it) + { + CurChannel = it->second; + FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteChannelMetadata, CurChannel)); + } + + for (Anope::insensitive_map<BotInfo *>::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it) + { + if (it->second->HasFlag(BI_CONF)) + continue; + + CurBot = it->second; + FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteBotMetadata, CurBot)); + + query = "INSERT INTO `anope_bs_core` (nick, user, host, rname, flags, created, chancount) VALUES(@nick, @user, @host, @rname, @flags, @created, @chancount) ON DUPLICATE KEY UPDATE nick=VALUES(nick), user=VALUES(user), host=VALUES(host), rname=VALUES(rname), flags=VALUES(flags), created=VALUES(created), chancount=VALUES(chancount)"; + query.setValue("nick", CurBot->nick); + query.setValue("user", CurBot->GetIdent()); + query.setValue("host", CurBot->host); + query.setValue("rname", CurBot->realname); + query.setValue("flags", ToString(CurBot->ToString())); + query.setValue("created", CurBot->created); + query.setValue("chancount", CurBot->chancount); + this->RunQuery(query); + } + + query = "TRUNCATE TABLE `anope_extra`"; + this->RunQuery(query); + FOREACH_MOD(I_OnDatabaseWrite, OnDatabaseWrite(Write)); + + return EVENT_CONTINUE; + } + + void OnPostCommand(CommandSource &source, Command *command, const std::vector<Anope::string> ¶ms) + { + User *u = source.u; + + if (command->name.find("nickserv/set/") == 0 || command->name.find("nickserv/saset/") == 0) + { + NickAlias *na = findnick(command->name.find("nickserv/set/") == 0 ? source.u->nick : params[1]); + if (!na) + return; + + if (command->name == "nickserv/set/password" || command->name == "nickserv/saset/password") + { + SQLQuery query("UPDATE `anope_ns_core` SET `pass` = @pass WHERE `display` = @display"); + query.setValue("pass", na->nc->pass); + query.setValue("display", na->nc->display); + this->RunQuery(query); + } + else if (command->name == "nickserv/set/language" || command->name == "nickserv/saset/language") + { + SQLQuery query("UPDATE `anope_ns_core` SET `language` = @language WHERE `display` = @display"); + query.setValue("language", na->nc->language); + query.setValue("display", na->nc->display); + this->RunQuery(query); + } + else if (command->name == "nickserv/set/email" || command->name == "nickserv/saset/email") + { + SQLQuery query("UPDATE `anope_ns_core` SET `email` = @email WHERE `display` = @display"); + query.setValue("email", na->nc->email); + query.setValue("display", na->nc->display); + this->RunQuery(query); + } + else if (command->name == "nickserv/set/greet" || command->name == "nickserv/saset/greet") + { + SQLQuery query("UPDATE `anope_ns_core` SET `greet` = @greet WHERE `display` = @display"); + query.setValue("greet", na->nc->greet); + query.setValue("display", na->nc->display); + this->RunQuery(query); + } + else + { + SQLQuery query("UPDATE `anope_ns_core` SET `flags` = @flags WHERE `display` = @display"); + query.setValue("flags", ToString(na->nc->ToString())); + query.setValue("display", na->nc->display); + this->RunQuery(query); + } + } + else if (command->name.find("chanserv/set") == 0 || command->name.find("chanserv/saset") == 0) + { + ChannelInfo *ci = params.size() > 0 ? cs_findchan(params[0]) : NULL; + if (!ci) + return; + + if (command->name == "chanserv/set/founder" || command->name == "chanserv/saset/founder") + { + SQLQuery query("UPDATE `anope_cs_info` SET `founder` = @founder WHERE `name` = @name"); + query.setValue("founder", ci->GetFounder() ? ci->GetFounder()->display : ""); + query.setValue("name", ci->name); + this->RunQuery(query); + } + else if (command->name == "chanserv/set/successor" || command->name == "chanserv/saset/successor") + { + SQLQuery query("UPDATE `anope_cs_info` SET `successor` = @successor WHERE `name` = @name"); + query.setValue("successor", ci->successor ? ci->successor->display : ""); + query.setValue("name", ci->name); + this->RunQuery(query); + } + else if (command->name == "chanserv/set/desc" || command->name == "chanserv/saset/desc") + { + SQLQuery query("UPDATE `anope_cs_info` SET `descr` = @descr WHERE `name` = @name"); + query.setValue("descr", ci->desc); + query.setValue("name", ci->name); + this->RunQuery(query); + } + else if (command->name == "chanserv/set/bantype" || command->name == "chanserv/saset/bantype") + { + SQLQuery query("UPDATE `anope_cs_info` SET `bantype` = @bantype WHERE `name` = @name"); + query.setValue("bantype", ci->bantype); + query.setValue("name", ci->name); + this->RunQuery(query); + } + else + { + SQLQuery query("UPDATE `anope_cs_info` SET `flags` = @flags WHERE `name` = @name"); + query.setValue("flags", ToString(ci->ToString())); + query.setValue("name", ci->name); + this->RunQuery(query); + } + } + else if (command->name == "botserv/kick" && params.size() > 2) + { + ChannelInfo *ci = cs_findchan(params[0]); + if (!ci) + return; + if (!ci->HasPriv(u, CA_SET) && !u->HasPriv("botserv/administration")) + return; + if (params[1].equals_ci("BADWORDS") || params[1].equals_ci("BOLDS") || params[1].equals_ci("CAPS") || params[1].equals_ci("COLORS") || params[1].equals_ci("FLOOD") || params[1].equals_ci("REPEAT") || params[1].equals_ci("REVERSES") || params[1].equals_ci("UNDERLINES")) + { + if (params[2].equals_ci("ON") || params[2].equals_ci("OFF")) + { + for (int i = 0; i < TTB_SIZE; ++i) + { + SQLQuery query("INSERT INTO `anope_cs_ttb` (channel, ttb_id, value) VALUES(@channel, @ttb_id, @value) ON DUPLICATE KEY UPDATE channel=VALUES(channel), ttb_id=VALUES(ttb_id), value=VALUES(value)"); + query.setValue("channel", ci->name); + query.setValue("ttb_id", i); + query.setValue("value", ci->ttb[i]); + this->RunQuery(query); + } + + { + SQLQuery query("UPDATE `anope_cs_info` SET `botflags` = @botflags WHERE `name` = @name"); + query.setValue("botflags", ToString(ci->botflags.ToString())); + query.setValue("name", ci->name); + this->RunQuery(query); + } + + if (params[1].equals_ci("CAPS")) + { + SQLQuery query("UPDATE `anope_cs_info` SET `capsmin` = @capsmin, `capspercent` = @capspercent WHERE `name` = @name"); + query.setValue("capsmin", ci->capsmin); + query.setValue("capspercent", ci->capspercent); + query.setValue("name", ci->name); + this->RunQuery(query); + } + else if (params[1].equals_ci("FLOOD")) + { + SQLQuery query("UPDATE `anope_cs_info` SET `floodlines` = @floodlines, `floodsecs` = @floodsecs WHERE `name` = @name"); + query.setValue("floodlines", ci->floodlines); + query.setValue("floodsecs", ci->floodsecs); + query.setValue("name", ci->name); + this->RunQuery(query); + } + else if (params[1].equals_ci("REPEAT")) + { + SQLQuery query("UPDATE `anope_cs_info` SET `repeattimes` = @ WHERE `name` = @"); + query.setValue("repeattimes", ci->repeattimes); + query.setValue("name", ci->name); + this->RunQuery(query); + } + } + } + } + else if (command->name == "botserv/set" && params.size() > 1) + { + ChannelInfo *ci = cs_findchan(params[0]); + if (ci && !ci->HasPriv(u, CA_SET) && !u->HasPriv("botserv/administration")) + return; + BotInfo *bi = NULL; + if (!ci) + bi = findbot(params[0]); + if (bi && params[1].equals_ci("PRIVATE") && u->HasPriv("botserv/set/private")) + { + SQLQuery query("UPDATE `anope_bs_core` SET `flags` = @ WHERE `nick` = @"); + query.setValue("flags", ToString(bi->ToString())); + query.setValue("nick", bi->nick); + this->RunQuery(query); + } + else if (!ci) + return; + else if (params[1].equals_ci("DONTKICKOPS") || params[1].equals_ci("DONTKICKVOICES") || params[1].equals_ci("FANTASY") || params[1].equals_ci("GREET") || params[1].equals_ci("SYMBIOSIS") || params[1].equals_ci("NOBOT")) + { + SQLQuery query("UPDATE `anope_cs_info` SET `botflags` = @ WHERE `name` = @"); + query.setValue("botflags", ToString(ci->botflags.ToString())); + query.setValue("name", ci->name); + this->RunQuery(query); + } + } + else if (command->name == "memoserv/ignore" && params.size() > 0) + { + Anope::string target = params[0]; + NickCore *nc = NULL; + ChannelInfo *ci = NULL; + if (target[0] != '#') + { + target = u->nick; + nc = u->Account(); + if (!nc) + return; + } + else + { + ci = cs_findchan(target); + if (!ci || !ci->HasPriv(u, CA_MEMO)) + return; + } + + MemoInfo *mi = ci ? &ci->memos : &nc->memos; + Anope::string table = ci ? "anope_cs_info_metadata" : "anope_ns_core_metadata"; + Anope::string ename = ci ? "channel" : "nick"; + + SQLQuery query("DELETE FROM `" + table + "` WHERE `" + ename + "` = @target AND `name` = 'MEMO_IGNORE'"); + query.setValue("target", target); + this->RunQuery(query); + + query = "INSERT INTO `" + table + "` VALUES(" + ename + ", name, value) (@target, 'MEMO_IGNORE, @ignore)"; + query.setValue("target", target); + for (unsigned j = 0; j < mi->ignores.size(); ++j) + { + query.setValue("ignore", mi->ignores[j]); + this->RunQuery(query); + } + } + } + + void OnNickAddAccess(NickCore *nc, const Anope::string &entry) + { + SQLQuery query("INSERT INTO `anope_ns_access` (display, access) VALUES(@display, @access)"); + query.setValue("display", nc->display); + query.setValue("access", entry); + this->RunQuery(query); + } + + void OnNickEraseAccess(NickCore *nc, const Anope::string &entry) + { + SQLQuery query("DELETE FROM `anope_ns_access` WHERE `display` = @display AND `access` = @access"); + query.setValue("display", nc->display); + query.setValue("access", entry); + this->RunQuery(query); + } + + void OnNickClearAccess(NickCore *nc) + { + SQLQuery query("DELETE FROM `anope_ns_access` WHERE `display` = @display"); + query.setValue("display", nc->display); + this->RunQuery(query); + } + + void OnDelCore(NickCore *nc) + { + SQLQuery query("DELETE FROM `anope_ns_core` WHERE `display` = @display"); + query.setValue("display", nc->display); + this->RunQuery(query); + } + + void OnNickForbidden(NickAlias *na) + { + SQLQuery query("UPDATE `anope_ns_alias` SET `flags` = @flags WHERE `nick` = @nick"); + query.setValue("flags", ToString(na->ToString())); + query.setValue("nick", na->nick); + this->RunQuery(query); + } + + void OnNickGroup(User *u, NickAlias *) + { + OnNickRegister(findnick(u->nick)); + } + + void InsertAlias(NickAlias *na) + { + SQLQuery query("INSERT INTO `anope_ns_alias` (nick, last_quit, last_realname, last_usermask, time_registered, last_seen, flags, display) VALUES(@nick, @last_quit, @last_realname, @last_usermask, @time_registered, @last_seen, @flags, @display) ON DUPLICATE KEY UPDATE last_quit=VALUES(last_quit), last_realname=VALUES(last_realname), last_usermask=VALUES(last_usermask), time_registered=VALUES(time_registered), last_seen=VALUES(last_seen), flags=VALUES(flags), display=VALUES(display)"); + query.setValue("nick", na->nick); + query.setValue("last_quit", na->last_quit); + query.setValue("last_realname", na->last_realname); + query.setValue("last_usermask", na->last_usermask); + query.setValue("time_registered", na->time_registered); + query.setValue("last_seen", na->last_seen); + query.setValue("flags", ToString(na->ToString())); + query.setValue("display", na->nc->display); + this->RunQuery(query); + } + + void InsertCore(NickCore *nc) + { + SQLQuery query("INSERT INTO `anope_ns_core` (display, pass, email, greet, flags, language, memomax) VALUES(@display, @pass, @email, @greet, @flags, @language, @memomax) ON DUPLICATE KEY UPDATE pass=VALUES(pass), email=VALUES(email), greet=VALUES(greet), flags=VALUES(flags), language=VALUES(language), memomax=VALUES(memomax)"); + query.setValue("display", nc->display); + query.setValue("pass", nc->pass); + query.setValue("email", nc->email); + query.setValue("greet", nc->greet); + query.setValue("flags", ToString(nc->ToString())); + query.setValue("language", nc->language); + query.setValue("memomax", nc->memos.memomax); + this->RunQuery(query); + } + + void OnNickRegister(NickAlias *na) + { + this->InsertCore(na->nc); + this->InsertAlias(na); + } + + void OnChangeCoreDisplay(NickCore *nc, const Anope::string &newdisplay) + { + SQLQuery query("UPDATE `anope_ns_core` SET `display` = @newdisplay WHERE `display` = @olddisplay"); + query.setValue("newdisplay", newdisplay); + query.setValue("olddisplay", nc->display); + this->RunQuery(query); + } + + void OnNickSuspend(NickAlias *na) + { + SQLQuery query("UPDATE `anope_ns_core` SET `flags` = @flags WHERE `display` = @display"); + query.setValue("flags", ToString(na->nc->ToString())); + query.setValue("display", na->nc->display); + this->RunQuery(query); + } + + void OnDelNick(NickAlias *na) + { + SQLQuery query("DELETE FROM `anope_ns_alias` WHERE `nick` = @nick"); + query.setValue("nick", na->nick); + this->RunQuery(query); + } + + void OnAccessAdd(ChannelInfo *ci, User *, ChanAccess *access) + { + SQLQuery query("INSERT INTO `anope_cs_access` (provider, data, mask, channel, last_seen, creator) VALUES (@provider, @data, @mask, @channel, @last_seen, @creator) ON DUPLICATE KEY UPDATE level=VALUES(level), display=VALUES(display), channel=VALUES(channel), last_seen=VALUES(last_seen), creator=VALUES(creator)"); + query.setValue("provider", access->provider->name); + query.setValue("data", access->Serialize()); + query.setValue("mask", access->mask); + query.setValue("channel", ci->name); + query.setValue("last_seen", access->last_seen); + query.setValue("creator", access->creator); + this->RunQuery(query); + } + + void OnAccessDel(ChannelInfo *ci, User *u, ChanAccess *access) + { + SQLQuery query("DELETE FROM `anope_cs_access` WHERE `mask` = @mask AND `channel` = @channel"); + query.setValue("mask", access->mask); + query.setValue("channel", ci->name); + this->RunQuery(query); + } + + void OnAccessClear(ChannelInfo *ci, User *u) + { + SQLQuery query("DELETE FROM `anope_cs_access` WHERE `channel` = @channel"); + query.setValue("channel", ci->name); + this->RunQuery(query); + } + + void OnLevelChange(User *u, ChannelInfo *ci, int pos, int what) + { + SQLQuery query("UPDATE `anope_cs_levels` SET `level` = @level WHERE `channel` = @channel AND `position` = @pos ON DUPLICATE KEY UPDATE level=VALUES(level), position=VALUES(position)"); + if (pos >= 0) + { + query.setValue("level", what); + query.setValue("channel", ci->name); + query.setValue("pos", pos); + this->RunQuery(query); + } + else + { + query.setValue("channel", ci->name); + for (int i = 0; i < CA_SIZE; ++i) + { + query.setValue("level", ci->levels[i]); + query.setValue("pos", i); + this->RunQuery(query); + } + } + } + + void OnChanForbidden(ChannelInfo *ci) + { + SQLQuery query("INSERT INTO `anope_cs_info` (name, time_registered, last_used, flags) VALUES (@name, @time_registered, @last_used, @flags)"); + query.setValue("name", ci->name); + query.setValue("time_registered", ci->time_registered); + query.setValue("last_used", ci->last_used); + query.setValue("flags", ToString(ci->ToString())); + this->RunQuery(query); + } + + void OnDelChan(ChannelInfo *ci) + { + SQLQuery query("DELETE FROM `anope_cs_info` WHERE `name` = @name"); + query.setValue("name", ci->name); + this->RunQuery(query); + } + + void OnChanRegistered(ChannelInfo *ci) + { + SQLQuery query("INSERT INTO `anope_cs_info` (name, founder, successor, descr, time_registered, last_used, last_topic, last_topic_setter, last_topic_time, flags, bantype, memomax, botnick, botflags, capsmin, capspercent, floodlines, floodsecs, repeattimes) VALUES(@name, @founder, @successor, @descr, @time_registered, @last_used, @last_topic_text, @last_topic_setter, @last_topic_time, @flags, @bantype, @memomax, @botnick, @botflags, @capsmin, @capspercent, @floodlines, @floodsecs, @repeattimes) ON DUPLICATE KEY UPDATE founder=VALUES(founder), successor=VALUES(successor), descr=VALUES(descr), time_registered=VALUES(time_registered), last_used=VALUES(last_used), last_topic=VALUES(last_topic), last_topic_setter=VALUES(last_topic_setter), last_topic_time=VALUES(last_topic_time), flags=VALUES(flags), bantype=VALUES(bantype), memomax=VALUES(memomax), botnick=VALUES(botnick), botflags=VALUES(botflags), capsmin=VALUES(capsmin), capspercent=VALUES(capspercent), floodlines=VALUES(floodlines), floodsecs=VALUES(floodsecs), repeattimes=VALUES(repeattimes)"); + query.setValue("name", ci->name); + query.setValue("founder", ci->GetFounder() ? ci->GetFounder()->display : ""); + query.setValue("successor", ci->successor ? ci->successor->display : ""); + query.setValue("descr", ci->desc); + query.setValue("time_registered", ci->time_registered); + query.setValue("last_used", ci->last_used); + query.setValue("last_topic_text", ci->last_topic); + query.setValue("last_topic_setter", ci->last_topic_setter); + query.setValue("last_topic_time", ci->last_topic_time); + query.setValue("flags", ToString(ci->ToString())); + query.setValue("bantype", ci->bantype); + query.setValue("memomax", ci->memos.memomax); + query.setValue("botnick", ci->bi ? ci->bi->nick : ""); + query.setValue("botflags", ToString(ci->botflags.ToString())); + query.setValue("capsmin", ci->capsmin); + query.setValue("capspercent", ci->capspercent); + query.setValue("floodlines", ci->floodlines); + query.setValue("floodsecs", ci->floodsecs); + query.setValue("repeattimes", ci->repeattimes); + this->RunQuery(query); + + query = "DELETE from `anope_cs_mlock` WHERE `channel` = @name"; + query.setValue("name", ci->name); + this->RunQuery(query); + for (std::multimap<ChannelModeName, ModeLock>::const_iterator it = ci->GetMLock().begin(), it_end = ci->GetMLock().end(); it != it_end; ++it) + { + const ModeLock &ml = it->second; + ChannelMode *cm = ModeManager::FindChannelModeByName(ml.name); + + if (cm != NULL) + { + query = "INSERT INTO `anope_cs_mlock` (channel, mode, status, setter, created, param) VALUES(@channel, @mode, @status, @setter, @created, @param)"; + query.setValue("channel", ci->name); + query.setValue("mode", cm->NameAsString()); + query.setValue("status", ml.set ? 1 : 0); + query.setValue("setter", ml.setter); + query.setValue("created", ml.created); + query.setValue("param", ml.param); + this->RunQuery(query); + } + } + } + + void OnChanSuspend(ChannelInfo *ci) + { + SQLQuery query("UPDATE `anope_cs_info` SET `flags` = @flags WHERE `name` = @name"); + query.setValue("flags", ToString(ci->ToString())); + query.setValue("name", ci->name); + this->RunQuery(query); + } + + void OnAkickAdd(ChannelInfo *ci, AutoKick *ak) + { + SQLQuery query("INSERT INTO `anope_cs_akick` (channel, flags, mask, reason, creator, created, last_used) VALUES(@channel, @flags, @mask, @reason, @creator, @created, @last_used)"); + query.setValue("channel", ci->name); + query.setValue("flags", ak->HasFlag(AK_ISNICK) ? "ISNICK" : ""); + query.setValue("mask", ak->HasFlag(AK_ISNICK) ? ak->nc->display : ak->mask); + query.setValue("reason", ak->reason); + query.setValue("creator", ak->creator); + query.setValue("created", ak->addtime); + query.setValue("last_used", ak->last_used); + this->RunQuery(query); + } + + void OnAkickDel(ChannelInfo *ci, AutoKick *ak) + { + SQLQuery query("DELETE FROM `anope_cs_akick` WHERE `channel`= @mask AND `mask` = @mask"); + query.setValue("channel", ci->name); + query.setValue("mask", ak->HasFlag(AK_ISNICK) ? ak->nc->display : ak->mask); + this->RunQuery(query); + } + + EventReturn OnMLock(ChannelInfo *ci, ModeLock *lock) + { + ChannelMode *cm = ModeManager::FindChannelModeByName(lock->name); + if (cm != NULL) + { + SQLQuery query("INSERT INTO `anope_cs_mlock` (channel, mode, status, setter, created, param) VALUES(@channel, @mode, @status, @setter, @created, @param)"); + query.setValue("channel", ci->name); + query.setValue("mode", cm->NameAsString()); + query.setValue("status", lock->set ? 1 : 0); + query.setValue("setter", lock->setter); + query.setValue("created", lock->created); + query.setValue("param", lock->param); + this->RunQuery(query); + } + return EVENT_CONTINUE; + } + + EventReturn OnUnMLock(ChannelInfo *ci, ChannelMode *mode, const Anope::string ¶m) + { + ChannelMode *cm = ModeManager::FindChannelModeByName(mode->Name); + if (cm != NULL) + { + SQLQuery query("DELETE FROM `anope_cs_mlock` WHERE `channel` = @channel AND `mode` = @mode AND `param` = @param"); + query.setValue("channel", ci->name); + query.setValue("mode", mode->NameAsString()); + query.setValue("param", param); + this->RunQuery(query); + } + return EVENT_CONTINUE; + } + + void OnBotCreate(BotInfo *bi) + { + SQLQuery query("INSERT INTO `anope_bs_core` (nick, user, host, rname, flags, created, chancount) VALUES(@nick, @user, @host, @rname, @flags, @created, @chancuont) ON DUPLICATE KEY UPDATE nick=VALUES(nick), user=VALUES(user), host=VALUES(host), rname=VALUES(rname), flags=VALUES(flags), created=VALUES(created), chancount=VALUES(chancount)"); + query.setValue("nick", bi->nick); + query.setValue("user", bi->GetIdent()); + query.setValue("host", bi->host); + query.setValue("rname", bi->realname); + query.setValue("flags", ToString(bi->ToString())); + query.setValue("created", bi->created); + query.setValue("chancount", bi->chancount); + this->RunQuery(query); + } + + void OnBotChange(BotInfo *bi) + { + OnBotCreate(bi); + } + + void OnBotDelete(BotInfo *bi) + { + SQLQuery query("UPDATE `anope_cs_info` SET `botnick` = '' WHERE `botnick` = @botnick"); + query.setValue("botnick", bi->nick); + this->RunQuery(query); + } + + EventReturn OnBotAssign(User *sender, ChannelInfo *ci, BotInfo *bi) + { + SQLQuery query("UPDATE `anope_cs_info` SET `botnick` = @botnick WHERE `name` = @channel"); + query.setValue("botnick", bi->nick); + query.setValue("channel", ci->name); + this->RunQuery(query); + return EVENT_CONTINUE; + } + + EventReturn OnBotUnAssign(User *sender, ChannelInfo *ci) + { + SQLQuery query("UPDATE `anope_cs_info` SET `botnick` = '' WHERE `name` = @channel"); + query.setValue("channel", ci->name); + this->RunQuery(query); + return EVENT_CONTINUE; + } + + void OnBadWordAdd(ChannelInfo *ci, BadWord *bw) + { + SQLQuery query("INSERT INTO `anope_bs_badwords` (channel, word, type) VALUES(@channel, @word, @type) ON DUPLICATE KEY UPDATE channel=VALUES(channel), word=VALUES(word), type=VALUES(type)"); + query.setValue("channel", ci->name); + query.setValue("word", bw->word); + switch (bw->type) + { + case BW_SINGLE: + query.setValue("type", "SINGLE"); + break; + case BW_START: + query.setValue("type", "START"); + break; + case BW_END: + query.setValue("type", "END"); + break; + default: + query.setValue("type", "ANY"); + } + this->RunQuery(query); + } + + void OnBadWordDel(ChannelInfo *ci, BadWord *bw) + { + SQLQuery query("DELETE FROM `anope_bs_badwords` WHERE `channel` = @channel AND `word` = @word AND `type` = @type"); + query.setValue("channel", ci->name); + query.setValue("word", bw->word); + switch (bw->type) + { + case BW_SINGLE: + query.setValue("type", "SINGLE"); + break; + case BW_START: + query.setValue("type", "START"); + break; + case BW_END: + query.setValue("type", "END"); + break; + default: + query.setValue("type", "ANY"); + } + this->RunQuery(query); + } + + void OnMemoSend(const Anope::string &source, const Anope::string &target, MemoInfo *mi, Memo *m) + { + const Anope::string &mtype = (!target.empty() && target[0] == '#' ? "CHAN" : "NICK"); + SQLQuery query("INSERT INTO `anope_ms_info` (receiver, flags, time, sender, text, serv) VALUES(@receiver, @flags, @time, @sender, @text, @serv)"); + query.setValue("receiver", target); + query.setValue("flags", ToString(m->ToString())); + query.setValue("time", m->time); + query.setValue("sender", source); + query.setValue("text", m->text); + query.setValue("serv", mtype); + this->RunQuery(query); + } + + void OnMemoDel(const NickCore *nc, MemoInfo *mi, Memo *m) + { + SQLQuery query; + + if (m) + { + query = "DELETE FROM `anope_ms_info` WHERE `receiver` = @receiver AND `time` = @time"; + query.setValue("receiver", nc->display); + query.setValue("time", m->time); + } + else + { + query = "DELETE FROM `anope_ms_info` WHERE `receiver` = @receiver"; + query.setValue("receiver", nc->display); + } + + this->RunQuery(query); + } + + void OnMemoDel(ChannelInfo *ci, MemoInfo *mi, Memo *m) + { + SQLQuery query; + + if (m) + { + query = "DELETE FROM `anope_ms_info` WHERE `receiver` = @receiver AND `time` = @time"; + query.setValue("receiver", ci->name); + query.setValue("time", m->time); + } + else + { + query = "DELETE FROM `anope_ms_info` WHERE `receiver` = @receiver"; + query.setValue("receiver", ci->name); + } + + this->RunQuery(query); + } + + EventReturn OnExceptionAdd(Exception *ex) + { + SQLQuery query("INSERT INTO `anope_os_exceptions` (mask, slimit, who, reason, time, expires) VALUES(@mask, @slimit, @who, @reason, @time, @expires)"); + query.setValue("mask", ex->mask); + query.setValue("slimit", ex->limit); + query.setValue("who", ex->who); + query.setValue("reason", ex->reason); + query.setValue("time", ex->time); + query.setValue("expires", ex->expires); + return EVENT_CONTINUE; + } + + void OnExceptionDel(User *, Exception *ex) + { + SQLQuery query("DELETE FROM `anope_os_exceptions` WHERE `mask` = @mask"); + query.setValue("mask", ex->mask); + this->RunQuery(query); + } + + EventReturn OnAddXLine(XLine *x, XLineManager *xlm) + { + SQLQuery query("INSERT INTO `anope_os_xlines` (type, mask, xby, reason, seton, expire) VALUES(@type, @mask, @xby, @reason, @seton, @expire)"); + query.setValue("type", Anope::string(xlm->Type())); + query.setValue("mask", x->Mask); + query.setValue("xby", x->By); + query.setValue("reason", x->Reason); + query.setValue("seton", x->Created); + query.setValue("expire", x->Expires); + this->RunQuery(query); + return EVENT_CONTINUE; + } + + void OnDelXLine(User *, XLine *x, XLineManager *xlm) + { + SQLQuery query; + + if (x) + { + query = "DELETE FROM `anope_os_xlines` WHERE `mask` = @mask AND `type` = @type"; + query.setValue("mask", x->Mask); + query.setValue("type", Anope::string(xlm->Type())); + } + else + { + query = "DELETE FROM `anope_os_xlines` WHERE `type` = @type"; + query.setValue("type", Anope::string(xlm->Type())); + } + + this->RunQuery(query); + } + + void OnDeleteVhost(NickAlias *na) + { + SQLQuery query("DELETE FROM `anope_hs_core` WHERE `nick` = @nick"); + query.setValue("nick", na->nick); + this->RunQuery(query); + } + + void OnSetVhost(NickAlias *na) + { + SQLQuery query("INSERT INTO `anope_hs_core` (nick, vident, vhost, creator, time) VALUES(@nick, @vident, @vhost, @creator, @time)"); + query.setValue("nick", na->nick); + query.setValue("vident", na->hostinfo.GetIdent()); + query.setValue("vhost", na->hostinfo.GetHost()); + query.setValue("creator", na->hostinfo.GetCreator()); + query.setValue("time", na->hostinfo.GetTime()); + this->RunQuery(query); + } +}; + +void MySQLInterface::OnResult(const SQLResult &r) +{ + Log(LOG_DEBUG) << "MySQL successfully executed query: " << r.finished_query; +} + +void MySQLInterface::OnError(const SQLResult &r) +{ + if (!r.GetQuery().query.empty()) + Log(LOG_DEBUG) << "Error executing query " << r.finished_query << ": " << r.GetError(); + else + Log(LOG_DEBUG) << "Error executing query: " << r.GetError(); +} + + +static void Write(const Anope::string &data) +{ + SQLQuery query("INSERT INTO `anope_extra` (data) VALUES(@data)"); + query.setValue("data", data); + me->RunQuery(data); +} + +static void WriteNickMetadata(const Anope::string &key, const Anope::string &data) +{ + if (!CurNick) + throw CoreException("WriteNickMetadata without a nick to write"); + + SQLQuery query("INSERT INTO `anope_ns_alias_metadata` (nick, name, value) VALUES(@nick, @name, @value)"); + query.setValue("nick", CurNick->nick); + query.setValue("name", key); + query.setValue("value", data); + me->RunQuery(query); +} + +static void WriteCoreMetadata(const Anope::string &key, const Anope::string &data) +{ + if (!CurCore) + throw CoreException("WritCoreMetadata without a core to write"); + + SQLQuery query("INSERT INTO `anope_ns_core_metadata` (nick, name, value) VALUES(@nick, @name, @value)"); + query.setValue("nick", CurCore->display); + query.setValue("name", key); + query.setValue("value", data); + me->RunQuery(query); +} + +static void WriteChannelMetadata(const Anope::string &key, const Anope::string &data) +{ + if (!CurChannel) + throw CoreException("WriteChannelMetadata without a channel to write"); + + SQLQuery query("INSERT INTO `anope_cs_info_metadata` (channel, name, value) VALUES(@channel, @name, @value)"); + query.setValue("channel", CurChannel->name); + query.setValue("name", key); + query.setValue("value", data); + me->RunQuery(query); +} + +static void WriteBotMetadata(const Anope::string &key, const Anope::string &data) +{ + if (!CurBot) + throw CoreException("WriteBotMetadata without a bot to write"); + + SQLQuery query("INSERT INTO `anope_bs_info_metadata` (botname, name, value) VALUES(@botname, @name, @value)"); + query.setValue("botname", CurBot->nick); + query.setValue("name", key); + query.setValue("value", data); + me->RunQuery(query); +} + +static void SaveDatabases() +{ + SQLQuery query; + + query = "TRUNCATE TABLE `anope_ns_core`"; + me->RunQuery(query); + + query = "TRUNCATE TABLE `anope_ms_info`"; + me->RunQuery(query); + + query = "TRUNCATE TABLE `anope_ns_alias`"; + me->RunQuery(query); + + for (nickcore_map::const_iterator nit = NickCoreList.begin(), nit_end = NickCoreList.end(); nit != nit_end; ++nit) + { + NickCore *nc = nit->second; + + me->InsertCore(nc); + + for (std::list<NickAlias *>::iterator it = nc->aliases.begin(), it_end = nc->aliases.end(); it != it_end; ++it) + { + me->InsertAlias(*it); + if ((*it)->hostinfo.HasVhost()) + me->OnSetVhost(*it); + } + + for (std::vector<Anope::string>::iterator it = nc->access.begin(), it_end = nc->access.end(); it != it_end; ++it) + { + query = "INSERT INTO `anope_ns_access` (display, access) VALUES(@display, @access)"; + query.setValue("display", nc->display); + query.setValue("access", *it); + me->RunQuery(query); + } + + for (unsigned j = 0, end = nc->memos.memos.size(); j < end; ++j) + { + Memo *m = nc->memos.memos[j]; + + me->OnMemoSend(m->sender, nc->display, &nc->memos, m); + } + } + + query = "TRUNCATE TABLE `anope_bs_core`"; + me->RunQuery(query); + + for (Anope::insensitive_map<BotInfo *>::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it) + me->OnBotCreate(it->second); + + query = "TRUNCATE TABLE `anope_cs_info`"; + me->RunQuery(query); + + query = "TRUNCATE TABLE `anope_bs_badwords`"; + me->RunQuery(query); + + query = "TRUNCATE TABLE `anope_cs_access`"; + me->RunQuery(query); + + query = "TRUNCATE TABLE `anope_cs_akick`"; + me->RunQuery(query); + + query = "TRUNCATE TABLE `anope_cs_levels`"; + me->RunQuery(query); + + for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end; ++it) + { + ChannelInfo *ci = it->second; + + me->OnChanRegistered(ci); + + for (unsigned j = 0, end = ci->GetBadWordCount(); j < end; ++j) + { + BadWord *bw = ci->GetBadWord(j); + + me->OnBadWordAdd(ci, bw); + } + + for (unsigned j = 0, end = ci->GetAccessCount(); j < end; ++j) + { + ChanAccess *access = ci->GetAccess(j); + + me->OnAccessAdd(ci, NULL, access); + } + + for (unsigned j = 0, end = ci->GetAkickCount(); j < end; ++j) + { + AutoKick *ak = ci->GetAkick(j); + + me->OnAkickAdd(ci, ak); + } + + for (int k = 0; k < CA_SIZE; ++k) + me->OnLevelChange(NULL, ci, -1, -1); + + for (unsigned j = 0, end = ci->memos.memos.size(); j < end; ++j) + { + Memo *m = ci->memos.memos[j]; + + me->OnMemoSend(m->sender, ci->name, &ci->memos, m); + } + } + + for (std::list<XLineManager *>::iterator it = XLineManager::XLineManagers.begin(), it_end = XLineManager::XLineManagers.end(); it != it_end; ++it) + for (unsigned i = 0, end = (*it)->GetCount(); i < end; ++i) + me->OnAddXLine((*it)->GetEntry(i), *it); + + if (me->SessionInterface) + for (SessionService::ExceptionVector::iterator it = me->SessionInterface->GetExceptions().begin(); it != me->SessionInterface->GetExceptions().end(); ++it) + me->OnExceptionAdd(*it); +} + +void CommandSQLSync::Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) +{ + SaveDatabases(); + source.Reply(_("Updating MySQL.")); + return; +} + +MODULE_INIT(DBMySQL) + diff --git a/modules/database/db_mysql_live.cpp b/modules/database/db_mysql_live.cpp new file mode 100644 index 000000000..ecfdc70d1 --- /dev/null +++ b/modules/database/db_mysql_live.cpp @@ -0,0 +1,287 @@ +#include "module.h" +#include "../extra/async_commands.h" +#include "../extra/sql.h" + +class SQLCache : public Timer +{ + typedef std::map<Anope::string, time_t, std::less<ci::string> > cache_map; + cache_map cache; + public: + + SQLCache() : Timer(300, Anope::CurTime, true) { } + + bool Check(const Anope::string &item) + { + cache_map::iterator it = this->cache.find(item); + if (it != this->cache.end() && Anope::CurTime - it->second < 5) + return true; + + this->cache[item] = Anope::CurTime; + return false; + } + + void Tick(time_t) + { + for (cache_map::iterator it = this->cache.begin(), next_it; it != this->cache.end(); it = next_it) + { + next_it = it; + ++next_it; + + if (Anope::CurTime - it->second > 5) + this->cache.erase(it); + } + } +}; + +static void ChanInfoUpdate(const SQLResult &res) +{ + try + { + ChannelInfo *ci = cs_findchan(res.Get(0, "name")); + if (!ci) + ci = new ChannelInfo(res.Get(0, "name")); + ci->SetFounder(findcore(res.Get(0, "founder"))); + ci->successor = findcore(res.Get(0, "successor")); + ci->desc = res.Get(0, "descr"); + ci->time_registered = convertTo<time_t>(res.Get(0, "time_registered")); + ci->ClearFlags(); + ci->FromString(BuildStringVector(res.Get(0, "flags"))); + ci->bantype = convertTo<int>(res.Get(0, "bantype")); + ci->memos.memomax = convertTo<unsigned>(res.Get(0, "memomax")); + + if (res.Get(0, "botnick").equals_cs(ci->bi ? ci->bi->nick : "") == false) + { + if (ci->bi) + ci->bi->UnAssign(NULL, ci); + BotInfo *bi = findbot(res.Get(0, "botnick")); + if (bi) + bi->Assign(NULL, ci); + } + + ci->capsmin = convertTo<int16>(res.Get(0, "capsmin")); + ci->capspercent = convertTo<int16>(res.Get(0, "capspercent")); + ci->floodlines = convertTo<int16>(res.Get(0, "floodlines")); + ci->floodsecs = convertTo<int16>(res.Get(0, "floodsecs")); + ci->repeattimes = convertTo<int16>(res.Get(0, "repeattimes")); + + if (ci->c) + check_modes(ci->c); + } + catch (const SQLException &ex) + { + Log(LOG_DEBUG) << ex.GetReason(); + } + catch (const ConvertException &) { } +} + +static void NickInfoUpdate(const SQLResult &res) +{ + try + { + NickCore *nc = findcore(res.Get(0, "display")); + if (!nc) + return; + NickAlias *na = findnick(res.Get(0, "nick")); + if (!na) + na = new NickAlias(res.Get(0, "nick"), nc); + + na->last_quit = res.Get(0, "last_quit"); + na->last_realname = res.Get(0, "last_realname"); + na->last_usermask = res.Get(0, "last_usermask"); + na->time_registered = convertTo<time_t>(res.Get(0, "time_registered")); + na->last_seen = convertTo<time_t>(res.Get(0, "last_seen")); + na->ClearFlags(); + na->FromString(BuildStringVector(res.Get(0, "flags"))); + + if (na->nc != nc) + { + std::list<NickAlias *>::iterator it = std::find(na->nc->aliases.begin(), na->nc->aliases.end(), na); + if (it != na->nc->aliases.end()) + na->nc->aliases.erase(it); + + na->nc = nc; + na->nc->aliases.push_back(na); + } + } + catch (const SQLException &ex) + { + Log(LOG_DEBUG) << ex.GetReason(); + } + catch (const ConvertException &) { } +} + +static void NickCoreUpdate(const SQLResult &res) +{ + try + { + NickCore *nc = findcore(res.Get(0, "display")); + if (!nc) + nc = new NickCore(res.Get(0, "display")); + + nc->pass = res.Get(0, "pass"); + nc->email = res.Get(0, "email"); + nc->greet = res.Get(0, "greet"); + nc->ClearFlags(); + nc->FromString(BuildStringVector(res.Get(0, "flags"))); + nc->language = res.Get(0, "language"); + } + catch (const SQLException &ex) + { + Log(LOG_DEBUG) << ex.GetReason(); + } + catch (const ConvertException &) { } +} + +class MySQLLiveModule : public Module +{ + service_reference<SQLProvider> SQL; + service_reference<AsynchCommandsService> ACS; + + SQLCache chan_cache, nick_cache, core_cache; + + SQLResult RunQuery(const SQLQuery &query) + { + if (!this->SQL) + throw SQLException("Unable to locate SQL reference, is m_mysql loaded and configured correctly?"); + + SQLResult res = SQL->RunQuery(query); + if (!res.GetError().empty()) + throw SQLException(res.GetError()); + return res; + } + + CommandMutex *CurrentCommand() + { + if (this->ACS) + return this->ACS->CurrentCommand(); + return NULL; + } + + public: + MySQLLiveModule(const Anope::string &modname, const Anope::string &creator) : + Module(modname, creator, DATABASE), SQL("mysql/main"), ACS("asynch_commands") + { + Implementation i[] = { I_OnFindChan, I_OnFindNick, I_OnFindCore, I_OnShutdown }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + } + + void OnShutdown() + { + Implementation i[] = { I_OnFindChan, I_OnFindNick, I_OnFindCore }; + for (size_t j = 0; j < 3; ++j) + ModuleManager::Detach(i[j], this); + } + + void OnFindChan(const Anope::string &chname) + { + if (chan_cache.Check(chname)) + return; + + try + { + SQLQuery query("SELECT * FROM `anope_cs_info` WHERE `name` = @name"); + query.setValue("name", chname); + CommandMutex *current_command = this->CurrentCommand(); + if (current_command) + { + current_command->Unlock(); + try + { + SQLResult res = this->RunQuery(query); + current_command->Lock(); + ChanInfoUpdate(res); + } + catch (const SQLException &) + { + current_command->Lock(); + throw; + } + } + else + { + SQLResult res = this->RunQuery(query); + ChanInfoUpdate(res); + } + } + catch (const SQLException &ex) + { + Log(LOG_DEBUG) << "OnFindChan: " << ex.GetReason(); + } + } + + void OnFindNick(const Anope::string &nick) + { + if (nick_cache.Check(nick)) + return; + + try + { + SQLQuery query("SELECT * FROM `anope_ns_alias` WHERE `nick` = @nick"); + query.setValue("nick", nick); + CommandMutex *current_command = this->CurrentCommand(); + if (current_command) + { + current_command->Unlock(); + try + { + SQLResult res = this->RunQuery(query); + current_command->Lock(); + NickInfoUpdate(res); + } + catch (const SQLException &) + { + current_command->Lock(); + throw; + } + } + else + { + SQLResult res = this->RunQuery(query); + NickInfoUpdate(res); + } + } + catch (const SQLException &ex) + { + Log(LOG_DEBUG) << "OnFindNick: " << ex.GetReason(); + } + } + + void OnFindCore(const Anope::string &nick) + { + if (core_cache.Check(nick)) + return; + + try + { + SQLQuery query("SELECT * FROM `anope_ns_core` WHERE `display` = @display"); + query.setValue("display", nick); + CommandMutex *current_command = this->CurrentCommand(); + if (current_command) + { + current_command->Unlock(); + try + { + SQLResult res = this->RunQuery(query); + current_command->Lock(); + NickCoreUpdate(res); + } + catch (const SQLException &) + { + current_command->Lock(); + throw; + } + } + else + { + SQLResult res = this->RunQuery(query); + NickCoreUpdate(res); + } + } + catch (const SQLException &ex) + { + Log(LOG_DEBUG) << "OnFindCore: " << ex.GetReason(); + } + } +}; + +MODULE_INIT(MySQLLiveModule) diff --git a/modules/database/db_plain.cpp b/modules/database/db_plain.cpp new file mode 100644 index 000000000..e2ade7974 --- /dev/null +++ b/modules/database/db_plain.cpp @@ -0,0 +1,908 @@ +/* + * (C) 2003-2011 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 "../commands/os_session.h" + +Anope::string DatabaseFile; +std::stringstream db_buffer; +service_reference<SessionService> SessionInterface("session"); + +/** Enum used for what METADATA type we are reading + */ +enum MDType +{ + MD_NONE, + MD_NC, + MD_NA, + MD_BI, + MD_CH +}; + +/* Character for newlines, in my research I have concluded it is significantly faster + * to use \n instead of std::endl because std::endl also flushes the buffer, which + * is not necessary here + */ +static const char endl = '\n'; + +/** Read from the database and call the events + * @param m If specified it only calls the read events for the specific module + */ +static void ReadDatabase(Module *m = NULL) +{ + EventReturn MOD_RESULT; + MDType Type = MD_NONE; + + std::fstream db; + db.open(DatabaseFile.c_str(), std::ios_base::in); + + if (!db.is_open()) + { + Log() << "Unable to open " << DatabaseFile << " for reading!"; + return; + } + + NickCore *nc = NULL; + NickAlias *na = NULL; + BotInfo *bi = NULL; + ChannelInfo *ci = NULL; + + Anope::string buf; + while (std::getline(db, buf.str())) + { + if (buf.empty()) + continue; + + spacesepstream sep(buf); + std::vector<Anope::string> params; + while (sep.GetToken(buf)) + { + if (buf[0] == ':') + { + buf.erase(buf.begin()); + if (!buf.empty() && !sep.StreamEnd()) + params.push_back(buf + " " + sep.GetRemaining()); + else if (!sep.StreamEnd()) + params.push_back(sep.GetRemaining()); + else if (!buf.empty()) + params.push_back(buf); + break; + } + else + params.push_back(buf); + } + + if (m) + MOD_RESULT = m->OnDatabaseRead(params); + else + FOREACH_RESULT(I_OnDatabaseRead, OnDatabaseRead(params)); + if (MOD_RESULT == EVENT_STOP) + continue; + + if (!params.empty()) + { + if (params[0].equals_ci("NC")) + { + nc = findcore(params[1]); + Type = MD_NC; + } + else if (params[0].equals_ci("NA")) + { + na = findnick(params[2]); + Type = MD_NA; + } + else if (params[0].equals_ci("BI")) + { + bi = findbot(params[1]); + Type = MD_BI; + } + else if (params[0].equals_ci("CH")) + { + ci = cs_findchan(params[1]); + Type = MD_CH; + } + else if (params[0].equals_ci("MD")) + { + Anope::string key = params[1]; + params.erase(params.begin()); + params.erase(params.begin()); + + if (Type == MD_NC && nc) + { + try + { + if (m) + m->OnDatabaseReadMetadata(nc, key, params); + else + FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(nc, key, params)); + } + catch (const DatabaseException &ex) + { + Log() << "[db_plain]: " << ex.GetReason(); + } + } + else if (Type == MD_NA && na) + { + try + { + if (m) + m->OnDatabaseReadMetadata(na, key, params); + else + FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(na, key, params)); + } + catch (const DatabaseException &ex) + { + Log() << "[db_plain]: " << ex.GetReason(); + } + } + else if (Type == MD_BI && bi) + { + try + { + if (m) + m->OnDatabaseReadMetadata(bi, key, params); + else + FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(bi, key, params)); + } + catch (const DatabaseException &ex) + { + Log() << "[db_plain]: " << ex.GetReason(); + } + } + else if (Type == MD_CH && ci) + { + try + { + if (m) + m->OnDatabaseReadMetadata(ci, key, params); + else + FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(ci, key, params)); + } + catch (const DatabaseException &ex) + { + Log() << "[db_plain]: " << ex.GetReason(); + } + } + } + } + } + + db.close(); +} + +struct ChannelLevel +{ + ChannelAccess Level; + Anope::string Name; +}; + +ChannelLevel ChannelLevels[] = { + { CA_ACCESS_LIST, "ACCESS_LIST" }, + { CA_NOKICK, "NOKICK" }, + { CA_FANTASIA, "FANTASIA" }, + { CA_GREET, "GREET" }, + { CA_AUTOVOICE, "AUTOVOICE" }, + { CA_VOICEME, "VOICEME" }, + { CA_VOICE, "VOICE" }, + { CA_INFO, "INFO" }, + { CA_SAY, "SAY" }, + { CA_AUTOHALFOP, "AUTOHALFOP" }, + { CA_HALFOPME, "HALFOPME" }, + { CA_HALFOP, "HALFOP" }, + { CA_KICK, "KICK" }, + { CA_SIGNKICK, "SIGNKICK" }, + { CA_BAN, "BAN" }, + { CA_TOPIC, "TOPIC" }, + { CA_MODE, "MODE" }, + { CA_GETKEY, "GETKEY" }, + { CA_INVITE, "INVITE" }, + { CA_UNBAN, "UNBAN" }, + { CA_AUTOOP, "AUTOOP" }, + { CA_OPDEOPME, "OPDEOPME" }, + { CA_OPDEOP, "OPDEOP" }, + { CA_AUTOPROTECT, "AUTOPROTECT" }, + { CA_AKICK, "AKICK" }, + { CA_BADWORDS, "BADWORDS" }, + { CA_ASSIGN, "ASSIGN" }, + { CA_MEMO, "MEMO" }, + { CA_ACCESS_CHANGE, "ACCESS_CHANGE" }, + { CA_PROTECTME, "PROTECTME" }, + { CA_PROTECT, "PROTECT" }, + { CA_SET, "SET" }, + { CA_AUTOOWNER, "AUTOPROTECT" }, + { CA_OWNERME, "OWNERME" }, + { CA_OWNER, "OWNER" }, + { CA_FOUNDER, "FOUNDER" }, + { CA_SIZE, "" } +}; + +static Anope::string ToString(const std::vector<Anope::string> &strings) +{ + Anope::string ret; + + for (unsigned i = 0; i < strings.size(); ++i) + ret += " " + strings[i]; + + if (!ret.empty()) + ret.erase(ret.begin()); + + return ret; +} + +static void LoadNickCore(const std::vector<Anope::string> ¶ms) +{ + NickCore *nc = new NickCore(params[0]); + /* Clear default flags */ + nc->ClearFlags(); + + nc->pass = params.size() > 1 ? params[1] : ""; + + Log(LOG_DEBUG_2) << "[db_plain]: Loaded NickCore " << nc->display; +} + +static void LoadNickAlias(const std::vector<Anope::string> ¶ms) +{ + NickCore *nc = findcore(params[0]); + if (!nc) + { + Log() << "[db_plain]: Unable to find core " << params[0]; + return; + } + + NickAlias *na = new NickAlias(params[1], nc); + + na->time_registered = params[2].is_pos_number_only() ? convertTo<time_t>(params[2]) : 0; + + na->last_seen = params[3].is_pos_number_only() ? convertTo<time_t>(params[3]) : 0; + + Log(LOG_DEBUG_2) << "[db_plain}: Loaded nickalias for " << na->nick; +} + +static void LoadBotInfo(const std::vector<Anope::string> ¶ms) +{ + BotInfo *bi = findbot(params[0]); + if (!bi) + bi = new BotInfo(params[0], params[1], params[2]); + + bi->created = params[3].is_pos_number_only() ? convertTo<time_t>(params[3]) : 0; + bi->chancount = params[4].is_pos_number_only() ? convertTo<uint32>(params[4]) : 0; + bi->realname = params[5]; + + Log(LOG_DEBUG_2) << "[db_plain]: Loaded botinfo for " << bi->nick; +} + +static void LoadChanInfo(const std::vector<Anope::string> ¶ms) +{ + ChannelInfo *ci = new ChannelInfo(params[0]); + /* CLear default mlock */ + ci->ClearMLock(); + /* Remove default channel flags */ + ci->ClearFlags(); + ci->botflags.ClearFlags(); + + ci->time_registered = params[1].is_pos_number_only() ? convertTo<time_t>(params[1]) : 0; + + ci->last_used = params[2].is_pos_number_only() ? convertTo<time_t>(params[2]) : 0; + + Log(LOG_DEBUG_2) << "[db_plain]: loaded channel " << ci->name; +} + +static void LoadOperInfo(const std::vector<Anope::string> ¶ms) +{ + if (params[0].equals_ci("STATS")) + { + maxusercnt = params[1].is_pos_number_only() ? convertTo<uint32>(params[1]) : 0; + maxusertime = params[2].is_pos_number_only() ? convertTo<time_t>(params[2]) : 0; + } + else if (params[0].equals_ci("SXLINE")) + { + char type = params[1][0]; + for (std::list<XLineManager *>::iterator it = XLineManager::XLineManagers.begin(), it_end = XLineManager::XLineManagers.end(); it != it_end; ++it) + { + XLineManager *xl = *it; + if (xl->Type() == type) + { + Anope::string mask = params[2]; + Anope::string by = params[3]; + time_t seton = params[4].is_pos_number_only() ? convertTo<time_t>(params[4]) : 0; + time_t expires = params[5].is_pos_number_only() ? convertTo<time_t>(params[5]) : 0; + Anope::string reason = params[6]; + + XLine *x = xl->Add(mask, by, expires, reason); + if (x) + x->Created = seton; + break; + } + } + } + else if (params[0].equals_ci("EXCEPTION") && SessionInterface) + { + Exception *exception = new Exception(); + exception->mask = params[1]; + exception->limit = params[2].is_pos_number_only() ? convertTo<int>(params[2]) : 1; + exception->who = params[3]; + exception->time = params[4].is_pos_number_only() ? convertTo<time_t>(params[4]) : 0; + exception->expires = params[5].is_pos_number_only() ? convertTo<time_t>(params[5]) : 0; + exception->reason = params[6]; + SessionInterface->AddException(exception); + } +} + +void Write(const Anope::string &buf) +{ + db_buffer << buf << endl; +} + +void WriteMetadata(const Anope::string &key, const Anope::string &data) +{ + Write("MD " + key + " " + data); +} + +class DBPlain : public Module +{ + /* Day the last backup was on */ + int LastDay; + /* Backup file names */ + std::list<Anope::string> Backups; + public: + DBPlain(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE) + { + this->SetAuthor("Anope"); + + Implementation i[] = { I_OnReload, I_OnDatabaseRead, I_OnLoadDatabase, I_OnDatabaseReadMetadata, I_OnSaveDatabase, I_OnModuleLoad }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + OnReload(); + + LastDay = 0; + } + + ~DBPlain() + { + } + + void BackupDatabase() + { + /* Do not backup a database that doesn't exist */ + if (!IsFile(DatabaseFile)) + return; + + time_t now = Anope::CurTime; + tm *tm = localtime(&now); + + if (tm->tm_mday != LastDay) + { + LastDay = tm->tm_mday; + Anope::string newname = "backups/" + DatabaseFile + "." + stringify(tm->tm_year) + stringify(tm->tm_mon) + stringify(tm->tm_mday); + + /* Backup already exists */ + if (IsFile(newname)) + return; + + Log(LOG_DEBUG) << "db_plain: Attemping to rename " << DatabaseFile << " to " << newname; + if (rename(DatabaseFile.c_str(), newname.c_str())) + { + Log() << "Unable to back up database!"; + + if (!Config->NoBackupOkay) + quitting = true; + + return; + } + + Backups.push_back(newname); + + unsigned KeepBackups = Config->KeepBackups; + if (KeepBackups && Backups.size() > KeepBackups) + { + DeleteFile(Backups.front().c_str()); + Backups.pop_front(); + } + } + } + + void OnReload() + { + ConfigReader config; + DatabaseFile = config.ReadValue("db_plain", "database", "anope.db", 0); + } + + EventReturn OnDatabaseRead(const std::vector<Anope::string> ¶ms) + { + Anope::string key = params[0]; + std::vector<Anope::string> otherparams = params; + otherparams.erase(otherparams.begin()); + + if (key.equals_ci("NC")) + LoadNickCore(otherparams); + else if (key.equals_ci("NA")) + LoadNickAlias(otherparams); + else if (key.equals_ci("BI")) + LoadBotInfo(otherparams); + else if (key.equals_ci("CH")) + LoadChanInfo(otherparams); + else if (key.equals_ci("OS")) + LoadOperInfo(otherparams); + + return EVENT_CONTINUE; + } + + EventReturn OnLoadDatabase() + { + ReadDatabase(); + + /* No need to ever reload this again, although this should never be trigged again */ + ModuleManager::Detach(I_OnLoadDatabase, this); + ModuleManager::Detach(I_OnDatabaseReadMetadata, this); + + return EVENT_STOP; + } + + EventReturn OnDatabaseReadMetadata(NickCore *nc, const Anope::string &key, const std::vector<Anope::string> ¶ms) + { + try + { + if (key.equals_ci("LANGUAGE")) + nc->language = params[0]; + else if (key.equals_ci("MEMOMAX")) + nc->memos.memomax = params[0].is_pos_number_only() ? convertTo<int16>(params[0]) : -1; + else if (key.equals_ci("EMAIL")) + nc->email = params[0]; + else if (key.equals_ci("GREET")) + nc->greet = params[0]; + else if (key.equals_ci("ACCESS")) + nc->AddAccess(params[0]); + else if (key.equals_ci("CERT")) + nc->AddCert(params[0]); + else if (key.equals_ci("FLAGS")) + nc->FromString(params); + else if (key.equals_ci("MI")) + { + Memo *m = new Memo; + m->time = params[0].is_pos_number_only() ? convertTo<time_t>(params[0]) : 0; + m->sender = params[1]; + for (unsigned j = 2; params[j].equals_ci("UNREAD") || params[j].equals_ci("RECEIPT"); ++j) + { + if (params[j].equals_ci("UNREAD")) + m->SetFlag(MF_UNREAD); + else if (params[j].equals_ci("RECEIPT")) + m->SetFlag(MF_RECEIPT); + } + m->text = params[params.size() - 1]; + nc->memos.memos.push_back(m); + } + else if (key.equals_ci("MIG")) + nc->memos.ignores.push_back(params[0].ci_str()); + } + catch (const ConvertException &ex) + { + throw DatabaseException(ex.GetReason()); + } + + return EVENT_CONTINUE; + } + + EventReturn OnDatabaseReadMetadata(NickAlias *na, const Anope::string &key, const std::vector<Anope::string> ¶ms) + { + if (key.equals_ci("LAST_USERMASK")) + na->last_usermask = params[0]; + else if (key.equals_ci("LAST_REALNAME")) + na->last_realname = params[0]; + else if (key.equals_ci("LAST_QUIT")) + na->last_quit = params[0]; + else if (key.equals_ci("FLAGS")) + na->FromString(params); + else if (key.equals_ci("VHOST")) + na->hostinfo.SetVhost(params.size() > 3 ? params[3] : "", params[2], params[0], params[1].is_pos_number_only() ? convertTo<time_t>(params[1]) : 0); + + return EVENT_CONTINUE; + } + + EventReturn OnDatabaseReadMetadata(BotInfo *bi, const Anope::string &key, const std::vector<Anope::string> ¶ms) + { + if (key.equals_ci("FLAGS")) + bi->FromString(params); + + return EVENT_CONTINUE; + } + + EventReturn OnDatabaseReadMetadata(ChannelInfo *ci, const Anope::string &key, const std::vector<Anope::string> ¶ms) + { + try + { + if (key.equals_ci("BANTYPE")) + ci->bantype = params[0].is_pos_number_only() ? convertTo<int16>(params[0]) : Config->CSDefBantype; + else if (key.equals_ci("MEMOMAX")) + ci->memos.memomax = params[0].is_pos_number_only() ? convertTo<int16>(params[0]) : -1; + else if (key.equals_ci("FOUNDER")) + ci->SetFounder(findcore(params[0])); + else if (key.equals_ci("SUCCESSOR")) + ci->successor = findcore(params[0]); + else if (key.equals_ci("LEVELS")) + { + for (unsigned j = 0, end = params.size(); j < end; j += 2) + for (int i = 0; ChannelLevels[i].Level != CA_SIZE; ++i) + if (params[j].equals_ci(ChannelLevels[i].Name)) + ci->levels[ChannelLevels[i].Level] = params[j + 1].is_number_only() ? convertTo<int16>(params[j + 1]) : 0; + } + else if (key.equals_ci("FLAGS")) + ci->FromString(params); + else if (key.equals_ci("DESC")) + ci->desc = params[0]; + else if (key.equals_ci("TOPIC")) + { + ci->last_topic_setter = params[0]; + ci->last_topic_time = params[1].is_pos_number_only() ? convertTo<time_t>(params[1]) : 0; + ci->last_topic = params[2]; + } + else if (key.equals_ci("SUSPEND")) + { + ci->Extend("suspend_by", new ExtensibleItemRegular<Anope::string>(params[0])); + ci->Extend("suspend_reason", new ExtensibleItemRegular<Anope::string>(params[1])); + } + else if (key.equals_ci("ACCESS")) + { + service_reference<AccessProvider> provider(params[0]); + if (!provider) + throw DatabaseException("Access entry for nonexistant provider " + params[0]); + + ChanAccess *access = provider->Create(); + access->ci = ci; + access->mask = params[1]; + access->Unserialize(params[2]); + access->last_seen = params[3].is_pos_number_only() ? convertTo<time_t>(params[3]) : 0; + access->creator = params[4]; + access->created = params.size() > 5 && params[5].is_pos_number_only() ? convertTo<time_t>(params[5]) : Anope::CurTime; + + ci->AddAccess(access); + } + else if (key.equals_ci("AKICK")) + { + // 0 is the old stuck + bool Nick = params[1].equals_ci("NICK"); + NickCore *nc = NULL; + if (Nick) + { + nc = findcore(params[2]); + if (!nc) + throw DatabaseException("Akick for nonexistant core " + params[2] + " on " + ci->name); + } + + AutoKick *ak; + if (Nick) + ak = ci->AddAkick(params[3], nc, params.size() > 6 ? params[6] : "", params[4].is_pos_number_only() ? convertTo<time_t>(params[4]) : 0, params[5].is_pos_number_only() ? convertTo<time_t>(params[5]) : 0); + else + ak = ci->AddAkick(params[3], params[2], params.size() > 6 ? params[6] : "", params[4].is_pos_number_only() ? convertTo<time_t>(params[4]) : 0, params[5].is_pos_number_only() ? convertTo<time_t>(params[5]) : 0); + if (Nick) + ak->SetFlag(AK_ISNICK); + + } + else if (key.equals_ci("MLOCK")) + { + std::vector<Anope::string> mlocks; + ci->GetExtRegular("db_mlock", mlocks); + + Anope::string mlock_string = params[0] + " " + params[1] + " " + params[2] + " " + params[3]; + if (params.size() > 4) + mlock_string += " " + params[4]; + + mlocks.push_back(mlock_string); + ci->Extend("db_mlock", new ExtensibleItemRegular<std::vector<Anope::string> >(mlocks)); + } + else if (key.equals_ci("MI")) + { + Memo *m = new Memo; + m->time = params[0].is_pos_number_only() ? convertTo<time_t>(params[0]) : 0; + m->sender = params[1]; + for (unsigned j = 2; params[j].equals_ci("UNREAD") || params[j].equals_ci("RECEIPT"); ++j) + { + if (params[j].equals_ci("UNREAD")) + m->SetFlag(MF_UNREAD); + else if (params[j].equals_ci("RECEIPT")) + m->SetFlag(MF_RECEIPT); + } + m->text = params[params.size() - 1]; + ci->memos.memos.push_back(m); + } + else if (key.equals_ci("MIG")) + ci->memos.ignores.push_back(params[0].ci_str()); + else if (key.equals_ci("BI")) + { + if (params[0].equals_ci("NAME")) + ci->bi = findbot(params[1]); + else if (params[0].equals_ci("FLAGS")) + ci->botflags.FromString(params); + else if (params[0].equals_ci("TTB")) + { + for (unsigned j = 1, end = params.size(); j < end; j += 2) + { + if (params[j].equals_ci("BOLDS")) + ci->ttb[0] = params[j + 1].is_pos_number_only() ? convertTo<int16>(params[j + 1]) : 0; + else if (params[j].equals_ci("COLORS")) + ci->ttb[1] = params[j + 1].is_pos_number_only() ? convertTo<int16>(params[j + 1]) : 0; + else if (params[j].equals_ci("REVERSES")) + ci->ttb[2] = params[j + 1].is_pos_number_only() ? convertTo<int16>(params[j + 1]) : 0; + else if (params[j].equals_ci("UNDERLINES")) + ci->ttb[3] = params[j + 1].is_pos_number_only() ? convertTo<int16>(params[j + 1]) : 0; + else if (params[j].equals_ci("BADWORDS")) + ci->ttb[4] = params[j + 1].is_pos_number_only() ? convertTo<int16>(params[j + 1]) : 0; + else if (params[j].equals_ci("CAPS")) + ci->ttb[5] = params[j + 1].is_pos_number_only() ? convertTo<int16>(params[j + 1]) : 0; + else if (params[j].equals_ci("FLOOD")) + ci->ttb[6] = params[j + 1].is_pos_number_only() ? convertTo<int16>(params[j + 1]) : 0; + else if (params[j].equals_ci("REPEAT")) + ci->ttb[7] = params[j + 1].is_pos_number_only() ? convertTo<int16>(params[j + 1]) : 0; + else if (params[j].equals_ci("ITALICS")) + ci->ttb[8] = params[j + 1].is_pos_number_only() ? convertTo<int16>(params[j + 1]) : 0; + else if (params[j].equals_ci("AMSGS")) + ci->ttb[9] = params[j + 1].is_pos_number_only() ? convertTo<int16>(params[j + 1]) : 0; + } + } + else if (params[0].equals_ci("CAPSMIN")) + ci->capsmin = params[1].is_pos_number_only() ? convertTo<int16>(params[1]) : 0; + else if (params[0].equals_ci("CAPSPERCENT")) + ci->capspercent = params[1].is_pos_number_only() ? convertTo<int16>(params[1]) : 0; + else if (params[0].equals_ci("FLOODLINES")) + ci->floodlines = params[1].is_pos_number_only() ? convertTo<int16>(params[1]) : 0; + else if (params[0].equals_ci("FLOODSECS")) + ci->floodsecs = params[1].is_pos_number_only() ? convertTo<int16>(params[1]) : 0; + else if (params[0].equals_ci("REPEATTIMES")) + ci->repeattimes = params[1].is_pos_number_only() ? convertTo<int16>(params[1]) : 0; + else if (params[0].equals_ci("BADWORD")) + { + BadWordType Type; + if (params[1].equals_ci("SINGLE")) + Type = BW_SINGLE; + else if (params[1].equals_ci("START")) + Type = BW_START; + else if (params[1].equals_ci("END")) + Type = BW_END; + else + Type = BW_ANY; + ci->AddBadWord(params[2], Type); + } + } + } + catch (const ConvertException &ex) + { + throw DatabaseException(ex.GetReason()); + } + + return EVENT_CONTINUE; + } + + EventReturn OnSaveDatabase() + { + BackupDatabase(); + + db_buffer << "VER 2" << endl; + + for (nickcore_map::const_iterator nit = NickCoreList.begin(), nit_end = NickCoreList.end(); nit != nit_end; ++nit) + { + NickCore *nc = nit->second; + + db_buffer << "NC " << nc->display << " " << nc->pass << endl; + + db_buffer << "MD MEMOMAX " << nc->memos.memomax << endl; + + if (!nc->language.empty()) + db_buffer << "MD LANGUAGE " << nc->language << endl; + if (!nc->email.empty()) + db_buffer << "MD EMAIL " << nc->email << endl; + if (!nc->greet.empty()) + db_buffer << "MD GREET :" << nc->greet << endl; + + if (!nc->access.empty()) + { + for (std::vector<Anope::string>::iterator it = nc->access.begin(), it_end = nc->access.end(); it != it_end; ++it) + db_buffer << "MD ACCESS " << *it << endl; + } + if (!nc->cert.empty()) + { + for (std::vector<Anope::string>::iterator it = nc->cert.begin(), it_end = nc->cert.end(); it != it_end; ++it) + db_buffer << "MD CERT " << *it << endl; + } + if (nc->FlagCount()) + db_buffer << "MD FLAGS " << ToString(nc->ToString()) << endl; + MemoInfo *mi = &nc->memos; + for (unsigned k = 0, end = mi->memos.size(); k < end; ++k) + { + db_buffer << "MD MI " << mi->memos[k]->time << " " << mi->memos[k]->sender; + if (mi->memos[k]->HasFlag(MF_UNREAD)) + db_buffer << " UNREAD"; + if (mi->memos[k]->HasFlag(MF_RECEIPT)) + db_buffer << " RECEIPT"; + db_buffer << " :" << mi->memos[k]->text << endl; + } + for (unsigned k = 0, end = mi->ignores.size(); k < end; ++k) + db_buffer << "MD MIG " << Anope::string(mi->ignores[k]) << endl; + FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteMetadata, nc)); + } + + for (nickalias_map::const_iterator it = NickAliasList.begin(), it_end = NickAliasList.end(); it != it_end; ++it) + { + NickAlias *na = it->second; + + db_buffer << "NA " << na->nc->display << " " << na->nick << " " << na->time_registered << " " << na->last_seen << endl; + if (!na->last_usermask.empty()) + db_buffer << "MD LAST_USERMASK " << na->last_usermask << endl; + if (!na->last_realname.empty()) + db_buffer << "MD LAST_REALNAME :" << na->last_realname << endl; + if (!na->last_quit.empty()) + db_buffer << "MD LAST_QUIT :" << na->last_quit << endl; + if (na->FlagCount()) + db_buffer << "MD FLAGS " << ToString(na->ToString()) << endl; + if (na->hostinfo.HasVhost()) + db_buffer << "MD VHOST " << na->hostinfo.GetCreator() << " " << na->hostinfo.GetTime() << " " << na->hostinfo.GetHost() << " :" << na->hostinfo.GetIdent() << endl; + + FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteMetadata, na)); + } + + for (Anope::insensitive_map<BotInfo *>::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it) + { + BotInfo *bi = it->second; + + if (bi->HasFlag(BI_CONF)) + continue; + + db_buffer << "BI " << bi->nick << " " << bi->GetIdent() << " " << bi->host << " " << bi->created << " " << bi->chancount << " :" << bi->realname << endl; + if (bi->FlagCount()) + db_buffer << "MD FLAGS " << ToString(bi->ToString()) << endl; + } + + for (registered_channel_map::const_iterator cit = RegisteredChannelList.begin(), cit_end = RegisteredChannelList.end(); cit != cit_end; ++cit) + { + ChannelInfo *ci = cit->second; + + db_buffer << "CH " << ci->name << " " << ci->time_registered << " " << ci->last_used << endl; + db_buffer << "MD BANTYPE " << ci->bantype << endl; + db_buffer << "MD MEMOMAX " << ci->memos.memomax << endl; + if (ci->GetFounder()) + db_buffer << "MD FOUNDER " << ci->GetFounder()->display << endl; + if (ci->successor) + db_buffer << "MD SUCCESSOR " << ci->successor->display << endl; + if (!ci->desc.empty()) + db_buffer << "MD DESC :" << ci->desc << endl; + if (!ci->last_topic.empty()) + db_buffer << "MD TOPIC " << ci->last_topic_setter << " " << ci->last_topic_time << " :" << ci->last_topic << endl; + db_buffer << "MD LEVELS"; + for (int j = 0; ChannelLevels[j].Level != CA_SIZE; ++j) + db_buffer << " " << ChannelLevels[j].Name << " " << ci->levels[ChannelLevels[j].Level]; + db_buffer << endl; + if (ci->FlagCount()) + db_buffer << "MD FLAGS " << ToString(ci->ToString()) << endl; + if (ci->HasFlag(CI_SUSPENDED)) + { + Anope::string by, reason; + ci->GetExtRegular("suspend_by", by); + ci->GetExtRegular("suspend_reason", reason); + db_buffer << "MD SUSPEND " << by << " :" << reason << endl; + } + for (unsigned k = 0, end = ci->GetAccessCount(); k < end; ++k) + { + ChanAccess *access = ci->GetAccess(k); + db_buffer << "MD ACCESS " << access->provider->name << " " << access->mask << " " << access->Serialize() << " " << access->last_seen << " " << access->creator << " " << access->created << endl; + } + for (unsigned k = 0, end = ci->GetAkickCount(); k < end; ++k) + { + db_buffer << "MD AKICK 0 " << (ci->GetAkick(k)->HasFlag(AK_ISNICK) ? "NICK " : "MASK ") << + (ci->GetAkick(k)->HasFlag(AK_ISNICK) ? ci->GetAkick(k)->nc->display : ci->GetAkick(k)->mask) << " " << ci->GetAkick(k)->creator << " " << ci->GetAkick(k)->addtime << " " << ci->last_used << " :"; + if (!ci->GetAkick(k)->reason.empty()) + db_buffer << ci->GetAkick(k)->reason; + db_buffer << endl; + } + { + std::vector<Anope::string> mlocks; + if (ci->GetExtRegular("db_mlock", mlocks)) + for (unsigned i = 0; i < mlocks.size(); ++i) + db_buffer << mlocks[i] << endl; + else + { + for (std::multimap<ChannelModeName, ModeLock>::const_iterator it = ci->GetMLock().begin(), it_end = ci->GetMLock().end(); it != it_end; ++it) + { + const ModeLock &ml = it->second; + ChannelMode *cm = ModeManager::FindChannelModeByName(ml.name); + if (cm != NULL) + db_buffer << "MD MLOCK " << (ml.set ? 1 : 0) << " " << cm->NameAsString() << " " << ml.setter << " " << ml.created << " " << ml.param << endl; + } + } + } + MemoInfo *memos = &ci->memos; + for (unsigned k = 0, end = memos->memos.size(); k < end; ++k) + { + db_buffer << "MD MI " << memos->memos[k]->time << " " << memos->memos[k]->sender; + if (memos->memos[k]->HasFlag(MF_UNREAD)) + db_buffer << " UNREAD"; + if (memos->memos[k]->HasFlag(MF_RECEIPT)) + db_buffer << " RECEIPT"; + db_buffer << " :" << memos->memos[k]->text << endl; + } + for (unsigned k = 0, end = memos->ignores.size(); k < end; ++k) + db_buffer << "MD MIG " << Anope::string(memos->ignores[k]) << endl; + if (ci->bi) + db_buffer << "MD BI NAME " << ci->bi->nick << endl; + if (ci->botflags.FlagCount()) + db_buffer << "MD BI FLAGS " << ToString(ci->botflags.ToString()) << endl; + db_buffer << "MD BI TTB BOLDS " << ci->ttb[0] << " COLORS " << ci->ttb[1] << " REVERSES " << ci->ttb[2] << " UNDERLINES " << ci->ttb[3] << " BADWORDS " << ci->ttb[4] << " CAPS " << ci->ttb[5] << " FLOOD " << ci->ttb[6] << " REPEAT " << ci->ttb[7] << " ITALICS " << ci->ttb[8] << " AMSGS " << ci->ttb[9] << endl; + if (ci->capsmin) + db_buffer << "MD BI CAPSMIN " << ci->capsmin << endl; + if (ci->capspercent) + db_buffer << "MD BI CAPSPERCENT " << ci->capspercent << endl; + if (ci->floodlines) + db_buffer << "MD BI FLOODLINES " << ci->floodlines << endl; + if (ci->floodsecs) + db_buffer << "MD BI FLOODSECS " << ci->floodsecs << endl; + if (ci->repeattimes) + db_buffer << "MD BI REPEATTIMES " << ci->repeattimes << endl; + for (unsigned k = 0, end = ci->GetBadWordCount(); k < end; ++k) + db_buffer << "MD BI BADWORD " << (ci->GetBadWord(k)->type == BW_ANY ? "ANY " : "") << (ci->GetBadWord(k)->type == BW_SINGLE ? "SINGLE " : "") << (ci->GetBadWord(k)->type == BW_START ? "START " : "") << + (ci->GetBadWord(k)->type == BW_END ? "END " : "") << ":" << ci->GetBadWord(k)->word << endl; + + FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteMetadata, ci)); + } + + db_buffer << "OS STATS " << maxusercnt << " " << maxusertime << endl; + + for (std::list<XLineManager *>::iterator it = XLineManager::XLineManagers.begin(), it_end = XLineManager::XLineManagers.end(); it != it_end; ++it) + { + XLineManager *xl = *it; + for (unsigned i = 0, end = xl->GetCount(); i < end; ++i) + { + XLine *x = xl->GetEntry(i); + db_buffer << "OS SXLINE " << xl->Type() << " " << x->GetUser() << " " << x->GetHost() << " " << x->By << " " << x->Created << " " << x->Expires << " :" << x->Reason << endl; + } + } + + if (SessionInterface) + for (SessionService::ExceptionVector::iterator it = SessionInterface->GetExceptions().begin(); it != SessionInterface->GetExceptions().end(); ++it) + { + Exception *e = *it; + db_buffer << "OS EXCEPTION " << e->mask << " " << e->limit << " " << e->who << " " << e->time << " " << e->expires << " " << e->reason << endl; + } + + FOREACH_MOD(I_OnDatabaseWrite, OnDatabaseWrite(Write)); + + std::fstream db; + db.open(DatabaseFile.c_str(), std::ios_base::out | std::ios_base::trunc); + + if (!db.is_open()) + { + ircdproto->SendGlobops(NULL, "Unable to open %s for writing!", DatabaseFile.c_str()); + return EVENT_CONTINUE; + } + + db << db_buffer.str(); + db_buffer.str(""); + + db.close(); + + return EVENT_CONTINUE; + } + + void OnModuleLoad(User *u, Module *m) + { + if (!u) + return; + + Implementation events[] = {I_OnDatabaseRead, I_OnDatabaseReadMetadata}; + for (int i = 0; i < 2; ++i) + { + std::vector<Module *>::iterator it = std::find(ModuleManager::EventHandlers[events[i]].begin(), ModuleManager::EventHandlers[events[i]].end(), m); + /* This module wants to read from the database */ + if (it != ModuleManager::EventHandlers[events[i]].end()) + /* Loop over the database and call the events it would on a normal startup */ + ReadDatabase(m); + } + } +}; + +MODULE_INIT(DBPlain) |