diff options
-rw-r--r-- | src/modules/extra/m_sqlauth.cpp | 89 | ||||
-rw-r--r-- | src/modules/extra/m_sqlutils.cpp | 298 | ||||
-rw-r--r-- | src/modules/extra/m_sqlutils.h | 102 |
3 files changed, 424 insertions, 65 deletions
diff --git a/src/modules/extra/m_sqlauth.cpp b/src/modules/extra/m_sqlauth.cpp index a89880c59..2cb2eae3a 100644 --- a/src/modules/extra/m_sqlauth.cpp +++ b/src/modules/extra/m_sqlauth.cpp @@ -16,7 +16,6 @@ */ #include <string> -#include <map> #include "users.h" #include "channels.h" @@ -24,14 +23,14 @@ #include "inspircd.h" #include "helperfuncs.h" #include "m_sqlv2.h" +#include "m_sqlutils.h" /* $ModDesc: Allow/Deny connections based upon an arbitary SQL table */ -typedef std::map<unsigned int, userrec*> QueryUserMap; - class ModuleSQLAuth : public Module { Server* Srv; + Module* SQLutils; std::string usertable; std::string userfield; @@ -43,13 +42,22 @@ class ModuleSQLAuth : public Module bool verbose; - QueryUserMap qumap; - public: ModuleSQLAuth(Server* Me) - : Module::Module(Me) + : Module::Module(Me), Srv(Me) { - Srv = Me; + SQLutils = Srv->FindFeature("SQLutils"); + + if(SQLutils) + { + log(DEBUG, "Successfully got SQLutils pointer"); + } + else + { + log(DEFAULT, "ERROR: This module requires a module offering the 'SQLutils' feature (usually m_sqlutils.so). Please load it and try again."); + throw ModuleException("This module requires a module offering the 'SQLutils' feature (usually m_sqlutils.so). Please load it and try again."); + } + OnRehash(""); } @@ -111,25 +119,21 @@ public: * came to be processed we'd get an invalid userrec* out of the map. Now we *could* solve this by watching * OnUserDisconnect() and iterating the map every time someone quits to make sure they didn't have any queries * in progress, but that would be relatively slow and inefficient. Instead (thanks to w00t ;p) we attach a list - * of query IDs associated with it to the userrec, so in OnUserDisconnect() we can remove it immediately. - */ + * of query IDs associated with it to the userrec, so in OnUserDisconnect() we can remove it immediately. + */ log(DEBUG, "Sent query, got given ID %lu", req.id); - qumap.insert(std::make_pair(req.id, user)); - - if(!user->Extend("sqlauth_queryid", new unsigned long(req.id))) - { - log(DEBUG, "BUG: user being sqlauth'd already extended with 'sqlauth_queryid' :/"); - } + AssociateUser(this, SQLutils, user, req.id).Send(); + return true; } else { log(DEBUG, "SQLrequest failed: %s", req.error.Str()); - + if (verbose) WriteOpers("Forbidden connection from %s!%s@%s (SQL query failed: %s)", user->nick, user->ident, user->host, req.error.Str()); - + return false; } } @@ -145,31 +149,16 @@ public: if(strcmp(SQLRESID, request->GetData()) == 0) { SQLresult* res; - QueryUserMap::iterator iter; res = static_cast<SQLresult*>(request); log(DEBUG, "Got SQL result (%s) with ID %lu", res->GetData(), res->id); - iter = qumap.find(res->id); + userrec* user = GetAssocUser(this, SQLutils, res->id).S().user; + UnAssociate(this, SQLutils, res->id).S(); - if(iter != qumap.end()) + if(user) { - userrec* user; - unsigned long* id; - - user = iter->second; - - /* Remove our ID from the lookup table to keep it as small and neat as possible */ - qumap.erase(iter); - - /* Cleanup the userrec, no point leaving this here */ - if(user->GetExt("sqlauth_queryid", id)) - { - user->Shrink("sqlauth_queryid"); - delete id; - } - if(res->error.Id() == NO_ERROR) { log(DEBUG, "Associated query ID %lu with user %s", res->id, user->nick); @@ -209,36 +198,6 @@ public: virtual void OnUserDisconnect(userrec* user) { - unsigned long* id; - - if(user->GetExt("sqlauth_queryid", id)) - { - QueryUserMap::iterator iter; - - iter = qumap.find(*id); - - if(iter != qumap.end()) - { - if(iter->second == user) - { - qumap.erase(iter); - - log(DEBUG, "Erased query from map associated with quitting user %s", user->nick); - } - else - { - log(DEBUG, "BUG: ID associated with user %s doesn't have the same userrec* associated with it in the map"); - } - } - else - { - log(DEBUG, "BUG: user %s was extended with sqlauth_queryid but there was nothing matching in the map", user->nick); - } - - user->Shrink("sqlauth_queryid"); - delete id; - } - user->Shrink("sqlauthed"); user->Shrink("sqlauth_failed"); } diff --git a/src/modules/extra/m_sqlutils.cpp b/src/modules/extra/m_sqlutils.cpp new file mode 100644 index 000000000..3bde05fd9 --- /dev/null +++ b/src/modules/extra/m_sqlutils.cpp @@ -0,0 +1,298 @@ +/* +------------------------------------+ + * | Inspire Internet Relay Chat Daemon | + * +------------------------------------+ + * + * InspIRCd is copyright (C) 2002-2004 ChatSpike-Dev. + * E-mail: + * <brain@chatspike.net> + * <Craig@chatspike.net> + * <omster@gmail.com> + * + * Written by Craig Edwards, Craig McLure, and others. + * This program is free but copyrighted software; see + * the file COPYING for details. + * + * --------------------------------------------------- + */ + +#include <sstream> +#include <string> +#include <map> +#include <list> + +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "helperfuncs.h" +#include "inspircd.h" +#include "configreader.h" + +#include "m_sqlutils.h" + +/* $ModDesc: Provides some utilities to SQL client modules, such as mapping queries to users and channels */ + +typedef std::map<unsigned long, userrec*> IdUserMap; +typedef std::map<unsigned long, chanrec*> IdChanMap; +typedef std::list<unsigned long> AssocIdList; + +class ModuleSQLutils : public Module +{ +private: + Server* Srv; + + IdUserMap iduser; + IdChanMap idchan; + +public: + ModuleSQLutils(Server* Me) + : Module::Module(Me), Srv(Me) + { + log(DEBUG, "%s 'SQLutils' feature", Srv->PublishFeature("SQLutils", this) ? "Published" : "Couldn't publish"); + } + + void Implements(char* List) + { + List[I_OnChannelDelete] = List[I_OnUnloadModule] = List[I_OnRequest] = List[I_OnUserDisconnect] = 1; + } + + virtual char* OnRequest(Request* request) + { + if(strcmp(SQLUTILAU, request->GetData()) == 0) + { + AssociateUser* req = (AssociateUser*)request; + + log(DEBUG, "Associated ID %lu with user %s", req->id, req->user->nick); + + iduser.insert(std::make_pair(req->id, req->user)); + + AttachList(req->user, req->id); + } + else if(strcmp(SQLUTILAC, request->GetData()) == 0) + { + AssociateChan* req = (AssociateChan*)request; + + log(DEBUG, "Associated ID %lu with channel %s", req->id, req->chan->name); + + idchan.insert(std::make_pair(req->id, req->chan)); + + AttachList(req->chan, req->id); + } + else if(strcmp(SQLUTILUA, request->GetData()) == 0) + { + UnAssociate* req = (UnAssociate*)request; + + /* Unassociate a given query ID with all users and channels + * it is associated with. + */ + + log(DEBUG, "Unassociating ID %lu with all users and channels", req->id); + + DoUnAssociate(iduser, req->id); + DoUnAssociate(idchan, req->id); + } + else if(strcmp(SQLUTILGU, request->GetData()) == 0) + { + GetAssocUser* req = (GetAssocUser*)request; + + IdUserMap::iterator iter = iduser.find(req->id); + + log(DEBUG, "Looking up user associated with ID %lu", req->id); + + if(iter != iduser.end()) + { + log(DEBUG, "Found user %s", iter->second->nick); + req->user = iter->second; + } + } + else if(strcmp(SQLUTILGC, request->GetData()) == 0) + { + GetAssocChan* req = (GetAssocChan*)request; + + IdChanMap::iterator iter = idchan.find(req->id); + + log(DEBUG, "Looking up channel associated with ID %lu", req->id); + + if(iter != idchan.end()) + { + log(DEBUG, "Found channel %s", iter->second->name); + req->chan = iter->second; + } + } + else + { + log(DEBUG, "Got unsupported API version string: %s", request->GetData()); + return NULL; + } + + return SQLUTILSUCCESS; + } + + virtual void OnUserDisconnect(userrec* user) + { + /* A user is disconnecting, first we need to check if they have a list of queries associated with them. + * Then, if they do, we need to erase each of them from our IdUserMap (iduser) so when the module that + * associated them asks to look them up then it gets a NULL result and knows to discard the query. + */ + AssocIdList* il; + + if(user->GetExt("sqlutils_queryids", il)) + { + for(AssocIdList::iterator listiter = il->begin(); listiter != il->end(); listiter++) + { + IdUserMap::iterator iter; + + iter = iduser.find(*listiter); + + if(iter != iduser.end()) + { + if(iter->second == user) + { + log(DEBUG, "Erased query from map associated with quitting user %s", user->nick); + } + else + { + log(DEBUG, "BUG: ID associated with user %s doesn't have the same userrec* associated with it in the map (erasing anyway)", user->nick); + } + + iduser.erase(iter); + } + else + { + log(DEBUG, "BUG: user %s was extended with sqlutils_queryids but there was nothing matching in the map", user->nick); + } + } + + user->Shrink("sqlutils_queryids"); + delete il; + } + } + + void AttachList(Extensible* obj, unsigned long id) + { + AssocIdList* il; + + if(!obj->GetExt("sqlutils_queryids", il)) + { + /* Doesn't already exist, create a new list and attach it. */ + il = new AssocIdList; + obj->Extend("sqlutils_queryids", il); + } + + /* Now either way we have a valid list in il, attached. */ + il->push_back(id); + } + + void RemoveFromList(Extensible* obj, unsigned long id) + { + AssocIdList* il; + + if(obj->GetExt("sqlutils_queryids", il)) + { + /* Only do anything if the list exists... (which it ought to) */ + il->remove(id); + + if(il->empty()) + { + /* If we just emptied it.. */ + delete il; + obj->Shrink("sqlutils_queryids"); + } + } + } + + template <class T> void DoUnAssociate(T &map, unsigned long id) + { + /* For each occurence of 'id' (well, only one..it's not a multimap) in 'map' + * remove it from the map, take an Extensible* value from the map and remove + * 'id' from the list of query IDs attached to it. + */ + typename T::iterator iter = map.find(id); + + if(iter != map.end()) + { + /* Found a value indexed by 'id', call RemoveFromList() + * on it with 'id' to remove 'id' from the list attached + * to the value. + */ + RemoveFromList(iter->second, id); + + log(DEBUG, "Removed query %lu from map and removed references to it on value", id); + } + else + { + log(DEBUG, "Nothing associated with query %lu", id); + } + } + + virtual void OnChannelDelete(chanrec* chan) + { + /* A channel is being destroyed, first we need to check if it has a list of queries associated with it. + * Then, if it does, we need to erase each of them from our IdChanMap (idchan) so when the module that + * associated them asks to look them up then it gets a NULL result and knows to discard the query. + */ + AssocIdList* il; + + if(chan->GetExt("sqlutils_queryids", il)) + { + for(AssocIdList::iterator listiter = il->begin(); listiter != il->end(); listiter++) + { + IdChanMap::iterator iter; + + iter = idchan.find(*listiter); + + if(iter != idchan.end()) + { + if(iter->second == chan) + { + log(DEBUG, "Erased query from map associated with dying channnel %s", chan->name); + } + else + { + log(DEBUG, "BUG: ID associated with channel %s doesn't have the same chanrec* associated with it in the map (erasing anyway)", chan->name); + } + + idchan.erase(iter); + } + else + { + log(DEBUG, "BUG: channel %s was extended with sqlutils_queryids but there was nothing matching in the map", chan->name); + } + } + + chan->Shrink("sqlutils_queryids"); + delete il; + } + } + + virtual Version GetVersion() + { + return Version(1, 0, 0, 0, VF_STATIC|VF_VENDOR|VF_SERVICEPROVIDER); + } + + virtual ~ModuleSQLutils() + { + } +}; + +class ModuleSQLutilsFactory : public ModuleFactory +{ + public: + ModuleSQLutilsFactory() + { + } + + ~ModuleSQLutilsFactory() + { + } + + virtual Module * CreateModule(Server* Me) + { + return new ModuleSQLutils(Me); + } +}; + + +extern "C" void * init_module( void ) +{ + return new ModuleSQLutilsFactory; +} diff --git a/src/modules/extra/m_sqlutils.h b/src/modules/extra/m_sqlutils.h new file mode 100644 index 000000000..bf13e4635 --- /dev/null +++ b/src/modules/extra/m_sqlutils.h @@ -0,0 +1,102 @@ +#ifndef INSPIRCD_SQLUTILS +#define INSPIRCD_SQLUTILS + +#include "modules.h" + +#define SQLUTILAU "SQLutil AssociateUser" +#define SQLUTILAC "SQLutil AssociateChan" +#define SQLUTILUA "SQLutil UnAssociate" +#define SQLUTILGU "SQLutil GetAssocUser" +#define SQLUTILGC "SQLutil GetAssocChan" +#define SQLUTILSUCCESS "You shouldn't be reading this (success)" + +class AssociateUser : public Request +{ +public: + unsigned long id; + userrec* user; + + AssociateUser(Module* s, Module* d, userrec* u, unsigned long i) + : Request(SQLUTILAU, s, d), id(i), user(u) + { + } + + AssociateUser& S() + { + Send(); + return *this; + } +}; + +class AssociateChan : public Request +{ +public: + unsigned long id; + chanrec* chan; + + AssociateChan(Module* s, Module* d, chanrec* u, unsigned long i) + : Request(SQLUTILAC, s, d), id(i), chan(u) + { + } + + AssociateChan& S() + { + Send(); + return *this; + } +}; + +class UnAssociate : public Request +{ +public: + unsigned long id; + + UnAssociate(Module* s, Module* d, unsigned long i) + : Request(SQLUTILUA, s, d), id(i) + { + } + + UnAssociate& S() + { + Send(); + return *this; + } +}; + +class GetAssocUser : public Request +{ +public: + unsigned long id; + userrec* user; + + GetAssocUser(Module* s, Module* d, unsigned long i) + : Request(SQLUTILGU, s, d), id(i), user(NULL) + { + } + + GetAssocUser& S() + { + Send(); + return *this; + } +}; + +class GetAssocChan : public Request +{ +public: + unsigned long id; + chanrec* chan; + + GetAssocChan(Module* s, Module* d, unsigned long i) + : Request(SQLUTILGC, s, d), id(i), chan(NULL) + { + } + + GetAssocChan& S() + { + Send(); + return *this; + } +}; + +#endif |