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