diff options
author | Adam- <Adam-@5417fbe8-f217-4b02-8779-1006273d7864> | 2010-02-28 17:33:31 +0000 |
---|---|---|
committer | Adam- <Adam-@5417fbe8-f217-4b02-8779-1006273d7864> | 2010-02-28 17:33:31 +0000 |
commit | 3f80e1cad02735f692a5a300ee3b200a21f330aa (patch) | |
tree | d7bcd3c5d1cfd54c725f4c9a0eb7984632b6be67 /src | |
parent | 393b5ab26e952f427f40a8ed6d10acfdf6ead96c (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')
-rw-r--r-- | src/Makefile | 1 | ||||
-rw-r--r-- | src/bin/CMakeLists.txt | 2 | ||||
-rwxr-xr-x | src/bin/mydbgen | 225 | ||||
-rw-r--r-- | src/core/cs_access.c | 13 | ||||
-rw-r--r-- | src/core/cs_xop.c | 10 | ||||
-rw-r--r-- | src/core/db_plain.cpp | 20 | ||||
-rw-r--r-- | src/core/ns_register.c | 3 | ||||
-rw-r--r-- | src/misc.c | 12 | ||||
-rw-r--r-- | src/modules/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/modules/Makefile | 8 | ||||
-rw-r--r-- | src/modules/mysql/db_mysql.h | 238 | ||||
-rw-r--r-- | src/modules/mysql/db_mysql_execute.cpp | 162 | ||||
-rw-r--r-- | src/modules/mysql/db_mysql_read.cpp | 515 | ||||
-rw-r--r-- | src/modules/mysql/db_mysql_write.cpp | 740 | ||||
-rw-r--r-- | src/nickcore.cpp | 6 |
15 files changed, 1928 insertions, 28 deletions
diff --git a/src/Makefile b/src/Makefile index 90705ce29..b0b4c8863 100644 --- a/src/Makefile +++ b/src/Makefile @@ -117,6 +117,7 @@ install: services test -d ${INSTDIR}/data || mkdir ${INSTDIR}/data (cd ../lang ; $(MAKE) install) $(CP) ../data/* $(INSTDIR)/data + $(INSTALL) bin/mydbgen $(INSTDIR)/data/mydbgen test -d $(INSTDIR)/data/backups || mkdir $(INSTDIR)/data/backups test -d $(INSTDIR)/data/logs || mkdir $(INSTDIR)/data/logs @if [ "$(INSTDIR)/data/modules" ] ; then \ diff --git a/src/bin/CMakeLists.txt b/src/bin/CMakeLists.txt index 5b50147ee..7c6fe514b 100644 --- a/src/bin/CMakeLists.txt +++ b/src/bin/CMakeLists.txt @@ -1,7 +1,7 @@ # If not on Windows, generate anoperc and install it along with mydbgen if(NOT WIN32) configure_file(${Anope_SOURCE_DIR}/src/bin/anoperc.in ${Anope_BINARY_DIR}/src/bin/anoperc) - install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/anoperc + install (PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/anoperc ${CMAKE_CURRENT_SOURCE_DIR}/mydbgen DESTINATION bin ) diff --git a/src/bin/mydbgen b/src/bin/mydbgen new file mode 100755 index 000000000..f3599a252 --- /dev/null +++ b/src/bin/mydbgen @@ -0,0 +1,225 @@ +#!/bin/sh +# +# $Id$ + +# Location of the .sql file with the schema +DBSQL="tables.sql" + +# Schema Version +SVER="1" + +# Local Version, defaults to 0 +LVER="0" + +TFILE="/tmp/.anopedb.$$" + +if [ "`eval echo -n 'a'`" = "-n a" ] ; then + c="\c" +else + n="-n" +fi + +# Fix for bug 10 +for try in HOME/services anope/data ../data data .. . +do + if [ -f "$try/$DBSQL" ]; then + DBFILE="$try/$DBSQL" + fi +done + +if [ ! -f "./$DBFILE" ] ; then + echo "Error: Required file $DBSQL was not found!"; + exit +fi + +echo "" +echo "This script will guide you through the process of configuring your Anope" +echo "installation to make use of MySQL support. This script must be used for both" +echo "new installs as well as for upgrading for users who have a previous version" +echo "of Anope installed" + +while [ -z "$SQLHOST" ] ; do + echo "" + echo "What is the hostname of your MySQL server?" + echo $n "-> $c" + read cc + if [ ! -z "$cc" ] ; then + SQLHOST=$cc + fi +done + +while [ -z "$SQLUSER" ] ; do + echo "" + echo "What is your MySQL username?" + echo $n "-> $c" + read cc + if [ ! -z "$cc" ] ; then + SQLUSER=$cc + fi +done + +OLD_TTY=`stty -g` + +echo "" +echo "What is your MySQL password?" +echo $n "-> $c" +stty -echo echonl +read cc +SQLPASS_PREFIX="" +if [ ! -z "$cc" ] ; then + SQLPASS_PREFIX="-p" + SQLPASS=$cc +fi +stty $OLD_TTY + +mysqlshow -h$SQLHOST -u$SQLUSER $SQLPASS_PREFIX$SQLPASS >/dev/null 2>&1 +if test "$?" = "1" ; then + echo "Error: Unable to login, verify your login/password and hostname" + exit +fi + +while [ -z "$SQLDB" ] ; do + echo "" + echo "What is the name of the Anope SQL database?" + echo $n "-> $c" + read cc + if [ ! -z "$cc" ] ; then + SQLDB=$cc + fi +done + +MYSQLDUMP="mysqldump -h$SQLHOST -u$SQLUSER $SQLPASS_PREFIX$SQLPASS $SQLDB" +MYSQLSHOW="mysqlshow -h$SQLHOST -u$SQLUSER $SQLPASS_PREFIX$SQLPASS $SQLDB" +MYSQL="mysql -h$SQLHOST -u$SQLUSER $SQLPASS_PREFIX$SQLPASS $SQLDB" + +echo "" + +$MYSQLSHOW | grep -q $SQLDB +if test "$?" = "1" ; then + echo -n "Unable to find databse, creating... " + mysql -h$SQLHOST -u$SQLUSER $SQLPASS_PREFIX$SQLPASS -Bs -e "create database $SQLDB" >/dev/null 2>&1 + if test "$?" = "0" ; then + echo "done!" + else + echo "failed!" + FAILED="$FAILED 'database creation'" + fi +fi + +$MYSQL -Bs -e "show tables like 'anope_os_core'" | grep -q anope_os_core +if test "$?" = "1" ; then + echo -n "Unable to find Anope schema, creating... " + $MYSQL < $DBFILE + if test "$?" = "0" ; then + echo "done!" + else + echo "failed!" + FAILED="$FAILED 'schema creation'" + fi +else + # Introduced on Anope 1.6.0 -> Table anope_info + $MYSQL -Bs -e "show tables like 'anope_info'" | grep -q anope_info + if test "$?" = "1" ; then + echo -n "Unable to find Anope info table, creating... " + echo "CREATE TABLE anope_info (version int, date datetime) TYPE=MyISAM" > $TFILE + mysql -h$SQLHOST -u$SQLUSER $SQLPASS_PREFIX$SQLPASS $SQLDB < $TFILE >/dev/null 2>&1 + if test "$?" = "0" ; then + echo "done!" + + else + echo "failed!" + FAILED="$FAILED 'anope_info table'" + fi + else + LVER="$($MYSQL -sB -e "select version from anope_info")" + if test "x$LVER" = "x" ; then + LVER=0 + fi + fi + + # Introduced on Anope 1.5.14.5 -> anope_cs_info.memomax + $MYSQL -Bs -e "describe anope_cs_info memomax" 2> /dev/null | grep -q memomax + if test "$?" = "1" ; then + echo -n "Unable to find anope_cs_info.memomax, altering... " + echo "ALTER TABLE anope_cs_info ADD memomax smallint unsigned NOT NULL default 0" > $TFILE + mysql -h$SQLHOST -u$SQLUSER $SQLPASS_PREFIX$SQLPASS $SQLDB < $TFILE >/dev/null 2>&1 + if test "$?" = "0" ; then + echo "done!" + + else + echo "failed!" + FAILED="$FAILED 'anope_cs_info.memomax alter'" + fi + fi + + # Introduced on Anope 1.5.14.5 -> anope_cs_info.ttb + $MYSQL -Bs -e "describe anope_cs_info ttb" 2> /dev/null | grep -q ttb + if test "$?" = "1" ; then + echo -n "Unable to find anope_cs_info.ttb, altering... " + echo "ALTER TABLE anope_cs_info ADD ttb smallint NOT NULL default 0" > $TFILE + mysql -h$SQLHOST -u$SQLUSER $SQLPASS_PREFIX$SQLPASS $SQLDB < $TFILE >/dev/null 2>&1 + if test "$?" = "0" ; then + echo "done!" + + else + echo "failed!" + FAILED="$FAILED 'anope_cs_info.ttb alter'" + fi + fi + + # Introduced on Anope 1.7.7 -> status smallint to inst unsigned. + echo "Blindly altering status for bigger capacity... " + echo "ALTER TABLE anope_ns_alias CHANGE status status int(11) unsigned NOT NULL default 0" > $TFILE + mysql -h$SQLHOST -u$SQLUSER $SQLPASS_PREFIX$SQLPASS $SQLDB < $TFILE >/dev/null 2>&1 + + # Introduced on Anope 1.7.8 (620) proxy scanner removed. + echo "Removing proxy scanner cache... " + echo "DROP TABLE IF EXISTS anope_os_cache" > $TFILE + mysql -h$SQLHOST -u$SQLUSER $SQLPASS_PREFIX$SQLPASS $SQLDB < $TFILE >/dev/null 2>&1 + + echo "done!" + +fi + +echo "" + +# Insert initial version number. This will have to be redesigned for 1.7 +if [ $LVER -ne $SVER ]; then +echo -n "Inserting initial version number... " +$MYSQL -Bs -e "delete from anope_info" +echo "INSERT INTO anope_info (version, date) VALUES ($SVER, now())" > $TFILE +$MYSQL < $TFILE >/dev/null 2>&1 +if test "$?" = "0" ; then + echo "done!" +else + echo "failed!" + FAILED="$FAILED 'version insert'" +fi +fi + +rm -f $TFILE +if test "x$FAILED" = "x" ; then + # Try to find out more about this installation + SQLSOCK="$(mysql_config --socket 2> /dev/null)" + SQLPORT="$(mysql_config --port 2> /dev/null)" + echo "" + echo "Your MySQL setup is complete and your Anope schema is up to date. Make" + echo "sure you configure MySQL on your services.conf file prior to launching" + echo "Anope with MySQL support. Your configuration values are:" + echo "" + echo "mysql" + echo "{" + echo " database = \"$SQLDB\"" + echo " server = \"$SQLHOST\"" + echo " username = \"$SQLUSER\"" + echo " password = \"$SQLPASS\"" + echo " port = \"$SQLPORT\"" + echo " updatedelay = \"60\"" + echo "}" + echo "" +else + echo "The following operations failed:" + echo "$FAILED" +fi + +exit diff --git a/src/core/cs_access.c b/src/core/cs_access.c index d26891d73..bc05695a2 100644 --- a/src/core/cs_access.c +++ b/src/core/cs_access.c @@ -17,7 +17,6 @@ static int access_del(User * u, ChannelInfo *ci, ChanAccess * access, int *perm, int uacc) { - char *nick; if (!access->in_use) return 0; if (uacc <= access->level && !u->Account()->HasPriv("chanserv/access/modify")) @@ -25,11 +24,11 @@ static int access_del(User * u, ChannelInfo *ci, ChanAccess * access, int *perm, (*perm)++; return 0; } - nick = access->nc->display; + NickCore *nc = access->nc; access->nc = NULL; access->in_use = 0; - FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, nick)); + FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, nc)); return 1; } @@ -225,7 +224,7 @@ class CommandCSAccess : public Command } access->level = level; - FOREACH_MOD(I_OnAccessChange, OnAccessChange(ci, u, na->nick, level)); + FOREACH_MOD(I_OnAccessChange, OnAccessChange(ci, u, na, level)); Alog() << Config.s_ChanServ << ": " << u->GetMask() << " (level " << ulev << ") set access level " << access->level << " to " << na->nick << " (group " << nc->display << ") on channel " << ci->name; @@ -242,7 +241,7 @@ class CommandCSAccess : public Command std::string usernick = u->nick; ci->AddAccess(nc, level, usernick); - FOREACH_MOD(I_OnAccessAdd, OnAccessAdd(ci, u, na->nick, level)); + FOREACH_MOD(I_OnAccessAdd, OnAccessAdd(ci, u, na, level)); Alog() << Config.s_ChanServ << ": " << u->GetMask() << " (level " << ulev << ") set access level " << level << " to " << na->nick << " (group " << nc->display << ") on channel " << ci->name; @@ -326,13 +325,11 @@ class CommandCSAccess : public Command /* We'll free the access entries no longer in use... */ ci->CleanAccess(); - /* We don't know the nick if someone used numbers, so we trigger the event without - * nick param. We just do this once, even if someone enters a range. -Certus */ /* Only call this event if na exists (if they deleted by user, not numlist). * The callback for deleting by numlist will call this event otherwise - Adam */ if (na) { - FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, na->nick)); + FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, na->nc)); } } } diff --git a/src/core/cs_xop.c b/src/core/cs_xop.c index 1fa32265e..0f5f863dc 100644 --- a/src/core/cs_xop.c +++ b/src/core/cs_xop.c @@ -195,12 +195,12 @@ class XOPBase : public Command if (!change) { - FOREACH_MOD(I_OnAccessAdd, OnAccessAdd(ci, u, na->nick, level)); + FOREACH_MOD(I_OnAccessAdd, OnAccessAdd(ci, u, na, level)); notice_lang(Config.s_ChanServ, u, messages[XOP_ADDED], nc->display, ci->name.c_str()); } else { - FOREACH_MOD(I_OnAccessChange, OnAccessChange(ci, u, na->nick, level)); + FOREACH_MOD(I_OnAccessChange, OnAccessChange(ci, u, na, level)); notice_lang(Config.s_ChanServ, u, messages[XOP_MOVED], nc->display, ci->name.c_str()); } @@ -290,7 +290,7 @@ class XOPBase : public Command access->nc = NULL; access->in_use = 0; - FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, na->nick)); + FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, na->nc)); deleted = 1; } @@ -572,16 +572,16 @@ int xop_del(User *u, ChannelInfo *ci, ChanAccess *access, int *perm, int uacc, i { if (!access->in_use || access->level != xlev) return 0; - char *nick = access->nc->display; if (uacc <= access->level && !u->Account()->HasPriv("chanserv/access/modify")) { ++(*perm); return 0; } + NickCore *nc = access->nc; access->nc = NULL; access->in_use = 0; - FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, nick)); + FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, nc)); return 1; } diff --git a/src/core/db_plain.cpp b/src/core/db_plain.cpp index c1c9cdae5..ff2a4cdf3 100644 --- a/src/core/db_plain.cpp +++ b/src/core/db_plain.cpp @@ -74,7 +74,7 @@ static void ReadDatabase(Module *m = NULL) if (m) MOD_RESULT = m->OnDatabaseRead(params); - else + else { FOREACH_RESULT(I_OnDatabaseRead, OnDatabaseRead(params)); } @@ -119,7 +119,7 @@ static void ReadDatabase(Module *m = NULL) try { if (m) - MOD_RESULT = m->OnDatabaseReadMetadata(nc, key, params); + m->OnDatabaseReadMetadata(nc, key, params); else { FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(nc, key, params)); @@ -127,7 +127,7 @@ static void ReadDatabase(Module *m = NULL) } catch (const char *err) { - Alog() << "[db_plain}: " << err; + Alog() << "[db_plain]: " << err; } } else if (na && Type == MD_NA) @@ -135,7 +135,7 @@ static void ReadDatabase(Module *m = NULL) try { if (m) - MOD_RESULT = m->OnDatabaseReadMetadata(na, key, params); + m->OnDatabaseReadMetadata(na, key, params); else { FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(na, key, params)); @@ -143,7 +143,7 @@ static void ReadDatabase(Module *m = NULL) } catch (const char *err) { - Alog() << "[db_plain}: " << err; + Alog() << "[db_plain]: " << err; } } else if (bi && Type == MD_BI) @@ -151,7 +151,7 @@ static void ReadDatabase(Module *m = NULL) try { if (m) - MOD_RESULT = m->OnDatabaseReadMetadata(bi, key, params); + m->OnDatabaseReadMetadata(bi, key, params); else { FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(bi, key, params)); @@ -159,15 +159,15 @@ static void ReadDatabase(Module *m = NULL) } catch (const char *err) { - Alog() << "[db_plain}: " << err; - } + Alog() << "[db_plain]: " << err; } + } else if (ci && Type == MD_CH) { try { if (m) - MOD_RESULT = m->OnDatabaseReadMetadata(ci, key, params); + m->OnDatabaseReadMetadata(ci, key, params); else { FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(ci, key, params)); @@ -175,7 +175,7 @@ static void ReadDatabase(Module *m = NULL) } catch (const char *err) { - Alog() << "[db_plain}: " << err; + Alog() << "[db_plain]: " << err; } } } diff --git a/src/core/ns_register.c b/src/core/ns_register.c index a351711dc..fcd9c329c 100644 --- a/src/core/ns_register.c +++ b/src/core/ns_register.c @@ -88,6 +88,9 @@ class CommandNSConfirm : public Command validate_user(user); } } + + FOREACH_MOD(I_OnNickRegister, OnNickRegister(na)); + return MOD_CONT; } diff --git a/src/misc.c b/src/misc.c index 4c786a83c..a39886ccb 100644 --- a/src/misc.c +++ b/src/misc.c @@ -1056,6 +1056,18 @@ std::list<ci::string> BuildStringList(const ci::string &src) return Ret; } +std::vector<std::string> BuildStringVector(const std::string &src) +{ + spacesepstream tokens(src); + std::string token; + std::vector<std::string> Ret; + + while (tokens.GetToken(token)) + Ret.push_back(token); + + return Ret; +} + /*************************************************************************/ /** 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, ¶m)) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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) + diff --git a/src/nickcore.cpp b/src/nickcore.cpp index fc46cd6d4..84761ed79 100644 --- a/src/nickcore.cpp +++ b/src/nickcore.cpp @@ -62,6 +62,9 @@ NickCore::~NickCore() /* Log .. */ Alog() << Config.s_NickServ << ": deleting nickname group " << this->display; + /* Clear access before deleting display name, we want to be able to use the display name in the clear access event */ + this->ClearAccess(); + /* Now we can safely free it. */ delete [] this->display; @@ -72,8 +75,6 @@ NickCore::~NickCore() if (this->url) delete [] this->url; - this->ClearAccess(); - if (!this->memos.memos.empty()) { for (unsigned i = 0; i < this->memos.memos.size(); ++i) @@ -119,6 +120,7 @@ bool NickCore::HasPriv(const std::string &privstr) const void NickCore::AddAccess(const std::string &entry) { access.push_back(entry); + FOREACH_MOD(I_OnNickAddAccess, OnNickAddAccess(this, entry)); } std::string NickCore::GetAccess(unsigned entry) |