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