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