]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/extra/m_sqlutils.cpp
Add m_sqlutils - Currently provides ID->chan/user lookups
[user/henk/code/inspircd.git] / src / modules / extra / m_sqlutils.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd is copyright (C) 2002-2004 ChatSpike-Dev.
6  *                       E-mail:
7  *                <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *                <omster@gmail.com>
10  *     
11  * Written by Craig Edwards, Craig McLure, and others.
12  * This program is free but copyrighted software; see
13  *            the file COPYING for details.
14  *
15  * ---------------------------------------------------
16  */
17
18 #include <sstream>
19 #include <string>
20 #include <map>
21 #include <list>
22
23 #include "users.h"
24 #include "channels.h"
25 #include "modules.h"
26 #include "helperfuncs.h"
27 #include "inspircd.h"
28 #include "configreader.h"
29
30 #include "m_sqlutils.h"
31
32 /* $ModDesc: Provides some utilities to SQL client modules, such as mapping queries to users and channels */
33
34 typedef std::map<unsigned long, userrec*> IdUserMap;
35 typedef std::map<unsigned long, chanrec*> IdChanMap;
36 typedef std::list<unsigned long> AssocIdList;
37
38 class ModuleSQLutils : public Module
39 {
40 private:
41         Server* Srv;
42
43         IdUserMap iduser;
44         IdChanMap idchan;
45
46 public:
47         ModuleSQLutils(Server* Me)
48         : Module::Module(Me), Srv(Me)
49         {
50                 log(DEBUG, "%s 'SQLutils' feature", Srv->PublishFeature("SQLutils", this) ? "Published" : "Couldn't publish");
51         }
52
53         void Implements(char* List)
54         {
55                 List[I_OnChannelDelete] = List[I_OnUnloadModule] = List[I_OnRequest] =  List[I_OnUserDisconnect] = 1;
56         }
57
58         virtual char* OnRequest(Request* request)
59         {
60                 if(strcmp(SQLUTILAU, request->GetData()) == 0)
61                 {
62                         AssociateUser* req = (AssociateUser*)request;
63                         
64                         log(DEBUG, "Associated ID %lu with user %s", req->id, req->user->nick);
65                         
66                         iduser.insert(std::make_pair(req->id, req->user));
67                         
68                         AttachList(req->user, req->id);
69                 }
70                 else if(strcmp(SQLUTILAC, request->GetData()) == 0)
71                 {
72                         AssociateChan* req = (AssociateChan*)request;
73
74                         log(DEBUG, "Associated ID %lu with channel %s", req->id, req->chan->name);
75                         
76                         idchan.insert(std::make_pair(req->id, req->chan));                      
77                         
78                         AttachList(req->chan, req->id);
79                 }
80                 else if(strcmp(SQLUTILUA, request->GetData()) == 0)
81                 {
82                         UnAssociate* req = (UnAssociate*)request;
83                         
84                         /* Unassociate a given query ID with all users and channels
85                          * it is associated with.
86                          */
87                         
88                         log(DEBUG, "Unassociating ID %lu with all users and channels", req->id);
89                         
90                         DoUnAssociate(iduser, req->id);
91                         DoUnAssociate(idchan, req->id);
92                 }
93                 else if(strcmp(SQLUTILGU, request->GetData()) == 0)
94                 {
95                         GetAssocUser* req = (GetAssocUser*)request;
96                         
97                         IdUserMap::iterator iter = iduser.find(req->id);
98                         
99                         log(DEBUG, "Looking up user associated with ID %lu", req->id);
100                         
101                         if(iter != iduser.end())
102                         {
103                                 log(DEBUG, "Found user %s", iter->second->nick);
104                                 req->user = iter->second;
105                         }
106                 }
107                 else if(strcmp(SQLUTILGC, request->GetData()) == 0)
108                 {
109                         GetAssocChan* req = (GetAssocChan*)request;                     
110                         
111                         IdChanMap::iterator iter = idchan.find(req->id);
112                         
113                         log(DEBUG, "Looking up channel associated with ID %lu", req->id);
114                         
115                         if(iter != idchan.end())
116                         {
117                                 log(DEBUG, "Found channel %s", iter->second->name);
118                                 req->chan = iter->second;
119                         }
120                 }
121                 else
122                 {
123                         log(DEBUG, "Got unsupported API version string: %s", request->GetData());
124                         return NULL;
125                 }
126                 
127                 return SQLUTILSUCCESS;
128         }
129         
130         virtual void OnUserDisconnect(userrec* user)
131         {
132                 /* A user is disconnecting, first we need to check if they have a list of queries associated with them.
133                  * Then, if they do, we need to erase each of them from our IdUserMap (iduser) so when the module that
134                  * associated them asks to look them up then it gets a NULL result and knows to discard the query.
135                  */
136                 AssocIdList* il;
137                 
138                 if(user->GetExt("sqlutils_queryids", il))
139                 {
140                         for(AssocIdList::iterator listiter = il->begin(); listiter != il->end(); listiter++)
141                         {
142                                 IdUserMap::iterator iter;
143                         
144                                 iter = iduser.find(*listiter);
145                         
146                                 if(iter != iduser.end())
147                                 {
148                                         if(iter->second == user)
149                                         {
150                                                 log(DEBUG, "Erased query from map associated with quitting user %s", user->nick);
151                                         }
152                                         else
153                                         {
154                                                 log(DEBUG, "BUG: ID associated with user %s doesn't have the same userrec* associated with it in the map (erasing anyway)", user->nick);
155                                         }
156
157                                         iduser.erase(iter);                                     
158                                 }
159                                 else
160                                 {
161                                         log(DEBUG, "BUG: user %s was extended with sqlutils_queryids but there was nothing matching in the map", user->nick);
162                                 }
163                         }
164                         
165                         user->Shrink("sqlutils_queryids");
166                         delete il;
167                 }
168         }
169         
170         void AttachList(Extensible* obj, unsigned long id)
171         {
172                 AssocIdList* il;
173                 
174                 if(!obj->GetExt("sqlutils_queryids", il))
175                 {
176                         /* Doesn't already exist, create a new list and attach it. */
177                         il = new AssocIdList;
178                         obj->Extend("sqlutils_queryids", il);
179                 }
180                 
181                 /* Now either way we have a valid list in il, attached. */
182                 il->push_back(id);
183         }
184         
185         void RemoveFromList(Extensible* obj, unsigned long id)
186         {
187                 AssocIdList* il;
188                 
189                 if(obj->GetExt("sqlutils_queryids", il))
190                 {
191                         /* Only do anything if the list exists... (which it ought to) */
192                         il->remove(id);
193                         
194                         if(il->empty())
195                         {
196                                 /* If we just emptied it.. */
197                                 delete il;
198                                 obj->Shrink("sqlutils_queryids");
199                         }
200                 }
201         }
202         
203         template <class T> void DoUnAssociate(T &map, unsigned long id)
204         {
205                 /* For each occurence of 'id' (well, only one..it's not a multimap) in 'map'
206                  * remove it from the map, take an Extensible* value from the map and remove
207                  * 'id' from the list of query IDs attached to it.
208                  */
209                 typename T::iterator iter = map.find(id);
210                 
211                 if(iter != map.end())
212                 {
213                         /* Found a value indexed by 'id', call RemoveFromList()
214                          * on it with 'id' to remove 'id' from the list attached
215                          * to the value.
216                          */
217                         RemoveFromList(iter->second, id);
218                         
219                         log(DEBUG, "Removed query %lu from map and removed references to it on value", id);
220                 }
221                 else
222                 {
223                         log(DEBUG, "Nothing associated with query %lu", id);
224                 }
225         }
226         
227         virtual void OnChannelDelete(chanrec* chan)
228         {
229                 /* A channel is being destroyed, first we need to check if it has a list of queries associated with it.
230                  * Then, if it does, we need to erase each of them from our IdChanMap (idchan) so when the module that
231                  * associated them asks to look them up then it gets a NULL result and knows to discard the query.
232                  */
233                 AssocIdList* il;
234                 
235                 if(chan->GetExt("sqlutils_queryids", il))
236                 {
237                         for(AssocIdList::iterator listiter = il->begin(); listiter != il->end(); listiter++)
238                         {
239                                 IdChanMap::iterator iter;
240                         
241                                 iter = idchan.find(*listiter);
242                         
243                                 if(iter != idchan.end())
244                                 {
245                                         if(iter->second == chan)
246                                         {
247                                                 log(DEBUG, "Erased query from map associated with dying channnel %s", chan->name);
248                                         }
249                                         else
250                                         {
251                                                 log(DEBUG, "BUG: ID associated with channel %s doesn't have the same chanrec* associated with it in the map (erasing anyway)", chan->name);
252                                         }
253
254                                         idchan.erase(iter);                                     
255                                 }
256                                 else
257                                 {
258                                         log(DEBUG, "BUG: channel %s was extended with sqlutils_queryids but there was nothing matching in the map", chan->name);
259                                 }
260                         }
261                         
262                         chan->Shrink("sqlutils_queryids");
263                         delete il;
264                 }
265         }
266                         
267         virtual Version GetVersion()
268         {
269                 return Version(1, 0, 0, 0, VF_STATIC|VF_VENDOR|VF_SERVICEPROVIDER);
270         }
271         
272         virtual ~ModuleSQLutils()
273         {
274         }       
275 };
276
277 class ModuleSQLutilsFactory : public ModuleFactory
278 {
279  public:
280         ModuleSQLutilsFactory()
281         {
282         }
283         
284         ~ModuleSQLutilsFactory()
285         {
286         }
287         
288         virtual Module * CreateModule(Server* Me)
289         {
290                 return new ModuleSQLutils(Me);
291         }
292 };
293
294
295 extern "C" void * init_module( void )
296 {
297         return new ModuleSQLutilsFactory;
298 }