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