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