summaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
authorAdam- <Adam-@5417fbe8-f217-4b02-8779-1006273d7864>2010-02-28 17:33:31 +0000
committerAdam- <Adam-@5417fbe8-f217-4b02-8779-1006273d7864>2010-02-28 17:33:31 +0000
commit3f80e1cad02735f692a5a300ee3b200a21f330aa (patch)
treed7bcd3c5d1cfd54c725f4c9a0eb7984632b6be67 /src/modules
parent393b5ab26e952f427f40a8ed6d10acfdf6ead96c (diff)
Added in support for live updating MySQL databases and the ability to execute commands to Anope through MySQL. Currently database support only applies to NickServ, ChanServ and BotServ but will be expanded soon.
git-svn-id: http://anope.svn.sourceforge.net/svnroot/anope/trunk@2798 5417fbe8-f217-4b02-8779-1006273d7864
Diffstat (limited to 'src/modules')
-rw-r--r--src/modules/CMakeLists.txt1
-rw-r--r--src/modules/Makefile8
-rw-r--r--src/modules/mysql/db_mysql.h238
-rw-r--r--src/modules/mysql/db_mysql_execute.cpp162
-rw-r--r--src/modules/mysql/db_mysql_read.cpp515
-rw-r--r--src/modules/mysql/db_mysql_write.cpp740
6 files changed, 1662 insertions, 2 deletions
diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt
index 79312cb8a..6745e8a67 100644
--- a/src/modules/CMakeLists.txt
+++ b/src/modules/CMakeLists.txt
@@ -58,6 +58,7 @@ endforeach(SRC)
file(GLOB MODULES_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*")
remove_item_from_list(MODULES_FILES ".svn")
remove_item_from_list(MODULES_FILES "CMakeFiles")
+remove_item_from_list(MODULES_FILES "mysql")
# Iterate through this directory searching for subdirectories, and creating modules for those subdirectories
foreach(FILE ${MODULES_FILES})
diff --git a/src/modules/Makefile b/src/modules/Makefile
index a65a497bd..b2686ae84 100644
--- a/src/modules/Makefile
+++ b/src/modules/Makefile
@@ -27,10 +27,14 @@ distclean: spotless
.SUFFIXES: .c .cpp .so
.c.so:
- $(MAKEBIN) $(CC) ${CFLAGS} ${CDEFS} ${MODULEFLAGS} -I../${INCLUDEDIR} -o $@ $<
+ $(MAKEBIN) $(CC) ${CFLAGS} ${CDEFS} ${MODULEFLAGS} \
+ $(if $(shell grep RequiredLibraries $< | grep mysqlpp),-lmysqlpp) \
+ -I../${INCLUDEDIR} -o $@ $<
.cpp.so:
- $(MAKEBIN) $(CC) ${CFLAGS} ${CDEFS} ${MODULEFLAGS} -I../${INCLUDEDIR} -o $@ $<
+ $(MAKEBIN) $(CC) ${CFLAGS} ${CDEFS} ${MODULEFLAGS} \
+ $(if $(shell grep RequiredLibraries $< | grep mysqlpp),-lmysqlpp) \
+ -I../${INCLUDEDIR} -o $@ $<
subs:
@for i in $(SUBS); do \
diff --git a/src/modules/mysql/db_mysql.h b/src/modules/mysql/db_mysql.h
new file mode 100644
index 000000000..b617b7dd5
--- /dev/null
+++ b/src/modules/mysql/db_mysql.h
@@ -0,0 +1,238 @@
+#include "module.h"
+
+struct NickAliasFlagInfo
+{
+ std::string Name;
+ NickNameFlag Flag;
+};
+
+NickAliasFlagInfo NickAliasFlags[] = {
+ {"FORBIDDEN", NS_FORBIDDEN},
+ {"NOEXPIRE", NS_NO_EXPIRE},
+ {"", static_cast<NickNameFlag>(-1)}
+};
+
+struct NickCoreFlagInfo
+{
+ std::string Name;
+ NickCoreFlag Flag;
+};
+
+NickCoreFlagInfo NickCoreFlags[] = {
+ {"KILLPROTECT", NI_KILLPROTECT},
+ {"SECURE", NI_SECURE},
+ {"MSG", NI_MSG},
+ {"MEMO_HARDMAX", NI_MEMO_HARDMAX},
+ {"MEMO_SIGNON", NI_MEMO_SIGNON},
+ {"MEMO_RECEIVE", NI_MEMO_RECEIVE},
+ {"PRIVATE", NI_PRIVATE},
+ {"HIDE_EMAIL", NI_HIDE_EMAIL},
+ {"HIDE_MASK", NI_HIDE_MASK},
+ {"HIDE_QUIT", NI_HIDE_QUIT},
+ {"KILL_QUICK", NI_KILL_QUICK},
+ {"KILL_IMMED", NI_KILL_IMMED},
+ {"MEMO_MAIL", NI_MEMO_MAIL},
+ {"HIDE_STATUS", NI_HIDE_STATUS},
+ {"SUSPENDED", NI_SUSPENDED},
+ {"AUTOOP", NI_AUTOOP},
+ {"FORBIDDEN", NI_FORBIDDEN},
+ {"", static_cast<NickCoreFlag>(-1)}
+};
+
+struct ChannelModeInfo
+{
+ std::string Name;
+ ChannelModeName Mode;
+};
+
+ChannelModeInfo ChannelModes[] = {
+ {"BLOCKCOLOR", CMODE_BLOCKCOLOR},
+ {"FLOOD", CMODE_FLOOD},
+ {"INVITE", CMODE_INVITE},
+ {"KEY", CMODE_KEY},
+ {"LIMIT", CMODE_LIMIT},
+ {"MODERATED", CMODE_MODERATED},
+ {"NOEXTERNAL", CMODE_NOEXTERNAL},
+ {"PRIVATE", CMODE_PRIVATE},
+ {"REGISTERED", CMODE_REGISTERED},
+ {"SECRET", CMODE_SECRET},
+ {"TOPIC", CMODE_TOPIC},
+ {"AUDITORIUM", CMODE_AUDITORIUM},
+ {"SSL", CMODE_SSL},
+ {"ADMINONLY", CMODE_ADMINONLY},
+ {"NOCTCP", CMODE_NOCTCP},
+ {"FILTER", CMODE_FILTER},
+ {"NOKNOCK", CMODE_NOKNOCK},
+ {"REDIRECT", CMODE_REDIRECT},
+ {"REGMODERATED", CMODE_REGMODERATED},
+ {"NONICK", CMODE_NONICK},
+ {"OPERONLY", CMODE_OPERONLY},
+ {"NONICK", CMODE_NONICK},
+ {"REGISTEREDONLY", CMODE_REGISTEREDONLY},
+ {"STRIPCOLOR", CMODE_STRIPCOLOR},
+ {"NONOTICE", CMODE_NONOTICE},
+ {"NOINVITE", CMODE_NOINVITE},
+ {"ALLINVITE", CMODE_ALLINVITE},
+ {"BLOCKCAPS", CMODE_BLOCKCAPS},
+ {"PERM", CMODE_PERM},
+ {"NICKFLOOD", CMODE_NICKFLOOD},
+ {"JOINFLOOD", CMODE_JOINFLOOD},
+ {"", static_cast<ChannelModeName>(-1)}
+};
+
+struct BotFlagInfo
+{
+ std::string Name;
+ BotServFlag Flag;
+};
+
+BotFlagInfo BotFlags[] = {
+ {"DONTKICKOPS", BS_DONTKICKOPS},
+ {"DONTKICKVOICES", BS_DONTKICKVOICES},
+ {"FANTASY", BS_FANTASY},
+ {"SYMBIOSIS", BS_SYMBIOSIS},
+ {"GREET", BS_GREET},
+ {"NOBOT", BS_NOBOT},
+ {"KICK_BOLDS", BS_KICK_BOLDS},
+ {"KICK_COLORS", BS_KICK_COLORS},
+ {"KICK_REVERSES", BS_KICK_REVERSES},
+ {"KICK_UNDERLINES", BS_KICK_UNDERLINES},
+ {"KICK_BADWORDS", BS_KICK_BADWORDS},
+ {"KICK_CAPS", BS_KICK_CAPS},
+ {"KICK_FLOOD", BS_KICK_FLOOD},
+ {"KICK_REPEAT", BS_KICK_REPEAT},
+ {"", static_cast<BotServFlag>(-1)}
+};
+
+struct ChannelFlagInfo
+{
+ std::string Name;
+ ChannelInfoFlag Flag;
+};
+
+ChannelFlagInfo ChannelFlags[] = {
+ {"KEEPTOPIC", CI_KEEPTOPIC},
+ {"SECUREOPS", CI_SECUREOPS},
+ {"PRIVATE", CI_PRIVATE},
+ {"TOPICLOCK", CI_TOPICLOCK},
+ {"RESTRICTED", CI_RESTRICTED},
+ {"PEACE", CI_PEACE},
+ {"SECURE", CI_SECURE},
+ {"FORBIDDEN", CI_FORBIDDEN},
+ {"NO_EXPIRE", CI_NO_EXPIRE},
+ {"MEMO_HARDMAX", CI_MEMO_HARDMAX},
+ {"OPNOTICE", CI_OPNOTICE},
+ {"SECUREFOUNDER", CI_SECUREFOUNDER},
+ {"SIGNKICK", CI_SIGNKICK},
+ {"SIGNKICK_LEVEL", CI_SIGNKICK_LEVEL},
+ {"XOP", CI_XOP},
+ {"SUSPENDED", CI_SUSPENDED},
+ {"PERSIST", CI_PERSIST},
+ {"", static_cast<ChannelInfoFlag>(-1)}
+};
+
+struct BotServFlagInfo
+{
+ std::string Name;
+ BotFlag Flag;
+};
+
+BotServFlagInfo BotServFlags[] = {
+ {"PRIVATE", BI_PRIVATE},
+ {"CHANSERV", BI_CHANSERV},
+ {"BOTSERV", BI_BOTSERV},
+ {"HOSTSERV", BI_HOSTSERV},
+ {"OPERSERV", BI_OPERSERV},
+ {"MEMOSERV", BI_MEMOSERV},
+ {"NICKSERV", BI_NICKSERV},
+ {"GLOBAL", BI_GLOBAL},
+ {"", static_cast<BotFlag>(-1)}
+};
+
+#define MYSQLPP_MYSQL_HEADERS_BURIED
+#include <mysql++/mysql++.h>
+
+inline std::string SQLAssign(const mysqlpp::String& s) { return s.c_str(); }
+
+class DBMySQL;
+static DBMySQL *Me;
+
+bool ExecuteQuery(mysqlpp::Query& query)
+{
+ Alog(LOG_DEBUG) << "MySQL: " << query.str();
+
+ if (!query.execute())
+ {
+ Alog(LOG_DEBUG) << "MySQL: error executing query: " << query.error();
+ return false;
+ }
+
+ return true;
+}
+
+mysqlpp::StoreQueryResult StoreQuery(mysqlpp::Query& query)
+{
+ Alog(LOG_DEBUG) << "MySQL: " << query.str();
+
+ mysqlpp::StoreQueryResult result = query.store();
+ if (!result)
+ {
+ Alog(LOG_DEBUG) << "MySQL: error executing query: " << query.error();
+ }
+ return result;
+}
+
+class DBMySQL : public Module
+{
+ private:
+ bool LoadConfig()
+ {
+ ConfigReader config;
+
+ Database = config.ReadValue("mysql", "database", "anope", 0);
+ Server = config.ReadValue("mysql", "server", "127.0.0.1", 0);
+ SQLUser = config.ReadValue("mysql", "username", "anope", 0);
+ Password = config.ReadValue("mysql", "password", "", 0);
+ Port = config.ReadInteger("mysql", "port", "3306", 0, true);
+ Delay = config.ReadInteger("mysql", "updatedelay", "60", 0, true);
+
+ return !Password.empty();
+ }
+
+ public:
+ mysqlpp::Connection *Con;
+
+ std::string Database;
+ std::string Server;
+ std::string SQLUser;
+ std::string Password;
+ unsigned int Port;
+ unsigned int Delay;
+
+ DBMySQL(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ Me = this;
+
+ this->SetAuthor("Anope");
+ this->SetVersion("$Id$");
+ this->SetType(SUPPORTED);
+
+ if (!LoadConfig())
+ throw ModuleException("Couldn't load config");
+
+ Con = new mysqlpp::Connection(false);
+ if (!Con->connect(Database.c_str(), Server.c_str(), SQLUser.c_str(), Password.c_str(), Port))
+ {
+ std::string Error = "MySQL: Error connecting to SQL server: ";
+ Error += Con->error();
+ delete Con;
+ throw ModuleException(Error.c_str());
+ }
+ }
+
+ virtual ~DBMySQL()
+ {
+ delete Con;
+ }
+};
+
diff --git a/src/modules/mysql/db_mysql_execute.cpp b/src/modules/mysql/db_mysql_execute.cpp
new file mode 100644
index 000000000..adeabd267
--- /dev/null
+++ b/src/modules/mysql/db_mysql_execute.cpp
@@ -0,0 +1,162 @@
+/* RequiredLibraries: mysqlpp */
+
+#include "db_mysql.h"
+#define HASH(nick) (((nick)[0]&31)<<5 | ((nick)[1]&31))
+
+class FakeNickCore : public NickCore
+{
+ public:
+ FakeNickCore() : NickCore("-SQLUser")
+ {
+ if (this->next)
+ this->next->prev = this->prev;
+ if (this->prev)
+ this->prev->next = this->next;
+ else
+ nclists[HASH(this->display)] = this->next;
+ }
+
+ ~FakeNickCore()
+ {
+ insert_core(this);
+ }
+
+ bool IsServicesOper() const { return true; }
+ bool HasCommand(const std::string &) const { return true; }
+ bool HasPriv(const std::string &) const { return true; }
+} SQLCore;
+
+class FakeUser : public User
+{
+ public:
+ FakeUser() : User("-SQLUser", "")
+ {
+ this->SetIdent("SQL");
+ this->host = sstrdup(Config.ServerName);
+ this->realname = sstrdup("Fake SQL User");
+ this->hostip = sstrdup("255.255.255.255");
+ this->vhost = NULL;
+
+ if (this->prev)
+ this->prev->next = this->next;
+ else
+ userlist[HASH(this->nick)] = this->next;
+ if (this->next)
+ this->next->prev = this->prev;
+ --usercnt;
+ }
+
+ ~FakeUser()
+ {
+ User **list = &userlist[HASH(this->nick)];
+ this->next = *list;
+ if (*list)
+ (*list)->prev = this;
+ *list = this;
+ ++usercnt;
+ }
+
+ void SetNewNick(const std::string &newnick) { this->nick = newnick; }
+
+ void SendMessage(const std::string &, const char *, ...) { }
+ void SendMessage(const std::string &, const std::string &) { }
+
+ NickCore *Account() const { return nc; }
+ const bool IsIdentified() const { return nc ? true : false; }
+} SQLUser;
+
+class SQLTimer : public Timer
+{
+ public:
+ SQLTimer() : Timer(Me->Delay, time(NULL), true) { }
+
+ void Tick(time_t)
+ {
+ mysqlpp::Query query(Me->Con);
+ mysqlpp::StoreQueryResult qres;
+
+ query << "SELECT * FROM `anope_commands`";
+ qres = StoreQuery(query);
+
+ if (qres && qres.num_rows())
+ {
+ for (size_t i = 0; i < qres.num_rows(); ++i)
+ {
+ User *u;
+ NickAlias *na = NULL;
+
+ /* If they want -SQLUser to execute the command, use it */
+ if (qres[i]["nick"] == "-SQLUser")
+ {
+ u = &SQLUser;
+ u->SetNewNick("-SQLUser");
+ SQLCore.Users.clear();
+ u->Login(&SQLCore);
+ }
+ else
+ {
+ /* See if the nick they want to execute the command is registered */
+ na = findnick(SQLAssign(qres[i]["nick"]));
+ if (na)
+ {
+ /* If it is and someone is online using that nick, use them */
+ if (!na->nc->Users.empty())
+ u = na->nc->Users.front();
+ /* Make a fake nick and use that logged in as the nick we want to use */
+ else
+ {
+ u = &SQLUser;
+ u->SetNewNick(SQLAssign(qres[i]["nick"]));
+ u->Login(na->nc);
+ }
+ }
+ else
+ {
+ /* Check if someone is online using the nick now */
+ u = finduser(SQLAssign(qres[i]["nick"]));
+ /* If they arent make a fake user, and use them */
+ if (!u)
+ {
+ u = &SQLUser;
+ u->SetNewNick(SQLAssign(qres[i]["nick"]));
+ u->Logout();
+ }
+ }
+ }
+
+ BotInfo *bi = findbot(SQLAssign(qres[i]["service"]));
+ if (!bi)
+ {
+ Alog() << "Warning: SQL command for unknown service " << qres[i]["service"];
+ continue;
+ }
+
+ // XXX this whole strtok thing needs to die
+ char *cmdbuf = sstrdup(qres[i]["command"].c_str());
+ char *cmd = strtok(cmdbuf, " ");
+ mod_run_cmd(bi->nick, u, bi->cmdTable, cmd);
+ delete [] cmdbuf;
+ }
+
+ query << "TRUNCATE TABLE `anope_commands`";
+ ExecuteQuery(query);
+ }
+ }
+};
+
+class DBMySQLExecute : public DBMySQL
+{
+ SQLTimer *_SQLTimer;
+ public:
+ DBMySQLExecute(const std::string &modname, const std::string &creator) : DBMySQL(modname, creator)
+ {
+ _SQLTimer = new SQLTimer();
+ }
+
+ ~DBMySQLExecute()
+ {
+ TimerManager::DelTimer(_SQLTimer);
+ }
+};
+
+MODULE_INIT(DBMySQLExecute)
diff --git a/src/modules/mysql/db_mysql_read.cpp b/src/modules/mysql/db_mysql_read.cpp
new file mode 100644
index 000000000..f9330270c
--- /dev/null
+++ b/src/modules/mysql/db_mysql_read.cpp
@@ -0,0 +1,515 @@
+/* RequiredLibraries: mysqlpp */
+
+#include "db_mysql.h"
+
+static std::vector<std::string> MakeVector(std::string buf)
+{
+ std::string s;
+ spacesepstream sep(buf);
+ std::vector<std::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 void LoadDatabase()
+{
+ mysqlpp::Query query(Me->Con);
+ mysqlpp::StoreQueryResult qres;
+
+ query << "SELECT * FROM `anope_ns_core`";
+ qres = StoreQuery(query);
+
+ if (qres)
+ {
+ for (size_t i = 0; i < qres.num_rows(); ++i)
+ {
+ NickCore *nc = new NickCore(SQLAssign(qres[i]["display"]));
+ nc->pass = SQLAssign(qres[i]["pass"]);
+ if (!qres[i]["email"].empty())
+ nc->email = sstrdup(qres[i]["email"].c_str());
+ if (!qres[i]["greet"].empty())
+ nc->greet = sstrdup(qres[i]["greet"].c_str());
+ if (!qres[i]["icq"].empty())
+ nc->icq = atol(qres[i]["icq"].c_str());
+ if (!qres[i]["url"].empty())
+ nc->url = sstrdup(qres[i]["url"].c_str());
+
+ spacesepstream sep(SQLAssign(qres[i]["flags"]));
+ std::string buf;
+ while (sep.GetToken(buf))
+ {
+ for (int j = 0; NickCoreFlags[j].Flag != -1; ++j)
+ {
+ if (NickCoreFlags[j].Name == buf)
+ {
+ nc->SetFlag(NickCoreFlags[j].Flag);
+ }
+ }
+ }
+
+ nc->language = atoi(qres[i]["language"].c_str());
+ nc->channelcount = atoi(qres[i]["channelcount"].c_str());
+ nc->memos.memomax = atoi(qres[i]["memomax"].c_str());
+ }
+ }
+
+ query << "SELECT * FROM `anope_ns_access`";
+ qres = StoreQuery(query);
+
+ if (qres)
+ {
+ for (size_t i = 0; i < qres.num_rows(); ++i)
+ {
+ NickCore *nc = findcore(qres[i]["display"].c_str());
+ if (!nc)
+ {
+ Alog() << "MySQL: Got NickCore access entry for nonexistant core " << qres[i]["display"];
+ continue;
+ }
+
+ nc->AddAccess(SQLAssign(qres[i]["access"]));
+ }
+ }
+
+ query << "SELECT * FROM `anope_ns_core_metadata`";
+ qres = StoreQuery(query);
+
+ if (qres)
+ {
+ for (size_t i = 0; i < qres.num_rows(); ++i)
+ {
+ NickCore *nc = findcore(qres[i]["display"].c_str());
+ if (!nc)
+ {
+ Alog() << "MySQL: Got NickCore access entry for nonexistant core " << qres[i]["display"];
+ continue;
+ }
+ try
+ {
+ EventReturn MOD_RESULT;
+ std::vector<std::string> Params = MakeVector(SQLAssign(qres[i]["value"]));
+ FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(nc, SQLAssign(qres[i]["name"]), Params));
+ }
+ catch (const char *err)
+ {
+ Alog() << "[db_mysql_read]: " << err;
+ }
+ }
+ }
+
+ query << "SELECT * FROM `anope_ns_alias`";
+ qres = StoreQuery(query);
+
+ if (qres)
+ {
+ for (size_t i = 0; i < qres.num_rows(); ++i)
+ {
+ NickCore *nc = findcore(qres[i]["display"].c_str());
+ if (!nc)
+ {
+ Alog() << "MySQL: Got NickAlias for nick " << qres[i]["nick"] << " with nonexistant core " << qres[i]["display"];
+ continue;
+ }
+
+ NickAlias *na = new NickAlias(SQLAssign(qres[i]["nick"]), nc);
+ na->last_quit = sstrdup(qres[i]["last_quit"].c_str());
+ na->last_realname = sstrdup(qres[i]["last_realname"].c_str());
+ na->last_usermask = sstrdup(qres[i]["last_usermask"].c_str());
+ na->time_registered = atol(qres[i]["time_registered"].c_str());
+ na->last_seen = atol(qres[i]["last_seen"].c_str());
+
+ spacesepstream sep(SQLAssign(qres[i]["flags"]));
+ std::string buf;
+ while (sep.GetToken(buf))
+ {
+ for (int j = 0; NickAliasFlags[j].Flag != -1; ++j)
+ {
+ if (NickAliasFlags[j].Name == buf)
+ {
+ na->SetFlag(NickAliasFlags[j].Flag);
+ }
+ }
+ }
+ }
+ }
+
+ query << "SELECT * FROM `anope_ns_alias_metadata`";
+ qres = StoreQuery(query);
+
+ if (qres)
+ {
+ for (size_t i = 0; i < qres.num_rows(); ++i)
+ {
+ NickAlias *na = findnick(SQLAssign(qres[i]["nick"]));
+ if (!na)
+ {
+ Alog() << "MySQL: Got metadata for nonexistant nick " << qres[i]["nick"];
+ continue;
+ }
+ try
+ {
+ EventReturn MOD_RESULT;
+ std::vector<std::string> Params = MakeVector(SQLAssign(qres[i]["value"]));
+ FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(na, SQLAssign(qres[i]["name"]), Params));
+ }
+ catch (const char *err)
+ {
+ Alog() << "[db_mysql_read]: " << err;
+ }
+ }
+ }
+
+ query << "SELECT * FROM `anope_bs_core`";
+ qres = StoreQuery(query);
+
+ if (qres)
+ {
+ for (size_t i = 0; i < qres.num_rows(); ++i)
+ {
+ BotInfo *bi = new BotInfo(SQLAssign(qres[i]["nick"]), SQLAssign(qres[i]["user"]), SQLAssign(qres[i]["host"]), SQLAssign(qres[i]["rname"]));
+ if (!qres[i]["flags"].empty())
+ {
+ spacesepstream sep(SQLAssign(qres[i]["flags"]));
+ std::string buf;
+ while (sep.GetToken(buf))
+ {
+ for (unsigned j = 0; BotServFlags[j].Flag != -1; ++j)
+ {
+ if (buf == BotServFlags[j].Name)
+ {
+ bi->SetFlag(BotServFlags[j].Flag);
+ break;
+ }
+ }
+ }
+ }
+ bi->created = atol(qres[i]["created"]);
+ bi->chancount = atol(qres[i]["chancount"]);
+ }
+ }
+
+ query << "SELECT * FROM `anope_cs_info`";
+ qres = StoreQuery(query);
+
+ if (qres)
+ {
+ for (size_t i = 0; i < qres.num_rows(); ++i)
+ {
+ NickCore *nc;
+ if (!qres[i]["founder"].empty())
+ {
+ nc = findcore(qres[i]["founder"].c_str());
+ if (!nc)
+ {
+ Alog() << "MySQL: Channel " << qres[i]["name"] << " with nonexistant founder " << qres[i]["founder"];
+ continue;
+ }
+ }
+
+ ChannelInfo *ci = new ChannelInfo(SQLAssign(qres[i]["name"]));
+ ci->founder = nc;
+ if (!qres[i]["successor"].empty())
+ ci->successor = findcore(qres[i]["successor"].c_str());
+ ci->desc = sstrdup(qres[i]["descr"].c_str());
+ if (!qres[i]["url"].empty())
+ ci->url = sstrdup(qres[i]["url"].c_str());
+ if (!qres[i]["email"].empty())
+ ci->email = sstrdup(qres[i]["email"].c_str());
+ ci->time_registered = atol(qres[i]["time_registered"]);
+ ci->last_used = atol(qres[i]["last_used"]);
+ if (!qres[i]["last_topic"].empty())
+ ci->last_topic = sstrdup(qres[i]["last_topic"].c_str());
+ if (!qres[i]["last_topic_setter"].empty())
+ ci->last_topic_setter = SQLAssign(qres[i]["last_topic_setter"]);
+ if (!qres[i]["last_topic_time"].empty())
+ ci->last_topic_time = atol(qres[i]["last_topic_time"].c_str());
+ if (!qres[i]["flags"].empty())
+ {
+ std::string buf;
+ spacesepstream sep(SQLAssign(qres[i]["flags"]));
+ while (sep.GetToken(buf))
+ {
+ for (int j = 0; ChannelFlags[j].Flag != -1; ++j)
+ {
+ if (buf == ChannelFlags[j].Name)
+ {
+ ci->SetFlag(ChannelFlags[j].Flag);
+ break;
+ }
+ }
+ }
+ }
+ if (!qres[i]["forbidby"].empty())
+ ci->forbidby = sstrdup(qres[i]["forbidby"].c_str());
+ if (!qres[i]["forbidreason"].empty())
+ ci->forbidreason = sstrdup(qres[i]["forbidreason"].c_str());
+ ci->bantype = atoi(qres[i]["bantype"].c_str());
+ if (!qres[i]["mlock_on"].empty())
+ {
+ std::string buf;
+ spacesepstream sep(SQLAssign(qres[i]["mlock_on"]));
+ while (sep.GetToken(buf))
+ {
+ for (int j = 0; ChannelModes[j].Mode != -1; ++j)
+ {
+ if (buf == ChannelModes[j].Name)
+ {
+ ci->SetMLock(ChannelModes[j].Mode, true);
+ break;
+ }
+ }
+ }
+ }
+ if (!qres[i]["mlock_off"].empty())
+ {
+ std::string buf;
+ spacesepstream sep(SQLAssign(qres[i]["mlock_off"]));
+ while (sep.GetToken(buf))
+ {
+ for (int j = 0; ChannelModes[j].Mode != -1; ++j)
+ {
+ if (buf == ChannelModes[j].Name)
+ {
+ ci->SetMLock(ChannelModes[j].Mode, false);
+ break;
+ }
+ }
+ }
+ }
+ if (!qres[i]["mlock_params"].empty())
+ {
+ std::string buf;
+ spacesepstream sep(SQLAssign(qres[i]["mlock_params"]));
+ while (sep.GetToken(buf))
+ {
+ for (int j = 0; ChannelModes[j].Mode != -1; ++j)
+ {
+ if (buf == ChannelModes[j].Name)
+ {
+ sep.GetToken(buf);
+ ci->SetMLock(ChannelModes[j].Mode, true, buf);
+ break;
+ }
+ }
+ }
+ }
+ if (!qres[i]["entry_message"].empty())
+ ci->entry_message = sstrdup(qres[i]["entry_message"].c_str());
+ ci->memos.memomax = atoi(qres[i]["memomax"].c_str());
+ if (!qres[i]["botnick"].empty())
+ ci->bi = findbot(SQLAssign(qres[i]["botnick"]));
+ if (ci->bi)
+ {
+ if (!qres[i]["botflags"].empty())
+ {
+ std::string buf;
+ spacesepstream sep(SQLAssign(qres[i]["botflags"]));
+ while (sep.GetToken(buf))
+ {
+ for (int j = 0; BotFlags[j].Flag != -1; ++j)
+ {
+ if (buf == BotFlags[j].Name)
+ {
+ ci->botflags.SetFlag(BotFlags[j].Flag);
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!qres[i]["capsmin"].empty())
+ ci->capsmin = atoi(qres[i]["capsmin"].c_str());
+ if (!qres[i]["capspercent"].empty())
+ ci->capspercent = atoi(qres[i]["capspercent"].c_str());
+ if (!qres[i]["floodlines"].empty())
+ ci->floodlines = atoi(qres[i]["capspercent"].c_str());
+ if (!qres[i]["floodsecs"].empty())
+ ci->floodsecs = atoi(qres[i]["floodsecs"].c_str());
+ if (!qres[i]["repeattimes"].empty())
+ ci->repeattimes = atoi(qres[i]["repeattimes"].c_str());
+ }
+ }
+
+ query << "SELECT * FROM `anope_cs_access`";
+ qres = StoreQuery(query);
+
+ if (qres)
+ {
+ for (size_t i = 0; i < qres.num_rows(); ++i)
+ {
+ ChannelInfo *ci = cs_findchan(SQLAssign(qres[i]["channel"]));
+ if (!ci)
+ {
+ Alog() << "MySQL: Channel access entry for nonexistant channel " << qres[i]["channel"];
+ continue;
+ }
+ NickCore *nc = findcore(qres[i]["display"]);
+ if (!nc)
+ {
+ Alog() << "MySQL: Channel access entry for " << ci->name << " with nonexistant nick " << qres[i]["display"];
+ continue;
+ }
+
+ ci->AddAccess(nc, atoi(qres[i]["level"]), SQLAssign(qres[i]["creator"]), atol(qres[i]["last_seen"]));
+ }
+ }
+
+ query << "SELECT * FROM `anope_cs_levels`";
+ qres = StoreQuery(query);
+
+ if (qres)
+ {
+ for (size_t i = 0; i < qres.num_rows(); ++i)
+ {
+ ChannelInfo *ci = cs_findchan(SQLAssign(qres[i]["channel"]));
+ if (!ci)
+ {
+ Alog() << "MySQL: Channel level entry for nonexistant channel " << qres[i]["channel"];
+ continue;
+ }
+ ci->levels[atoi(qres[i]["position"])] = atoi(qres[i]["level"]);
+ }
+ }
+
+ query << "SELECT * FROM `anope_cs_info_metadata`";
+ qres = StoreQuery(query);
+
+ if (qres)
+ {
+ for (size_t i = 0; i < qres.num_rows(); ++i)
+ {
+ ChannelInfo *ci = cs_findchan(SQLAssign(qres[i]["channel"]));
+ if (!ci)
+ {
+ Alog() << "MySQL: Channel level entry for nonexistant channel " << qres[i]["channel"];
+ continue;
+ }
+ try
+ {
+ EventReturn MOD_RESULT;
+ std::vector<std::string> Params = MakeVector(SQLAssign(qres[i]["value"]));
+ FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(ci, SQLAssign(qres[i]["name"]), Params));
+ }
+ catch (const char *err)
+ {
+ Alog() << "[db_mysql_read]: " << err;
+ }
+ }
+ }
+
+ query << "SELECT * FROM `anope_ns_request`";
+ qres = StoreQuery(query);
+
+ if (qres)
+ {
+ for (size_t i = 0; i < qres.num_rows(); ++i)
+ {
+ NickRequest *nr = new NickRequest(qres[i]["nick"].c_str());
+ nr->passcode = SQLAssign(qres[i]["passcode"]);
+ nr->password = SQLAssign(qres[i]["password"]);
+ nr->email = sstrdup(qres[i]["email"].c_str());
+ nr->requested = atol(qres[i]["requested"].c_str());
+ }
+ }
+
+ EventReturn MOD_RESULT;
+ query << "SELECT * FROM `anope_extra`";
+ qres = StoreQuery(query);
+
+ if (qres)
+ {
+ for (size_t i = 0; i < qres.num_rows(); ++i)
+ {
+ std::vector<std::string> params = MakeVector(SQLAssign(qres[i]["data"]));
+ FOREACH_RESULT(I_OnDatabaseRead, OnDatabaseRead(params));
+ }
+ }
+
+ query << "SELECT * FROM `anope_ns_core_metadata`";
+ qres = StoreQuery(query);
+
+ if (qres)
+ {
+ for (size_t i = 0; i < qres.num_rows(); ++i)
+ {
+ NickCore *nc = findcore(qres[i]["nick"].c_str());
+ if (nc)
+ {
+ std::vector<std::string> params = MakeVector(SQLAssign(qres[i]["value"]));
+ FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(nc, SQLAssign(qres[i]["name"]), params));
+ }
+ }
+ }
+
+ query << "SELECT * FROM `anope_ns_alias_metadata`";
+ qres = StoreQuery(query);
+
+ if (qres)
+ {
+ for (size_t i = 0; i < qres.num_rows(); ++i)
+ {
+ NickAlias *na = findnick(SQLAssign(qres[i]["nick"]));
+ if (na)
+ {
+ std::vector<std::string> params = MakeVector(SQLAssign(qres[i]["value"]));
+ FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(na, SQLAssign(qres[i]["name"]), params));
+ }
+ }
+ }
+
+ query << "SELECT * FROM `anope_cs_info_metadata`";
+ qres = StoreQuery(query);
+
+ if (qres)
+ {
+ for (size_t i = 0; i < qres.num_rows(); ++i)
+ {
+ ChannelInfo *ci = cs_findchan(SQLAssign(qres[i]["channel"]));
+ if (ci)
+ {
+ std::vector<std::string> params = MakeVector(SQLAssign(qres[i]["value"]));
+ FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(ci, SQLAssign(qres[i]["name"]), params));
+ }
+ }
+ }
+}
+
+class DBMySQLRead : public DBMySQL
+{
+ public:
+ DBMySQLRead(const std::string &modname, const std::string &creator) : DBMySQL(modname, creator)
+ {
+ Implementation i[] = { I_OnLoadDatabase };
+ ModuleManager::Attach(i, this, 1);
+ }
+
+ ~DBMySQLRead()
+ {
+ }
+
+ EventReturn OnLoadDatabase()
+ {
+ LoadDatabase();
+
+ /* No need to ever reload this again, although this should never be triggered again */
+ ModuleManager::Detach(I_OnLoadDatabase, this);
+
+ return EVENT_STOP;
+ }
+};
+
+MODULE_INIT(DBMySQLRead)
diff --git a/src/modules/mysql/db_mysql_write.cpp b/src/modules/mysql/db_mysql_write.cpp
new file mode 100644
index 000000000..21f2513d1
--- /dev/null
+++ b/src/modules/mysql/db_mysql_write.cpp
@@ -0,0 +1,740 @@
+/* RequiredLibraries: mysqlpp */
+
+#include "db_mysql.h"
+
+static std::string BuildFlagsList(ChannelInfo *ci)
+{
+ std::string ret;
+
+ for (int i = 0; ChannelFlags[i].Flag != -1; ++i)
+ {
+ if (ci->HasFlag(ChannelFlags[i].Flag))
+ {
+ ret += " " + ChannelFlags[i].Name;
+ }
+ }
+
+ if (!ret.empty())
+ ret.erase(ret.begin());
+
+ return ret;
+}
+
+static std::string BuildFlagsList(NickAlias *na)
+{
+ std::string ret;
+
+ for (int i = 0; NickAliasFlags[i].Flag != -1; ++i)
+ {
+ if (na->HasFlag(NickAliasFlags[i].Flag))
+ {
+ ret += " " + NickAliasFlags[i].Name;
+ }
+ }
+
+ if (!ret.empty())
+ ret.erase(ret.begin());
+
+ return ret;
+}
+
+static std::string BuildFlagsList(NickCore *nc)
+{
+ std::string ret;
+
+ for (int i = 0; NickCoreFlags[i].Flag != -1; ++i)
+ {
+ if (nc->HasFlag(NickCoreFlags[i].Flag))
+ {
+ ret += " " + NickCoreFlags[i].Name;
+ }
+ }
+
+ if (!ret.empty())
+ ret.erase(ret.begin());
+
+ return ret;
+}
+
+static std::string MakeMLock(ChannelInfo *ci, bool status)
+{
+ std::string ret;
+
+ for (int i = 0; ChannelModes[i].Mode != -1; ++i)
+ {
+ if (ci->HasMLock(ChannelModes[i].Mode, status))
+ {
+ ret += " " + ChannelModes[i].Name;
+ }
+ }
+
+ if (!ret.empty())
+ ret.erase(ret.begin());
+
+ return ret;
+}
+
+static inline std::string GetMLockOn(ChannelInfo *ci)
+{
+ return MakeMLock(ci, true);
+}
+
+static inline std::string GetMLockOff(ChannelInfo *ci)
+{
+ return MakeMLock(ci, false);
+}
+
+static std::string GetMLockParams(ChannelInfo *ci)
+{
+ std::string ret;
+
+ for (int i = 0; ChannelModes[i].Mode != -1; ++i)
+ {
+ std::string param;
+ if (ci->GetParam(ChannelModes[i].Mode, &param))
+ {
+ ret += " " + param;
+ }
+ }
+
+ if (!ret.empty())
+ ret.erase(ret.begin());
+
+ return ret;
+}
+
+static std::string GetBotFlags(Flags<BotServFlag>& Flags)
+{
+ std::string buf;
+
+ for (int i = 0; BotFlags[i].Flag != -1; ++i)
+ {
+ if (Flags.HasFlag(BotFlags[i].Flag))
+ {
+ buf += " " + BotFlags[i].Name;
+ }
+ }
+
+ if (!buf.empty())
+ buf.erase(buf.begin());
+
+ return buf;
+}
+
+static std::string GetBotServFlags(BotInfo *bi)
+{
+ std::string buf;
+
+ for (int i = 0; BotServFlags[i].Flag != -1; ++i)
+ {
+ if (bi->HasFlag(BotServFlags[i].Flag))
+ {
+ buf += " " + BotServFlags[i].Name;
+ }
+ }
+
+ if (!buf.empty())
+ buf.erase(buf.begin());;
+
+ return buf;
+}
+
+static NickAlias *CurNick = NULL;
+static NickCore *CurCore = NULL;
+static ChannelInfo *CurChannel = NULL;
+
+void Write(const std::string &data)
+{
+ mysqlpp::Query query(Me->Con);
+ query << "INSERT DELAYED INTO `anope_extra` (data) VALUES(" << mysqlpp::quote << data << ")";
+ ExecuteQuery(query);
+}
+
+void WriteMetadata(const std::string &key, const std::string &data)
+{
+ mysqlpp::Query query(Me->Con);
+ query << "INSERT DELAYED INTO `anope_metadata` (name, value) VALUES(" << mysqlpp::quote << key << ", " << mysqlpp::quote << data << ")";
+ ExecuteQuery(query);
+}
+
+void WriteNickMetadata(const std::string &key, const std::string &data)
+{
+ if (!CurNick)
+ throw CoreException("WriteNickMetadata without a nick to write");
+
+ mysqlpp::Query query(Me->Con);
+ query << "INSERT DELAYED INTO `anope_ns_alias_metadata` (nick, name, value) VALUES(" << mysqlpp::quote << CurNick->nick << ", " << mysqlpp::quote << key << ", " << mysqlpp::quote << data << ")";
+ ExecuteQuery(query);
+}
+
+void WriteCoreMetadata(const std::string &key, const std::string &data)
+{
+ if (!CurCore)
+ throw CoreException("WritCoreMetadata without a core to write");
+
+ mysqlpp::Query query(Me->Con);
+ query << "INSERT DELAYED INTO `anope_ns_core_metadata` (nick, name, value) VALUES(" << mysqlpp::quote << CurCore->display << ", " << mysqlpp::quote << key << ", " << mysqlpp::quote << data << ")";
+ ExecuteQuery(query);
+}
+
+void WriteChannelMetadata(const std::string &key, const std::string &data)
+{
+ if (!CurChannel)
+ throw CoreException("WriteChannelMetadata without a channel to write");
+
+ mysqlpp::Query query(Me->Con);
+ query << "INSERT DELAYED INTO `anope_cs_info_metadata` (channel, name, value) VALUES(" << mysqlpp::quote << CurChannel->name << ", " << mysqlpp::quote << key << ", " << mysqlpp::quote << data << ")";
+ ExecuteQuery(query);
+}
+
+static void SaveDatabases()
+{
+ int i;
+ mysqlpp::Query query(Me->Con);
+
+ query << "TRUNCATE TABLE `anope_ns_access`";
+ ExecuteQuery(query);
+ query << "TRUNCATE TABLE `anope_ns_core`";
+ ExecuteQuery(query);
+ for (i = 0; i < 1024; ++i)
+ {
+ for (NickCore *nc = nclists[i]; nc; nc = nc->next)
+ {
+ std::string flags = BuildFlagsList(nc);
+ query << "INSERT DELAYED INTO `anope_ns_core` (display, pass, email, greet, icq, url, flags, language, channelcount, memomax) VALUES(";
+ query << mysqlpp::quote << nc->display << ", " << mysqlpp::quote << nc->pass << ", ";
+ query << mysqlpp::quote << (nc->email ? nc->email : "") << ", " << mysqlpp::quote << (nc->greet ? nc->greet : "");
+ query << ", " << nc->icq << ", " << mysqlpp::quote << (nc->url ? nc->url : "");
+ query << ", '" << (!flags.empty() ? flags : "") << "', " << nc->language << ", " << nc->channelcount << ", ";
+ query << nc->memos.memomax << ") ";
+ query << "ON DUPLICATE KEY UPDATE pass=VALUES(pass), email=VALUES(email), greet=VALUES(greet), icq=VALUES(icq), flags=VALUES(flags), language=VALUES(language), ";
+ query << "channelcount=VALUES(channelcount), memomax=VALUES(memomax)";
+ ExecuteQuery(query);
+
+ for (std::vector<std::string>::iterator it = nc->access.begin(); it != nc->access.end(); ++it)
+ {
+ query << "INSERT DELAYED INTO `anope_ns_access` (display, access) VALUES(" << mysqlpp::quote << nc->display << ", " << mysqlpp::quote << *it << ")";
+ ExecuteQuery(query);
+ }
+ }
+ }
+
+ query << "TRUNCATE TABLE `anope_ns_alias`";
+ ExecuteQuery(query);
+ for (i = 0; i < 1024; ++i)
+ {
+ for (NickAlias *na = nalists[i]; na; na = na->next)
+ {
+ std::string flags = BuildFlagsList(na);
+ query << "INSERT DELAYED INTO `anope_ns_alias` (nick, last_quit, last_realname, last_usermask, time_registered, last_seen, flags, display) VALUES(";
+ query << mysqlpp::quote << na->nick << ", " << mysqlpp::quote << (na->last_quit ? na->last_quit : "") << ", ";
+ query << mysqlpp::quote << (na->last_realname ? na->last_realname : "") << ", ";
+ query << mysqlpp::quote << (na->last_usermask ? na->last_usermask : "") << ", " << na->time_registered << ", " << na->last_seen;
+ query << ", '" << (!flags.empty() ? flags : "") << "', " << mysqlpp::quote << na->nc->display << ") ";
+ query << "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)";
+ ExecuteQuery(query);
+ }
+ }
+
+ query << "TRUNCATE TABLE `anope_bs_core`";
+ ExecuteQuery(query);
+ for (i = 0; i < 256; ++i)
+ {
+ for (BotInfo *bi = botlists[i]; bi; bi = bi->next)
+ {
+ query << "INSERT DELAYED INTO `anope_bs_core` (nick, user, host, rname, flags, created, chancount) VALUES(";
+ query << mysqlpp::quote << bi->nick << ", " << mysqlpp::quote << bi->user << ", " << mysqlpp::quote << bi->host << ", ";
+ query << mysqlpp::quote << bi->real << ", '" << GetBotServFlags(bi) << "', " << bi->created << ", " << bi->chancount << ") ";
+ query << "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)";
+ ExecuteQuery(query);
+ }
+ }
+
+ query << "TRUNCATE TABLE `anope_cs_info`";
+ ExecuteQuery(query);
+ query << "TRUNCATE TABLE `anope_cs_access`";
+ ExecuteQuery(query);
+
+ for (i = 0; i < 256; ++i)
+ {
+ for (ChannelInfo *ci = chanlists[i]; ci; ci = ci->next)
+ {
+ std::string flags = BuildFlagsList(ci), mlockon = GetMLockOn(ci), mlockoff = GetMLockOff(ci), mlockparams = GetMLockParams(ci);
+ query << "INSERT DELAYED INTO `anope_cs_info` (name, founder, successor, descr, url, email, time_registered, last_used, last_topic, last_topic_setter, last_topic_time, flags, forbidby, forbidreason, bantype, mlock_on, mlock_off, mlock_params, entry_message, memomax, botnick, botflags, capsmin, capspercent, floodlines, floodsecs, repeattimes) VALUES(";
+ query << mysqlpp::quote << ci->name << ", " << mysqlpp::quote << (ci->founder ? ci->founder->display : "") << ", ";
+ query << mysqlpp::quote << (ci->successor ? ci->successor->display : "") << ", " << mysqlpp::quote << ci->desc << ", ";
+ query << mysqlpp::quote << (ci->url ? ci->url : "") << ", " << mysqlpp::quote << (ci->email ? ci->email : "") << ", ";
+ query << ci->time_registered << ", " << ci->last_used << ", " << mysqlpp::quote << (ci->last_topic ? ci->last_topic : "");
+ query << ", " << mysqlpp::quote << (!ci->last_topic_setter.empty() ? ci->last_topic_setter : "");
+ query << ", " << ci->last_topic_time << ", '" << (!flags.empty() ? flags : "") << "', ";
+ query << mysqlpp::quote << (ci->forbidby ? ci->forbidby : "") << ", " << mysqlpp::quote << (ci->forbidreason ? ci->forbidreason : "") << ", " << ci->bantype << ", '";
+ query << mlockon << "', '" << mlockoff << "', '";
+ query << mlockparams << "', " << mysqlpp::quote << (ci->entry_message ? ci->entry_message : "") << ", ";
+ query << ci->memos.memomax << ", " << mysqlpp::quote << (ci->bi ? ci->bi->nick : "") << ", '" << GetBotFlags(ci->botflags);
+ query << "', " << ci->capsmin << ", " << ci->capspercent << ", " << ci->floodlines;
+ query << ", " << ci->floodsecs << ", " << ci->repeattimes << ") ";
+ query << "ON DUPLICATE KEY UPDATE founder=VALUES(founder), successor=VALUES(successor), descr=VALUES(descr), url=VALUES(url), email=VALUES(email), 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), forbidby=VALUES(forbidby), forbidreason=VALUES(forbidreason), bantype=VALUES(bantype), mlock_on=VALUES(mlock_on), mlock_off=VALUES(mlock_off), mlock_params=VALUES(mlock_params), entry_message=VALUES(entry_message), memomax=VALUES(memomax), botnick=VALUES(botnick), botflags=VALUES(botflags), capsmin=VALUES(capsmin), capspercent=VALUES(capspercent), floodlines=VALUES(floodlines), floodsecs=VALUES(floodsecs), repeattimes=VALUES(repeattimes)";
+ ExecuteQuery(query);
+
+ for (unsigned j = 0; j < ci->GetAccessCount(); ++j)
+ {
+ ChanAccess *access = ci->GetAccess(j);
+
+ if (access->in_use)
+ {
+ query << "INSERT DELAYED INTO `anope_cs_access` (level, display, channel, last_seen, creator) VALUES('" << access->level << "', " << mysqlpp::quote << access->nc->display << ", " << mysqlpp::quote << ci->name << ", " << access->last_seen << ", " << mysqlpp::quote << access->creator << ") ON DUPLICATE KEY UPDATE level=VALUES(level), last_seen=VALUES(last_seen), creator=VALUES(creator)";
+ ExecuteQuery(query);
+ }
+ }
+
+ query << "TRUNCATE TABLE `anope_cs_levels`";
+ ExecuteQuery(query);
+ for (int k = 0; k < CA_SIZE; ++k)
+ {
+ query << "INSERT DELAYED INTO `anope_cs_levels` (channel, position, level) VALUES(" << mysqlpp::quote << ci->name << ", '" << k << "', '" << ci->levels[k] << "') ON DUPLICATE KEY UPDATE position=VALUES(position), level=VALUES(level)";
+ ExecuteQuery(query);
+ }
+ }
+ }
+
+ query << "TRUNCATE TABLE `anope_ns_request`";
+ ExecuteQuery(query);
+
+ for (i = 0; i < 1024; i++)
+ {
+ for (NickRequest *nr = nrlists[i]; nr; nr = nr->next)
+ {
+ query << "INSERT DELAYED INTO `anope_ns_request` (nick, passcode, password, email, requested) VALUES(" << mysqlpp::quote << nr->nick << ", '" << nr->passcode << "', '" << nr->password << "', " << mysqlpp::quote << nr->email << ", '" << nr->requested << "') ON DUPLICATE KEY UPDATE passcode=VALUES(passcode), pasword=VALUES(password), email=VALUES(email), requests=VALUES(requested)";
+ ExecuteQuery(query);
+ }
+ }
+}
+
+class CommandSyncSQL : public Command
+{
+ public:
+ CommandSyncSQL(const std::string &cname) : Command(cname, 0, 0)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ notice_lang(Config.s_OperServ, u, MYSQL_SYNC_UPDATING);
+ SaveDatabases();
+ notice_lang(Config.s_OperServ, u, MYSQL_SYNC_UPDATED);
+ return MOD_CONT;
+ }
+};
+
+class DBMySQLWrite : public DBMySQL
+{
+ public:
+ DBMySQLWrite(const std::string &modname, const std::string &creator) : DBMySQL(modname, creator)
+ {
+ ModuleManager::Attach(I_OnServerConnect, this);
+
+ this->AddCommand(OPERSERV, new CommandSyncSQL("SQLSYNC"));
+ }
+
+ ~DBMySQLWrite()
+ {
+ }
+
+ void OnServerConnect()
+ {
+ Implementation i[] = {
+ /* Misc */
+ I_OnSaveDatabase, I_OnPostCommand,
+ /* NickServ */
+ I_OnNickAddAccess, I_OnNickEraseAccess, I_OnNickClearAccess,
+ I_OnDelCore, I_OnNickForbidden, I_OnNickGroup, I_OnMakeNickRequest,
+ I_OnDelNickRequest, I_OnNickRegister, I_OnChangeCoreDisplay,
+ I_OnNickSuspended,
+ /* Chanserv */
+ I_OnAccessAdd, I_OnAccessDel, I_OnAccessChange, I_OnAccessClear,
+ I_OnChanForbidden, I_OnDelChan, I_OnChanRegistered, I_OnChanSuspend,
+ /* BotServ */
+ I_OnBotCreate, I_OnBotChange, I_OnBotDelete,
+ I_OnBotAssign, I_OnBotUnAssign
+ };
+ ModuleManager::Attach(i, this, 26);
+ }
+
+ EventReturn OnSaveDatabase()
+ {
+ for (int i = 0; i < 1024; ++i)
+ {
+ for (NickCore *nc = nclists[i]; nc; nc = nc->next)
+ {
+ CurCore = nc;
+ FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteCoreMetadata, nc));
+ }
+ }
+
+ for (int i = 0; i < 1024; ++i)
+ {
+ for (NickAlias *na = nalists[i]; na; na = na->next)
+ {
+ CurNick = na;
+ FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteNickMetadata, na));
+ }
+ }
+
+ for (int i = 0; i < 256; ++i)
+ {
+ for (ChannelInfo *ci = chanlists[i]; ci; ci = ci->next)
+ {
+ CurChannel = ci;
+ FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteChannelMetadata, ci));
+ }
+ }
+
+ FOREACH_MOD(I_OnDatabaseWrite, OnDatabaseWrite(Write));
+
+ return EVENT_CONTINUE;
+ }
+
+ void OnPostCommand(User *u, const std::string &service, const ci::string &command, const std::vector<ci::string> &params)
+ {
+ mysqlpp::Query query(Me->Con);
+
+ if (service == Config.s_NickServ)
+ {
+ if (u->Account() && ((command == "SET" && !params.empty()) || (command == "SASET" && u->Account()->HasCommand("nickserv/saset") && params.size() > 1)))
+ {
+ ci::string cmd = (command == "SET" ? params[0] : params[1]);
+ NickCore *nc = (command == "SET" ? u->Account() : findcore(params[0].c_str()));
+ if (!nc)
+ return;
+ if (cmd == "PASSWORD" && params.size() > 1)
+ {
+ query << "UPDATE `anope_ns_core` SET `password` = " << mysqlpp::quote << nc->pass << " WHERE `display` = " << mysqlpp::quote << nc->display;
+ ExecuteQuery(query);
+ }
+ else if (cmd == "LANGUAGE" && params.size() > 1)
+ {
+ query << "UPDATE `anope_ns_core` SET `language` = " << nc->language << " WHERE `display` = " << mysqlpp::quote << nc->display;
+ ExecuteQuery(query);
+ }
+ else if (cmd == "URL")
+ {
+ query << "UPDATE `anope_ns_core` SET `url` = " << mysqlpp::quote << nc->url << " WHERE `display` = " << mysqlpp::quote << nc->display;
+ ExecuteQuery(query);
+ }
+ else if (cmd == "EMAIL")
+ {
+ query << "UPDATE `anope_ns_core` SET `email` = " << mysqlpp::quote << nc->email << " WHERE `display` = " << mysqlpp::quote << nc->display;
+ ExecuteQuery(query);
+ }
+ else if (cmd == "ICQ")
+ {
+ query << "UPDATE `anope_ns_core` SET `icq` = " << nc->icq << " WHERE `display` = " << mysqlpp::quote << nc->display;
+ ExecuteQuery(query);
+ }
+ else if (cmd == "GREET")
+ {
+ query << "UPDATE `anope_ns_core` SET `greet` = " << mysqlpp::quote << nc->greet << " WHERE `display` = " << mysqlpp::quote << nc->display;
+ ExecuteQuery(query);
+ }
+ else if (cmd == "KILL" || cmd == "SECURE" || cmd == "PRIVATE" || cmd == "MSG" || cmd == "HIDE" || cmd == "AUTOOP")
+ {
+ query << "UPDATE `anope_ns_core` SET `flags` = " << BuildFlagsList(nc) << " WHERE `display` = " << mysqlpp::quote << nc->display;
+ ExecuteQuery(query);
+ }
+ }
+ }
+ else if (service == Config.s_ChanServ)
+ {
+ if (command == "SET" && u->Account() && params.size() > 1)
+ {
+ ChannelInfo *ci = cs_findchan(params[0].c_str());
+ if (!ci)
+ return;
+ if (!u->Account()->HasPriv("chanserv/set") && check_access(u, ci, CA_SET))
+ return;
+ if (params[1] == "FOUNDER" && ci->founder)
+ {
+ query << "UPDATE `anope_cs_info` SET `founder` = " << mysqlpp::quote << ci->founder->display << " WHERE `name` = " << mysqlpp::quote << ci->name;
+ ExecuteQuery(query);
+ }
+ else if (params[1] == "SUCCESSOR")
+ {
+ query << "UPDATE `anope_cs_info` SET `successor` = " << mysqlpp::quote << (ci->successor ? ci->successor->display : "") << " WHERE `name` = " << mysqlpp::quote << ci->name;
+ ExecuteQuery(query);
+ }
+ else if (params[1] == "DESC")
+ {
+ query << "UPDATE `anope_cs_info` SET `descr` = " << mysqlpp::quote << ci->desc << " WHERE `name` = " << mysqlpp::quote << ci->name;
+ ExecuteQuery(query);
+ }
+ else if (params[1] == "URL")
+ {
+ query << "UPDATE `anope_cs_info` SET `url` = " << mysqlpp::quote << ci->url << " WHERE `name` = " << mysqlpp::quote << ci->name;
+ ExecuteQuery(query);
+ }
+ else if (params[1] == "EMAIL")
+ {
+ query << "UPDATE `anope_cs_info` SET `email` = " << mysqlpp::quote << ci->email << " WHERE `name` = " << mysqlpp::quote << ci->name;
+ ExecuteQuery(query);
+ }
+ else if (params[1] == "ENTRYMSG")
+ {
+ query << "UPDATE `anope_cs_info` SET `entry_message` = " << mysqlpp::quote << ci->entry_message << " WHERE `name` = " << mysqlpp::quote << ci->name;
+ ExecuteQuery(query);
+ }
+ else if (params[1] == "MLOCK")
+ {
+ query << "UPDATE `anope_cs_info` SET `mlock_on` = '" << GetMLockOn(ci) << "' WHERE `name` = " << mysqlpp::quote << ci->name;
+ ExecuteQuery(query);
+ query << "UPDATE `anope_cs_info` SET `mlock_off` = '" << GetMLockOff(ci) << "' WHERE `name` = " << mysqlpp::quote << ci->name;
+ ExecuteQuery(query);
+ query << "UPDATE `anope_cs_info` SET `mlock_params` = '" << GetMLockParams(ci) << "' WHERE `name` = " << mysqlpp::quote << ci->name;
+ ExecuteQuery(query);
+ }
+ else if (params[1] == "BANTYPE")
+ {
+ query << "UPDATE `anope_cs_info` SET `bantype` = " << ci->bantype << " WHERE `name` = " << mysqlpp::quote << ci->name;
+ ExecuteQuery(query);
+ }
+ else if (params[1] == "KEEPTOPIC" || params[1] == "TOPICLOCK" || params[1] == "PRIVATE" || params[1] == "SECUREOPS" || params[1] == "SECUREFOUNDER" || params[1] == "RESTRICTED" || params[1] == "SECURE" || params[1] == "SIGNKICK" || params[1] == "OPNOTICE" || params[1] == "XOP" || params[1] == "PEACE" || params[1] == "PERSIST" || params[1] == "NOEXPIRE")
+ {
+ query << "UPDATE `anope_cs_info` SET `flags` = '" << BuildFlagsList(ci) << "' WHERE `name` = " << mysqlpp::quote << ci->name;
+ ExecuteQuery(query);
+ }
+ }
+ }
+ }
+
+ void OnNickAddAccess(NickCore *nc, const std::string &entry)
+ {
+ mysqlpp::Query query(Me->Con);
+ query << "INSERT DELAYED INTO `anope_ns_access` (display, access) VALUES(" << mysqlpp::quote << nc->display << ", " << mysqlpp::quote << entry << ")";
+ ExecuteQuery(query);
+ }
+
+ void OnNickEraseAccess(NickCore *nc, const std::string &entry)
+ {
+ mysqlpp::Query query(Me->Con);
+ query << "DELETE FROM `anope_ns_access` WHERE `display` = " << mysqlpp::quote << nc->display << " AND `access` = " << mysqlpp::quote << entry;
+ ExecuteQuery(query);
+ }
+
+ void OnNickClearAccess(NickCore *nc)
+ {
+ mysqlpp::Query query(Me->Con);
+ query << "DELETE FROM `anope_ns_access` WHERE `display` = " << mysqlpp::quote << nc->display;
+ ExecuteQuery(query);
+ }
+
+ void OnDelCore(NickCore *nc)
+ {
+ mysqlpp::Query query(Me->Con);
+ query << "DELETE FROM `anope_cs_access` WHERE `display` = " << mysqlpp::quote << nc->display;
+ ExecuteQuery(query);
+ query << "DELETE FROM `anope_ns_access` WHERE `display` = " << mysqlpp::quote << nc->display;
+ ExecuteQuery(query);
+ query << "DELETE FROM `anope_ns_alias` WHERE `display` = " << mysqlpp::quote << nc->display;
+ ExecuteQuery(query);
+ query << "DELETE FROM `anope_ns_core` WHERE `display` = " << mysqlpp::quote << nc->display;
+ ExecuteQuery(query);
+ }
+
+ void OnNickForbidden(NickAlias *na)
+ {
+ std::string flags = BuildFlagsList(na);
+ mysqlpp::Query query(Me->Con);
+ query << "UPDATE `anope_ns_alias` SET `flags` = '" << (!flags.empty() ? flags : "") << "' WHERE `nick` = " << mysqlpp::quote << na->nick;
+ ExecuteQuery(query);
+ }
+
+ void OnNickGroup(User *u, NickAlias *)
+ {
+ OnNickRegister(findnick(u->nick));
+ }
+
+ void OnMakeNickRequest(NickRequest *nr)
+ {
+ mysqlpp::Query query(Me->Con);
+ query << "INSERT DELAYED INTO `anope_ns_request` (nick, passcode, password, email, requested) VALUES(" << mysqlpp::quote << nr->nick << ", ";
+ query << mysqlpp::quote << nr->passcode << ", " << mysqlpp::quote << nr->password << ", " << mysqlpp::quote << nr->email << ", '";
+ query << nr->requested << "')";
+ ExecuteQuery(query);
+ }
+
+ void OnDelNickRequest(NickRequest *nr)
+ {
+ mysqlpp::Query query(Me->Con);
+ query << "DELETE FROM `anope_ns_request` WHERE `nick` = " << mysqlpp::quote << nr->nick;
+ ExecuteQuery(query);
+ }
+
+ void OnNickRegister(NickAlias *na)
+ {
+ std::string flags = BuildFlagsList(na);
+ mysqlpp::Query query(Me->Con);
+ query << "INSERT DELAYED INTO `anope_ns_alias` (nick, last_quit, last_realname, last_usermask, time_registered, last_seen, flags, display) VALUES(";
+ query << mysqlpp::quote << na->nick << ", " << mysqlpp::quote << (na->last_quit ? na->last_quit : "") << ", ";
+ query << mysqlpp::quote << (na->last_realname ? na->last_realname : "") << ", ";
+ query << mysqlpp::quote << (na->last_usermask ? na->last_usermask : "") << ", " << na->time_registered << ", " << na->last_seen;
+ query << ", '" << (!flags.empty() ? flags : "") << "', " << mysqlpp::quote << na->nc->display << ") ";
+ query << "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)";
+ ExecuteQuery(query);
+
+ flags = BuildFlagsList(na->nc);
+ query << "INSERT DELAYED INTO `anope_ns_core` (display, pass, email, greet, icq, url, flags, language, channelcount, memomax) VALUES(";
+ query << mysqlpp::quote << na->nc->display << ", " << mysqlpp::quote << na->nc->pass << ", ";
+ query << mysqlpp::quote << (na->nc->email ? na->nc->email : "") << ", " << mysqlpp::quote << (na->nc->greet ? na->nc->greet : "");
+ query << ", " << na->nc->icq << ", " << mysqlpp::quote << (na->nc->url ? na->nc->url : "");
+ query << ", '" << (!flags.empty() ? flags : "") << "', " << na->nc->language << ", " << na->nc->channelcount << ", ";
+ query << na->nc->memos.memomax << ") ";
+ query << "ON DUPLICATE KEY UPDATE pass=VALUES(pass), email=VALUES(email), greet=VALUES(greet), icq=VALUES(icq), flags=VALUES(flags), language=VALUES(language), ";
+ query << "channelcount=VALUES(channelcount), memomax=VALUES(memomax)";
+ ExecuteQuery(query);
+ }
+
+ void OnChangeCoreDisplay(NickCore *nc, const std::string &newdisplay)
+ {
+ mysqlpp::Query query(Me->Con);
+ query << "UPDATE `anope_ns_core` SET `display` = " << mysqlpp::quote << newdisplay << " WHERE `display` = " << mysqlpp::quote << nc->display;
+ ExecuteQuery(query);
+ query << "UPDATE `anope_ns_alias` SET `display` = " << mysqlpp::quote << newdisplay << " WHERE `display` = " << mysqlpp::quote << nc->display;
+ ExecuteQuery(query);
+ query << "UPDATE `anope_ns_access` SET `display` = " << mysqlpp::quote << newdisplay << " WHERE `display` = " << mysqlpp::quote << nc->display;
+ ExecuteQuery(query);
+ query << "UPDATE `anope_cs_access` SET `display` = " << mysqlpp::quote << newdisplay << " WHERE `display` = " << mysqlpp::quote << nc->display;
+ ExecuteQuery(query);
+ query << "UPDATE `anope_cs_info` SET `founder` = " << mysqlpp::quote << newdisplay << " WHERE `founder` = " << mysqlpp::quote << nc->display;
+ ExecuteQuery(query);
+ query << "UPDATE `anope_cs_info` SET `successor` = " << mysqlpp::quote << newdisplay << " WHERE `successor` = " << mysqlpp::quote << nc->display;
+ ExecuteQuery(query);
+ }
+
+ void OnNickSuspend(NickAlias *na)
+ {
+ mysqlpp::Query query(Me->Con);
+ query << "UPDATE `anope_ns_core` SET `flags` = '" << BuildFlagsList(na->nc) << "' WHERE `display` = " << mysqlpp::quote << na->nc->display;
+ ExecuteQuery(query);
+ }
+
+ void OnAccessAdd(ChannelInfo *ci, User *u, NickAlias *na, int level)
+ {
+ mysqlpp::Query query(Me->Con);
+ query << "INSERT DELAYED INTO `anope_cs_access` (level, display, channel, last_seen, creator) VALUES (" << level << ", " << mysqlpp::quote << na->nc->display << ", " << mysqlpp::quote << ci->name << ", " << time(NULL) << ", " << mysqlpp::quote << u->nick << ")";
+ ExecuteQuery(query);
+ }
+
+ void OnAccessDel(ChannelInfo *ci, User *u, NickCore *nc)
+ {
+ mysqlpp::Query query(Me->Con);
+ query << "DELETE FROM `anope_cs_access` WHERE `display` = " << mysqlpp::quote << nc->display << " AND `channel` = " << mysqlpp::quote << ci->name;
+ ExecuteQuery(query);
+ }
+
+ void OnAccessChange(ChannelInfo *ci, User *u, NickAlias *na, int level)
+ {
+ mysqlpp::Query query(Me->Con);
+ query << "INSERT DELAYED INTO `anope_cs_access` (level, display, channel, last_seen, creator) VALUES (" << level << ", " << mysqlpp::quote << na->nc->display << ", " << mysqlpp::quote << ci->name << ", " << time(NULL) << ", " << mysqlpp::quote << u->nick << ") ON DUPLICATE KEY UPDATE level=VALUES(level), display=VALUES(display), channel=VALUES(channel), last_seen=VALUES(last_seen), creator=VALUES(creator)";
+ ExecuteQuery(query);
+ }
+
+ void OnAccessClear(ChannelInfo *ci, User *u)
+ {
+ mysqlpp::Query query(Me->Con);
+ query << "DELETE FROM `anope_cs_access` WHERE `channel` = " << mysqlpp::quote << ci->name;
+ ExecuteQuery(query);
+ }
+
+ void OnChanForbidden(ChannelInfo *ci)
+ {
+ mysqlpp::Query query(Me->Con);
+ query << "INSERT DELAYED INTO `anope_cs_info` (name, time_registered, last_used, flags, forbidby, forbidreason) VALUES (";
+ query << mysqlpp::quote << ci->name << ", " << ci->time_registered << ", " << ci->last_used << ", '" << BuildFlagsList(ci) << "', " << mysqlpp::quote << ci->forbidby << ", " << mysqlpp::quote << ci->forbidreason << ")";
+ ExecuteQuery(query);
+ }
+
+ void OnDelChan(ChannelInfo *ci)
+ {
+ mysqlpp::Query query(Me->Con);
+ query << "DELETE FROM `anope_cs_access` WHERE `channel` = " << mysqlpp::quote << ci->name;
+ ExecuteQuery(query);
+ query << "DELETE FROM `anope_cs_info` WHERE `name` = " << mysqlpp::quote << ci->name;
+ ExecuteQuery(query);
+ query << "DELETE FROM `anope_cs_levels` WHERE `channel` = " << mysqlpp::quote << ci->name;
+ ExecuteQuery(query);
+ }
+
+ void OnChanRegistered(ChannelInfo *ci)
+ {
+ mysqlpp::Query query(Me->Con);
+ std::string flags = BuildFlagsList(ci), mlockon = GetMLockOn(ci), mlockoff = GetMLockOff(ci), mlockparams = GetMLockParams(ci);
+ query << "INSERT DELAYED INTO `anope_cs_info` (name, founder, successor, descr, url, email, time_registered, last_used, last_topic, last_topic_setter, last_topic_time, flags, forbidby, forbidreason, bantype, mlock_on, mlock_off, mlock_params, entry_message, memomax, botnick, botflags, capsmin, capspercent, floodlines, floodsecs, repeattimes) VALUES(";
+ query << mysqlpp::quote << ci->name << ", " << mysqlpp::quote << (ci->founder ? ci->founder->display : "") << ", ";
+ query << mysqlpp::quote << (ci->successor ? ci->successor->display : "") << ", " << mysqlpp::quote << ci->desc << ", ";
+ query << mysqlpp::quote << (ci->url ? ci->url : "") << ", " << mysqlpp::quote << (ci->email ? ci->email : "") << ", ";
+ query << ci->time_registered << ", " << ci->last_used << ", " << mysqlpp::quote << (ci->last_topic ? ci->last_topic : "");
+ query << ", " << mysqlpp::quote << (!ci->last_topic_setter.empty() ? ci->last_topic_setter : "");
+ query << ", " << ci->last_topic_time << ", '" << (!flags.empty() ? flags : "") << "', ";
+ query << mysqlpp::quote << (ci->forbidby ? ci->forbidby : "") << ", " << mysqlpp::quote << (ci->forbidreason ? ci->forbidreason : "") << ", " << ci->bantype << ", '";
+ query << mlockon << "', '" << mlockoff << "', '";
+ query << mlockparams << "', " << mysqlpp::quote << (ci->entry_message ? ci->entry_message : "") << ", ";
+ query << ci->memos.memomax << ", " << mysqlpp::quote << (ci->bi ? ci->bi->nick : "") << ", '" << GetBotFlags(ci->botflags);
+ query << "', " << ci->capsmin << ", " << ci->capspercent << ", " << ci->floodlines;
+ query << ", " << ci->floodsecs << ", " << ci->repeattimes << ") ";
+ query << "ON DUPLICATE KEY UPDATE founder=VALUES(founder), successor=VALUES(successor), descr=VALUES(descr), url=VALUES(url), email=VALUES(email), 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), forbidby=VALUES(forbidby), forbidreason=VALUES(forbidreason), bantype=VALUES(bantype), mlock_on=VALUES(mlock_on), mlock_off=VALUES(mlock_off), mlock_params=VALUES(mlock_params), entry_message=VALUES(entry_message), memomax=VALUES(memomax), botnick=VALUES(botnick), botflags=VALUES(botflags), capsmin=VALUES(capsmin), capspercent=VALUES(capspercent), floodlines=VALUES(floodlines), floodsecs=VALUES(floodsecs), repeattimes=VALUES(repeattimes)";
+ ExecuteQuery(query);
+ }
+
+ void OnChanSuspend(ChannelInfo *ci)
+ {
+ mysqlpp::Query query(Me->Con);
+ query << "UPDATE `anope_cs_info` SET `flags` = '" << BuildFlagsList(ci) << "' WHERE `name` = " << mysqlpp::quote << ci->name;
+ ExecuteQuery(query);
+ query << "UPDATE `anope_cs_info` SET `forbidby` = " << mysqlpp::quote << ci->forbidby << " WHERE `name` = " << mysqlpp::quote << ci->name;
+ ExecuteQuery(query);
+ query << "UPDATE `anope_cs_info` SET `forbidreason` = " << mysqlpp::quote << ci->forbidreason << " WHERE `name` = " << mysqlpp::quote << ci->name;
+ ExecuteQuery(query);
+ }
+
+ void OnBotCreate(BotInfo *bi)
+ {
+ mysqlpp::Query query(Me->Con);
+ query << "INSERT DELAYED INTO `anope_bs_core` (nick, user, host, rname, flags, created, chancount) VALUES(";
+ query << mysqlpp::quote << bi->nick << ", " << mysqlpp::quote << bi->user << ", " << mysqlpp::quote << bi->host << ", ";
+ query << mysqlpp::quote << bi->real << ", '" << GetBotServFlags(bi) << "', " << bi->created << ", " << bi->chancount << ") ";
+ query << "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)";
+ ExecuteQuery(query);
+ }
+
+ void OnBotChange(BotInfo *bi)
+ {
+ OnBotCreate(bi);
+ }
+
+ void OnBotDelete(BotInfo *bi)
+ {
+ mysqlpp::Query query(Me->Con);
+ query << "DELETE FROM `anope_bs_core` WHERE `nick` = " << mysqlpp::quote << bi->nick;
+ ExecuteQuery(query);
+ query << "UPDATE `anope_cs_info` SET `botnick` = '' WHERE `botnick` = " << mysqlpp::quote << bi->nick;
+ ExecuteQuery(query);
+ }
+
+ EventReturn OnBotAssign(User *sender, ChannelInfo *ci, BotInfo *bi)
+ {
+ mysqlpp::Query query(Me->Con);
+ query << "UPDATE `anope_cs_info` SET `botnick` = " << mysqlpp::quote << bi->nick << " WHERE `name` = " << mysqlpp::quote << ci->name;
+ ExecuteQuery(query);
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnBotUnAssign(User *sender, ChannelInfo *ci)
+ {
+ mysqlpp::Query query(Me->Con);
+ query << "UPDATE `anope_cs_info` SET `botnick` = '' WHERE `name` = " << mysqlpp::quote << ci->name;
+ ExecuteQuery(query);
+ return EVENT_CONTINUE;
+ }
+};
+
+MODULE_INIT(DBMySQLWrite)
+