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