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