(thanks to the people reading git and excessively exploiting this bug... It was undet...
[NeonServV5.git] / src / WHOHandler.c
1 /* WHOHandler.c - NeonServ v5.6
2  * Copyright (C) 2011-2012  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17
18 #include "WHOHandler.h"
19 #include "ChanNode.h"
20 #include "UserNode.h"
21 #include "ChanUser.h"
22 #include "ModeNode.h"
23 #include "ClientSocket.h"
24 #include "IPNode.h"
25 #include "modules.h"
26 #include "log.h"
27
28 #define WHOQUEUETYPE_ISONQUEUE 0x01
29 #define WHOQUEUETYPE_USERLIST  0x02
30 #define WHOQUEUETYPE_USERAUTH  0x04
31 #define WHOQUEUETYPE_CHECKTYPE 0x07
32 #define WHOQUEUETYPE_FOUND     0x08
33
34 #define MAXCALLBACKS 10
35
36 struct WHOQueueEntry {
37     char type;
38     int whoid;
39     struct ChanNode *chan;
40     struct UserNode *user;
41     struct WHOQueueEntry *next;
42     void *callback[MAXCALLBACKS];
43     int module_id[MAXCALLBACKS];
44     void *data[MAXCALLBACKS];
45 };
46
47 static int checkWHOID(struct ClientSocket *client, int whoid) {
48     struct WHOQueueEntry *entry;
49     for(entry = client->whoqueue_first; entry; entry = entry->next) {
50         if(entry->whoid == whoid)
51             return 1;
52     }
53     return 0;
54 }
55
56 static struct WHOQueueEntry* addWHOQueueEntry(struct ClientSocket *client) {
57     SYNCHRONIZE(whohandler_sync);
58     int whoid = 0;
59     do {
60         whoid++;
61     } while(checkWHOID(client, whoid) && whoid < 1000);
62     if(whoid == 1000) {
63         DESYNCHRONIZE(whohandler_sync);
64         return NULL;
65     }
66     struct WHOQueueEntry *entry = malloc(sizeof(*entry));
67     if (!entry)
68     {
69         printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__);
70         DESYNCHRONIZE(whohandler_sync);
71         return NULL;
72     }
73     entry->next = NULL;
74     entry->whoid = whoid;
75     if(client->whoqueue_last) {
76         client->whoqueue_last->next = entry;
77     } else {
78         client->whoqueue_first = entry;
79     }
80     client->whoqueue_last = entry;
81     DESYNCHRONIZE(whohandler_sync);
82     return entry;
83 }
84
85 static struct WHOQueueEntry* getNextWHOQueueEntry(struct ClientSocket *client, int whoid, int freeEntry) {
86     if(!client->whoqueue_first) return NULL;
87     SYNCHRONIZE(whohandler_sync);
88     struct WHOQueueEntry *entry;
89     for(entry = client->whoqueue_first; entry; entry = entry->next) {
90         if(entry->whoid == whoid)
91             break;
92     }
93     if(!entry) {
94         DESYNCHRONIZE(whohandler_sync);
95         return NULL;
96     }
97     if(freeEntry) {
98         client->whoqueue_first = entry->next;
99         if(entry == client->whoqueue_last) {
100             client->whoqueue_last = NULL;
101         }
102     }
103     DESYNCHRONIZE(whohandler_sync);
104     return entry;
105 }
106
107 void clear_whoqueue(struct ClientSocket *client) {
108     if(!client->whoqueue_first) return;
109     SYNCHRONIZE(whohandler_sync);
110     struct WHOQueueEntry *entry, *next;
111     for(entry = client->whoqueue_first; entry; entry = next) {
112         next = entry->next;
113         free(entry);
114     }
115     client->whoqueue_last = NULL;
116     client->whoqueue_first = NULL;
117     DESYNCHRONIZE(whohandler_sync);
118 }
119
120 void get_userlist(struct ChanNode *chan, int module_id, userlist_callback_t callback, void *data) {
121     struct ClientSocket *bot;
122     for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
123         if(isUserOnChan(bot->user, chan))
124             break;
125     }
126     if(bot == NULL) return;
127     //check if we really need to who the channel
128     int do_who = (!(chan->flags & CHANFLAG_RECEIVED_USERLIST));
129     if(!do_who) {
130         struct ChanUser *chanuser;
131         for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) {
132             if(!(chanuser->user->flags & (USERFLAG_ISAUTHED | USERFLAG_ISIRCOP | USERFLAG_ISBOT)) && (time(0) - chanuser->user->last_who) > REWHO_TIMEOUT) {
133                 do_who = 1;
134                 break;
135             }
136         }
137     }
138     if(do_who) {
139         struct WHOQueueEntry* entry = addWHOQueueEntry(bot);
140         entry->type = WHOQUEUETYPE_ISONQUEUE | WHOQUEUETYPE_USERLIST;
141         entry->chan = chan;
142         entry->callback[0] = callback;
143         entry->module_id[0] = module_id;
144         int i;
145         for(i = 1; i < MAXCALLBACKS; i++)
146             entry->callback[i] = NULL;
147         entry->data[0] = data;
148         for(i = 1; i < MAXCALLBACKS; i++)
149             entry->data[i] = NULL;
150         putsock(bot, "WHO %s,%d %%tuihnaf,%d", chan->name, entry->whoid, entry->whoid);
151     } else
152         callback(bot, chan, data);
153 }
154
155 void _get_userlist_with_invisible(struct ChanNode *chan, int module_id, userlist_callback_t callback, void *data, int force) {
156     struct ClientSocket *bot;
157     for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
158         if(isUserOnChan(bot->user, chan))
159             break;
160     }
161     if(bot == NULL) return;
162     //check if we really need to who the channel
163     //invisible users can only be present if chanmode +D or +d is set!
164     int do_who = (!(chan->flags & CHANFLAG_RECEIVED_USERLIST))  || (isModeSet(chan->modes, 'd') || isModeSet(chan->modes, 'D'));
165     if(!do_who && force) {
166         struct ChanUser *chanuser;
167         for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) {
168             if(!(chanuser->user->flags & (USERFLAG_ISAUTHED | USERFLAG_ISIRCOP | USERFLAG_ISBOT)) && (time(0) - chanuser->user->last_who) > REWHO_TIMEOUT) {
169                 do_who = 1;
170                 break;
171             }
172         }
173     }
174     if(do_who) {
175         struct WHOQueueEntry* entry = addWHOQueueEntry(bot);
176         entry->type = WHOQUEUETYPE_ISONQUEUE | WHOQUEUETYPE_USERLIST;
177         entry->chan = chan;
178         entry->callback[0] = callback;
179         entry->module_id[0] = module_id;
180         int i;
181         for(i = 1; i < MAXCALLBACKS; i++)
182             entry->callback[i] = NULL;
183         entry->data[0] = data;
184         for(i = 1; i < MAXCALLBACKS; i++)
185             entry->data[i] = NULL;
186         putsock(bot, "WHO %s,%d d%%tuihnaf,%d", chan->name, entry->whoid, entry->whoid);
187     } else
188         callback(bot, chan, data);
189 }
190
191 void get_userauth(struct UserNode *user, int module_id, userauth_callback_t callback, void *data) {
192     //check if we have already an active WHO for this user
193     struct ClientSocket *bot, *whobot = NULL;
194     struct WHOQueueEntry *entry;
195     for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
196         for(entry = bot->whoqueue_first; entry; entry = entry->next) {
197             if((entry->type & WHOQUEUETYPE_USERAUTH) && entry->user == user) {
198                 int i = 0;
199                 for(i = 1; i < MAXCALLBACKS; i++) {
200                     if(!entry->callback[i]) {
201                         entry->callback[i] = callback;
202                         entry->module_id[i] = module_id;
203                         entry->data[i] = data;
204                         return;
205                     }
206                 }
207             }
208         }
209         if(bot->flags & SOCKET_FLAG_PREFERRED)
210             whobot = bot;
211     }
212     bot = whobot;
213     if(bot == NULL) bot = getBots(SOCKET_FLAG_READY, NULL);
214     //check if we really need to who the user
215     if(!is_valid_nick(user->nick)) {
216         callback(bot, user->nick, NULL, data);
217         return;
218     }
219     if((user->flags & (USERFLAG_ISAUTHED | USERFLAG_ISSERVER)) || (time(0) - user->last_who) <= REWHO_TIMEOUT) {
220         callback(bot, user->nick, user, data);
221         return;
222     }
223     entry = addWHOQueueEntry(bot);
224     entry->type = WHOQUEUETYPE_ISONQUEUE | WHOQUEUETYPE_USERAUTH;
225     entry->user = user;
226     user->flags |= USERFLAG_IS_ON_WHO_QUEUE;
227     entry->callback[0] = callback;
228     entry->module_id[0] = module_id;
229     int i;
230     for(i = 1; i < MAXCALLBACKS; i++)
231         entry->callback[i] = NULL;
232     entry->data[0] = data;
233     for(i = 1; i < MAXCALLBACKS; i++)
234             entry->data[i] = NULL;
235     //WHO ".$user->getNick().",".$id." %tuhna,".$id
236     putsock(bot, "WHO %s,%d %%tuhna,%d", user->nick, entry->whoid, entry->whoid);
237 }
238
239 static void _recv_whohandler_354(struct ClientSocket *client, char **argv, unsigned int argc);
240 void recv_whohandler_354(struct ClientSocket *client, char **argv, unsigned int argc) {
241     _recv_whohandler_354(client, argv, argc);
242 }
243
244 static void _recv_whohandler_354(struct ClientSocket *client, char **argv, unsigned int argc) {
245     int i;
246     if(argc < 2) return;
247     int type = atoi(argv[1]);
248     if(!type) return;
249     struct WHOQueueEntry* entry = getNextWHOQueueEntry(client, type, 0);
250     if(entry == NULL) return;
251     #ifdef HAVE_THREADS
252     unsigned int tid = (unsigned int) pthread_self_tid();
253     while(!clientsocket_parseorder_top(tid)) {
254         usleep(1000); //1ms
255     }
256     #endif
257     if(entry->type & WHOQUEUETYPE_USERLIST) {
258         if(argc < 7) return;
259         //:OGN2.OnlineGamesNet.net 354 skynet 1 pk910 2001:41d0:2:1d3b::babe skynet H@ pk910
260         struct ChanNode *chan = entry->chan;
261         //add the user toe the channel if he isn't added, yet and update its user data
262         //parse flags
263         int userflags = 0;
264         int chanuserflags = 0;
265         for(i = 0; i < strlen(argv[6]); i++) {
266             switch (argv[6][i]) {
267                 case '@':
268                     chanuserflags |= CHANUSERFLAG_OPPED;
269                     break;
270                 case '%':
271                     chanuserflags |= CHANUSERFLAG_HALFOPPED;
272                     break;
273                 case '+':
274                     chanuserflags |= CHANUSERFLAG_VOICED;
275                     break;
276                 case '*':
277                     userflags |= USERFLAG_ISIRCOP;
278                     break;
279                 case '<':
280                     chanuserflags |= CHANUSERFLAG_INVISIBLE;
281                     break;
282                 default:
283                     break;
284             }
285         }
286         
287         struct UserNode *user = getUserByNick(argv[5]);
288         struct ChanUser *chanuser;
289         if((chanuserflags & CHANUSERFLAG_INVISIBLE) && (!user || (user && !isBot(user)))) {
290             user = createTempUser(argv[5]); //always add a temponary user to prevent cache problems when the user joins right now (while it's stored in our cache as being invisible)
291             user->flags |= USERFLAG_ISTMPUSER;
292             chan->flags |= CHANFLAG_HAVE_INVISIBLES;
293             chanuser = addInvisibleChanUser(chan, user);
294             chanuser->flags = (chanuser->flags & ~CHANUSERFLAG_OPPED_OR_VOICED) | chanuserflags;
295         } else {
296             if(user == NULL) {
297                 user = addUser(argv[5]);
298             }
299             if(!(chanuser = getChanUser(user, chan))) {
300                 chanuser = addChanUser(chan, user);
301             }
302             chanuser->flags = (chanuser->flags & ~(CHANUSERFLAG_OPPED_OR_VOICED | CHANUSERFLAG_INVISIBLE)) | chanuserflags;
303         }
304         user->flags = (user->flags & ~USERFLAG_ISIRCOP) | userflags;
305         user->last_who = time(0);
306         if(!*user->ident)
307             strcpy(user->ident, argv[2]);
308         if(!user->ip)
309             user->ip = createIPNode(argv[3]);
310         if(!*user->host)
311             strcpy(user->host, argv[4]);
312         if(!(user->flags & USERFLAG_ISAUTHED) && strcmp(argv[7], "0")) {
313             strcpy(user->auth, argv[7]);
314             user->flags |= USERFLAG_ISAUTHED;
315         }
316     } else if((entry->type & WHOQUEUETYPE_USERAUTH) && !(entry->type & WHOQUEUETYPE_FOUND)) {
317         //:OGN2.OnlineGamesNet.net 354 Skynet 1 pk910 2001:41d0:2:1d3b::babe Skynet pk910
318         entry->type |= WHOQUEUETYPE_FOUND;
319         entry->user->last_who = time(0);
320         if(strcmp(argv[5], "0") && !(entry->user->flags & USERFLAG_ISAUTHED)) {
321             strcpy(entry->user->auth, argv[5]);
322             entry->user->flags |= USERFLAG_ISAUTHED;
323         }
324         for(i = 0; i < MAXCALLBACKS; i++) {
325             userauth_callback_t *callback = entry->callback[i];
326             if(!callback) break;
327             if(!entry->module_id[i] || module_loaded(entry->module_id[i]))
328                 callback(client, entry->user->nick, entry->user, entry->data[i]);
329         }
330     }
331 }
332
333 static void _recv_whohandler_315(struct ClientSocket *client, char **argv, unsigned int argc);
334 void recv_whohandler_315(struct ClientSocket *client, char **argv, unsigned int argc) {
335     _recv_whohandler_315(client, argv, argc);
336 }
337
338 static void _recv_whohandler_315(struct ClientSocket *client, char **argv, unsigned int argc) {
339     if(argc < 2) return;
340     char *typestr = strstr(argv[1], ",");
341     if(!typestr) return;
342     typestr++;
343     int type = atoi(typestr);
344     struct WHOQueueEntry* entry = getNextWHOQueueEntry(client, type, 0);
345     if(entry == NULL) return;
346     #ifdef HAVE_THREADS
347     unsigned int tid = (unsigned int) pthread_self_tid();
348     while(!clientsocket_parseorder_top(tid)) {
349         usleep(1000); //1ms
350     }
351     #endif
352     getNextWHOQueueEntry(client, type, 1);
353     if(entry->type & WHOQUEUETYPE_USERLIST) {
354         //:OGN2.OnlineGamesNet.net 315 skynet #pk910,1 :End of /WHO list.
355         entry->chan->flags |= CHANFLAG_RECEIVED_USERLIST;
356         userlist_callback_t *callback;
357         int i;
358         for(i = 0; i < MAXCALLBACKS; i++) {
359             callback = entry->callback[i];
360             if(!callback) break;
361             if(!entry->module_id[i] || module_loaded(entry->module_id[i]))
362                 callback(client, entry->chan, entry->data[i]);
363         }
364         if(entry->chan->flags & CHANFLAG_HAVE_INVISIBLES) {
365             //remove all invisible users again
366             struct ChanUser *chanuser, *next;
367             for(chanuser = getChannelUsers(entry->chan, NULL); chanuser; chanuser = next) {
368                 next = getChannelUsers(entry->chan, chanuser);
369                 if((chanuser->flags & CHANUSERFLAG_INVISIBLE) && !isBot(chanuser->user)) {
370                     delChanUser(chanuser, 1);
371                 }
372             }
373         }
374     } else if(entry->type & WHOQUEUETYPE_USERAUTH) {
375         if(!(entry->type & WHOQUEUETYPE_FOUND)) {
376             userauth_callback_t *callback;
377             int i;
378             for(i = 0; i < MAXCALLBACKS; i++) {
379                 callback = entry->callback[i];
380                 if(!callback) break;
381                 if(!entry->module_id[i] || module_loaded(entry->module_id[i]))
382                     callback(client, entry->user->nick, NULL, entry->data[i]);
383             }
384         }
385         entry->user->flags &= ~USERFLAG_IS_ON_WHO_QUEUE;
386         if(entry->user->flags & USERFLAG_FREE_AFTER_WHO) {
387             delUser(entry->user, 1);
388         }
389     }
390     free(entry);
391 }
392
393 void free_whoqueue() {
394     
395 }