modified code to use IOHandler functions instead of own ones
[NeonServV5.git] / src / QServer.c
1 /* QServer.c - NeonServ v5.5
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 "QServer.h"
19 #include "UserNode.h"
20 #include "ChanNode.h"
21 #include "ModeNode.h"
22 #include "ChanUser.h"
23 #include "ClientSocket.h"
24 #include "WHOHandler.h"
25 #include "ConfigParser.h"
26 #include "bots.h"
27 #include "IOHandler.h"
28
29 #ifdef WIN32
30 typedef uint32_t socklen_t;
31 #endif
32
33 #define QSERVER_TIMEOUT 30
34 #define QSERVER_MAXCLIENTS 100
35
36 #define QSERVER_FLAG_AUTHED     0x01
37 #define QSERVER_FLAG_IN_USE     0x02
38
39 struct QServerClient {
40     struct IODescriptor *iofd;
41     unsigned int flags;
42     int references;
43     struct QServerClient *next;
44 };
45
46 static struct IODescriptor *server_iofd = NULL;
47 struct QServerClient *qserver_clients = NULL;
48 static int qserver_clientcount = 0;
49
50 static IOHANDLER_CALLBACK(qserver_callback);
51
52 void qserver_init() {
53     if(get_int_field("QServer.enabled")) {
54         char *host = get_string_field("QServer.host");
55         if(!host)
56             host = "0.0.0.0";
57         int portno = get_int_field("QServer.port");
58         if(!portno)
59             portno = 7499;
60         server_iofd = iohandler_listen(host, port, qserver_callback);
61     }
62 }
63
64 void qserver_free() {
65     struct QServerClient *client, *next;
66     for (client = qserver_clients; client; client = next) {
67         next = client->next;
68         if(client->iofd)
69             iohandler_close(client->iofd);
70         free(client);
71     }
72     qserver_clients = NULL;
73     qserver_clientcount = 0;
74     iohandler_close(server_iofd);
75     server_iofd = NULL;
76 }
77
78 static int qserver_write(struct QServerClient *client, char* msg, int len) {
79     if (!client || !client->iofd) return 0;
80     if(!len)
81         len = strlen(msg);
82         iohandler_send(client->iofd, msg, len);
83     return 1;
84 }
85
86 static void qserver_put(struct QServerClient *client, const char *text, ...) {
87     va_list arg_list;
88     char sendBuf[MAXLEN];
89     int pos;
90     if (!(client && !(client->flags & QSERVER_FLAG_DISCONNECT))) return;
91     sendBuf[0] = '\0';
92     va_start(arg_list, text);
93     pos = vsnprintf(sendBuf, MAXLEN - 2, text, arg_list);
94     va_end(arg_list);
95     if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2;
96     sendBuf[pos] = '\n';
97     sendBuf[pos+1] = '\0';
98     qserver_write(client, sendBuf, pos+1);
99 }
100
101 static void qserver_update_lastmsg(struct QServerClient *client) {
102     struct timeval timeout;
103     gettimeofday(&timeout, NULL);
104     timeout.tv_sec += QSERVER_TIMEOUT;
105     iohandler_set_timeout(client->iofd, &timeout);
106 }
107
108 static void qserver_parse_A(struct QServerClient *client, char **argv, int argc) {
109     if(client->flags & QSERVER_FLAG_AUTHED) {
110         qserver_put(client, "E :Already Authed");
111         return;
112     }
113     if(!argv) {
114         qserver_put(client, "E :Missing Parameter");
115         return;
116     }
117     if(strcmp(argv[0], get_string_field("QServer.pass"))) {
118         qserver_put(client, "E :Wrong Password");
119         return;
120     }
121     client->flags |= QSERVER_FLAG_AUTHED;
122     qserver_update_lastmsg(client);
123     qserver_put(client, "A :Logged in");
124 }
125
126 #define QSERVER_COMMAND_HEADER {\
127     if(!(client->flags & QSERVER_FLAG_AUTHED)) {\
128         qserver_put(client, "E :Not Authed");\
129         return;\
130     }\
131     qserver_update_lastmsg(client);\
132 }
133
134 static void qserver_parse_U(struct QServerClient *client, char **argv, int argc);
135 static void qserver_parse_C(struct QServerClient *client, char **argv, int argc);
136 static void qserver_parse_AC(struct QServerClient *client, char **argv, int argc);
137 static void qserver_parse_ACU(struct QServerClient *client, char **argv, int argc);
138 static void qserver_parse_R(struct QServerClient *client, char **argv, int argc);
139
140 static void qserver_parse(struct QServerClient *client, char *line) {
141     int argc = 0;
142     char *argv[MAXNUMPARAMS];
143     while(*line) {
144         //skip leading spaces
145         while (*line == ' ')
146             *line++ = 0;
147         if (*line == ':') {
148            //the rest is a single parameter
149            argv[argc++] = line + 1;
150            break;
151         }
152         argv[argc++] = line;
153         if (argc >= MAXNUMPARAMS)
154             break;
155         while (*line != ' ' && *line)
156             line++;
157     }
158     if(!stricmp(argv[0], "A")) //AUTH
159         qserver_parse_A(client, argv+1, argc-1);
160     else if(!stricmp(argv[0], "U")) //get User
161         qserver_parse_U(client, argv+1, argc-1);
162     else if(!stricmp(argv[0], "C")) //get Channel
163         qserver_parse_C(client, argv+1, argc-1);
164     else if(!stricmp(argv[0], "AC")) //get All Channels
165         qserver_parse_AC(client, argv+1, argc-1);
166     else if(!stricmp(argv[0], "ACU")) //get All ChannelUsers
167         qserver_parse_ACU(client, argv+1, argc-1);
168     else if(!stricmp(argv[0], "R")) //RAW
169         qserver_parse_R(client, argv+1, argc-1);
170     else
171         qserver_put(client, "E :Unknown Command");
172 }
173
174 static void qserver_accept(int sockfd) {
175     struct IODescriptor *client_iofd = iohandler_add(sockfd, IOTYPE_CLIENT, NULL, qserver_callback);
176     client_iofd->state = IO_CONNECTED;
177     iohandler_update(client_iofd);
178     if(qserver_clientcount >= QSERVER_MAXCLIENTS) {
179         iohandler_printf(client_iofd, "E :Maximum QServer Connections reached");
180         iohandler_close(client_iofd);
181         return;
182     }
183     struct QServerClient *client = malloc(sizeof(*client));
184     client->iofd = client_iofd;
185     client->references = 0;
186     client->next = qserver_clients;
187     qserver_clients = client;
188     qserver_clientcount++;
189 }
190
191 static void qserver_cleanup() {
192     struct QServerClient *client, *next, *prev = NULL;
193     for (client = qserver_clients; client; client = next) {
194         next = client->next;
195         if(client->iofd == NULL && !(client->flags & QSERVER_FLAG_IN_USE)) {
196             if(prev) 
197                 prev->next = client->next;
198             else
199                 qserver_clients = client->next;
200             qserver_clientcount--;
201             free(client);
202         }
203     }
204 }
205
206 static IOHANDLER_CALLBACK(qserver_callback) {
207     struct QServerClient *client = event->iofd->data;
208     switch(event->type) {
209     case IOEVENT_TIMEOUT:
210         qserver_put(client, "E :Timeout");
211         client->iofd = NULL;
212         break;
213     case IOEVENT_RECV:
214         qserver_parse(client, event->data.recv_str);
215         break;
216     case IOEVENT_CLOSED:
217         iohandler_close(client->iofd);
218         client->iofd = NULL;
219         break;
220     case IOEVENT_ACCEPT:
221         
222         break;
223     default:
224         break;
225     }
226     qserver_cleanup();
227 }
228
229 /* 
230 * Command functions
231 */
232
233 static USERAUTH_CALLBACK(qserver_parse_U_async);
234 static void qserver_parse_U(struct QServerClient *client, char **argv, int argc) {
235     QSERVER_COMMAND_HEADER;
236     if(!argv) {
237         qserver_put(client, "E :Missing Parameter");
238         return;
239     }
240     struct UserNode *cuser = getUserByNick(argv[0]);
241     if(!cuser) {
242         cuser = createTempUser(argv[0]);
243         if(!cuser) {
244             qserver_put(client, "U 0 :Unknown User");
245             return;
246         }
247         cuser->flags |= USERFLAG_ISTMPUSER;
248     }
249     client->references++;
250     client->flags |= QSERVER_FLAG_IN_USE;
251     get_userauth(cuser, 0, qserver_parse_U_async, client);
252 }
253
254 static USERAUTH_CALLBACK(qserver_parse_U_async) {
255     struct QServerClient *qclient = data;
256     qclient->references--;
257     if(!qclient->references)
258         qclient->flags &= ~QSERVER_FLAG_IN_USE;
259     if(!user) {
260         qserver_put(qclient, "U 0 :Unknown User");
261         return;
262     }
263     qserver_put(qclient, "U 1 %s %s %s %s :%s", user->nick, user->ident, user->host, ((user->flags & USERFLAG_ISAUTHED) ? user->auth : "0"), user->realname);
264 }
265
266 static void qserver_parse_C(struct QServerClient *client, char **argv, int argc) {
267     QSERVER_COMMAND_HEADER;
268     if(!argv) {
269         qserver_put(client, "E :Missing Parameter");
270         return;
271     }
272     struct ChanNode *chan = getChanByName(argv[0]);
273     if(!chan) {
274         qserver_put(client, "C 0 :Unknown Channel");
275         return;
276     }
277     char tmpStr[MAXLEN];
278     getModeString(chan->modes, tmpStr);
279     qserver_put(client, "C 1 %s %d %s :%s", chan->name, chan->usercount, tmpStr, chan->topic);
280 }
281
282 static void qserver_parse_AC(struct QServerClient *client, char **argv, int argc) {
283     QSERVER_COMMAND_HEADER;
284     struct ChanNode *chan;
285     char tmpStr[MAXLEN];
286     for(chan = getAllChans(NULL); chan; chan = getAllChans(chan)) {
287         getModeString(chan->modes, tmpStr);
288         qserver_put(client, "AC %s %d %s :%s", chan->name, chan->usercount, tmpStr, chan->topic);
289     }
290     qserver_put(client, "ACE"); //AllChannelsEnd
291 }
292
293 static USERLIST_CALLBACK(qserver_parse_ACU_async);
294 static void qserver_parse_ACU(struct QServerClient *client, char **argv, int argc) {
295     QSERVER_COMMAND_HEADER;
296     if(!argv) {
297         qserver_put(client, "E :Missing Parameter");
298         return;
299     }
300     struct ChanNode *chan = getChanByName(argv[0]);
301     if(!chan) {
302         qserver_put(client, "ACUE 0 :Unknown Channel");
303         return;
304     }
305     if(argc > 1 && !stricmp(argv[1], "1")) {
306         client->references++;
307         client->flags |= QSERVER_FLAG_IN_USE;
308         get_userlist_if_invisible(chan, 0, qserver_parse_ACU_async, client);
309         return;
310     }
311     char tmpStr[6];
312     int tmpStrPos;
313     struct ChanUser *chanuser;
314     for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) {
315         tmpStrPos = 0;
316         if(chanuser->flags & CHANUSERFLAG_OPPED)
317             tmpStr[tmpStrPos++] = '@';
318         if(chanuser->flags & CHANUSERFLAG_HALFOPPED)
319             tmpStr[tmpStrPos++] = '%';
320         if(chanuser->flags & CHANUSERFLAG_VOICED)
321             tmpStr[tmpStrPos++] = '+';
322         if(chanuser->flags & CHANUSERFLAG_INVISIBLE)
323             tmpStr[tmpStrPos++] = '<';
324         tmpStr[tmpStrPos] = '\0';
325         qserver_put(client, "ACU %s %s %s", chanuser->user->nick, ((chanuser->user->flags & USERFLAG_ISAUTHED) ? chanuser->user->auth : "0"), tmpStr);
326     }
327     qserver_put(client, "ACUE 1");
328 }
329
330 static USERLIST_CALLBACK(qserver_parse_ACU_async) {
331     struct QServerClient *qclient = data;
332     qclient->references--;
333     if(!qclient->references)
334         qclient->flags &= ~QSERVER_FLAG_IN_USE;
335     char tmpStr[6];
336     int tmpStrPos;
337     struct ChanUser *chanuser;
338     for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) {
339         tmpStrPos = 0;
340         if(chanuser->flags & CHANUSERFLAG_OPPED)
341             tmpStr[tmpStrPos++] = '@';
342         if(chanuser->flags & CHANUSERFLAG_HALFOPPED)
343             tmpStr[tmpStrPos++] = '%';
344         if(chanuser->flags & CHANUSERFLAG_VOICED)
345             tmpStr[tmpStrPos++] = '+';
346         if(chanuser->flags & CHANUSERFLAG_INVISIBLE)
347             tmpStr[tmpStrPos++] = '<';
348         tmpStr[tmpStrPos] = '\0';
349         qserver_put(qclient, "ACU %s %s %s", chanuser->user->nick, ((chanuser->user->flags & USERFLAG_ISAUTHED) ? chanuser->user->auth : "0"), tmpStr);
350     }
351     qserver_put(qclient, "ACUE 1");
352 }
353
354 static void qserver_parse_R(struct QServerClient *client, char **argv, int argc) {
355     QSERVER_COMMAND_HEADER;
356     if(argc < 3) {
357         qserver_put(client, "E :Missing Parameter");
358         return;
359     }
360     struct ClientSocket *bot;
361     if(!strcmp(argv[0], "1")) {
362         for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
363             if(!stricmp(bot->user->nick, argv[1])) break;
364         }
365     } else {
366         struct ClientSocket *low_bot;
367         int botid = resolve_botalias(argv[1]);
368         if(botid == -1)
369             botid = atoi(argv[1]);
370         for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
371             if(bot->botid == botid) {
372                 if(bot->flags & SOCKET_FLAG_PREFERRED) break;
373                 low_bot = bot;
374             }
375         }
376         if(!bot)
377             bot = low_bot;
378     }
379     if(!bot) {
380         qserver_put(client, "R 0 :Bot not found");
381         return;
382     }
383     putsock(bot, "%s", argv[2]);
384     qserver_put(client, "R 1");
385 }