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