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