added QServer for external cache access
[NeonServV5.git] / src / QServer.c
1 /* QServer.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 "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
28 #define QSERVER_TIMEOUT 30
29 #define QSERVER_MAXCLIENTS 100
30
31 #define QSERVER_FLAG_DISCONNECT 0x01
32 #define QSERVER_FLAG_AUTHED     0x02
33 #define QSERVER_FLAG_IN_USE     0x04
34
35 struct QServerClient {
36     int sock;
37     unsigned int flags;
38     time_t lastmsg;
39     char buffer[MAXLEN];
40     int bufferpos;
41     int references;
42     struct QServerClient *next;
43 };
44
45 static int server_sockfd = 0;
46 struct QServerClient *qserver_clients = NULL;
47 static int qserver_clientcount = 0;
48
49 void qserver_init() {
50     if(get_int_field("QServer.enabled")) {
51         server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
52         if (server_sockfd < 0) 
53             return;
54         struct sockaddr_in serv_addr;
55         bzero((char *) &serv_addr, sizeof(serv_addr));
56         int portno = get_int_field("QServer.port");
57         if(!portno)
58             portno = 7499;
59         serv_addr.sin_family = AF_INET;
60         serv_addr.sin_addr.s_addr = INADDR_ANY;
61         serv_addr.sin_port = htons(portno);
62         if (bind(server_sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) 
63             return;
64         listen(server_sockfd,5);
65     }
66 }
67
68 static int qserver_write(struct QServerClient *client, char* msg, int len) {
69     if (!(client && !(client->flags & QSERVER_FLAG_DISCONNECT))) return 0;
70     if(!len)
71         len = strlen(msg);
72     #ifdef WIN32
73     send(client->sock, msg, len, 0);
74     #else
75     write(client->sock, msg, len);
76     #endif
77     return 1;
78 }
79
80 static void qserver_put(struct QServerClient *client, const char *text, ...) {
81     va_list arg_list;
82     char sendBuf[MAXLEN];
83     int pos;
84     if (!(client && !(client->flags & QSERVER_FLAG_DISCONNECT))) return;
85     sendBuf[0] = '\0';
86     va_start(arg_list, text);
87     pos = vsnprintf(sendBuf, MAXLEN - 2, text, arg_list);
88     va_end(arg_list);
89     if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2;
90     sendBuf[pos] = '\n';
91     sendBuf[pos+1] = '\0';
92     qserver_write(client, sendBuf, pos+1);
93 }
94
95 static void qserver_parse_A(struct QServerClient *client, char **argv, int argc) {
96     if(client->flags & QSERVER_FLAG_AUTHED) {
97         qserver_put(client, "E :Already Authed");
98         return;
99     }
100     if(!argv) {
101         qserver_put(client, "E :Missing Parameter");
102         return;
103     }
104     if(strcmp(argv[0], get_string_field("QServer.pass"))) {
105         qserver_put(client, "E :Wrong Password");
106         return;
107     }
108     client->flags |= QSERVER_FLAG_AUTHED;
109     client->lastmsg = time(0);
110     qserver_put(client, "A :Logged in");
111 }
112
113 #define QSERVER_COMMAND_HEADER {\
114     if(!(client->flags & QSERVER_FLAG_AUTHED)) {\
115         qserver_put(client, "E :Not Authed");\
116         return;\
117     }\
118     client->lastmsg = time(0);\
119 }
120
121 static void qserver_parse_U(struct QServerClient *client, char **argv, int argc);
122 static void qserver_parse_C(struct QServerClient *client, char **argv, int argc);
123 static void qserver_parse_AC(struct QServerClient *client, char **argv, int argc);
124 static void qserver_parse_ACU(struct QServerClient *client, char **argv, int argc);
125 static void qserver_parse_R(struct QServerClient *client, char **argv, int argc);
126
127 static void qserver_parse(struct QServerClient *client, char *line, int len) {
128     int argc = 0;
129     char *argv[MAXNUMPARAMS];
130     while(*line) {
131         //skip leading spaces
132         while (*line == ' ')
133             *line++ = 0;
134         if (*line == ':') {
135            //the rest is a single parameter
136            argv[argc++] = line + 1;
137            break;
138         }
139         argv[argc++] = line;
140         if (argc >= MAXNUMPARAMS)
141             break;
142         while (*line != ' ' && *line)
143             line++;
144     }
145     if(!stricmp(argv[0], "A")) //AUTH
146         qserver_parse_A(client, argv+1, argc-1);
147     else if(!stricmp(argv[0], "U")) //get User
148         qserver_parse_U(client, argv+1, argc-1);
149     else if(!stricmp(argv[0], "C")) //get Channel
150         qserver_parse_C(client, argv+1, argc-1);
151     else if(!stricmp(argv[0], "AC")) //get All Channels
152         qserver_parse_AC(client, argv+1, argc-1);
153     else if(!stricmp(argv[0], "ACU")) //get All ChannelUsers
154         qserver_parse_ACU(client, argv+1, argc-1);
155     else if(!stricmp(argv[0], "R")) //RAW
156         qserver_parse_R(client, argv+1, argc-1);
157     else
158         qserver_put(client, "E :Unknown Command");
159 }
160
161 void qserver_loop() {
162     struct timeval tv;
163     struct QServerClient *client, *next, *prev = NULL;
164     int ret;
165     time_t now = time(0);
166     fd_set fds;
167     tv.tv_sec = 0;
168     tv.tv_usec = 0;
169     FD_ZERO(&fds);
170     ret = server_sockfd;
171     FD_SET(server_sockfd, &fds);
172     for (client = qserver_clients; client; client = next) {
173         next = client->next;
174         if((client->flags & (QSERVER_FLAG_DISCONNECT | QSERVER_FLAG_IN_USE)) == QSERVER_FLAG_DISCONNECT) {
175             close(client->sock);
176             if(prev) 
177                 prev->next = client->next;
178             else
179                 qserver_clients = client->next;
180             qserver_clientcount--;
181             free(client);
182             continue;
183         }
184         prev = client;
185         if(client->flags & QSERVER_FLAG_DISCONNECT) continue;
186         if(now - client->lastmsg > QSERVER_TIMEOUT) {
187             qserver_put(client, "E :Timeout");
188             client->flags |= QSERVER_FLAG_DISCONNECT;
189             continue;
190         }
191         FD_SET(client->sock, &fds);
192         if(client->sock > ret)
193             ret = client->sock;
194     }
195     ret = select(ret + 1, &fds, NULL, NULL, &tv);
196     if(ret == 0) {
197         return;
198     }
199     if(FD_ISSET(server_sockfd, &fds)) {
200         //new connection
201         struct sockaddr_in cli_addr;
202         socklen_t clilen;
203         if(qserver_clientcount >= QSERVER_MAXCLIENTS) {
204             qserver_put(client, "E :Maximum QServer Connections reached");
205             close(client->sock);
206         } else {
207             client = malloc(sizeof(*client));
208             clilen = sizeof(cli_addr);
209             client->sock = accept(server_sockfd, (struct sockaddr *) &cli_addr, &clilen);
210             client->flags = 0;
211             client->lastmsg = now;
212             client->bufferpos = 0;
213             client->references = 0;
214             client->next = qserver_clients;
215             qserver_clients = client;
216             qserver_clientcount++;
217         }
218     }
219     int bytes, i;
220     char buffer[MAXLEN];
221     for (client = qserver_clients; client; client = next) {
222         next = client->next;
223         if(FD_ISSET(client->sock, &fds)) {
224             #ifdef WIN32
225             bytes = recv(client->sock, buffer, sizeof(buffer), 0);
226             #else
227             bytes = read(client->sock, buffer, sizeof(buffer));
228             #endif
229             if(bytes <= 0) {
230                 client->flags |= QSERVER_FLAG_DISCONNECT;
231                 continue;
232             }
233             for(i = 0; i < bytes; i++) {
234                 if(client->bufferpos == MAXLEN-1) {
235                     //buffer overflow
236                     qserver_put(client, "E :Buffer Overflow");
237                     client->flags |= QSERVER_FLAG_DISCONNECT;
238                     break;
239                 }
240                 if(buffer[i] == '\r') continue;
241                 else if(buffer[i] == '\n') {
242                     client->buffer[client->bufferpos] = '\0';
243                     qserver_parse(client, client->buffer, client->bufferpos);
244                     client->bufferpos = 0;
245                 } else {
246                     client->buffer[client->bufferpos++] = buffer[i];
247                 }
248             }
249         }
250     }
251 }
252
253 void qserver_free() {
254     struct QServerClient *client, *next;
255     for (client = qserver_clients; client; client = next) {
256         next = client->next;
257         close(client->sock);
258         free(client);
259     }
260     qserver_clients = NULL;
261     qserver_clientcount = 0;
262     close(server_sockfd);
263 }
264
265 /* 
266 * Command functions
267 */
268
269 static USERAUTH_CALLBACK(qserver_parse_U_async);
270 static void qserver_parse_U(struct QServerClient *client, char **argv, int argc) {
271     QSERVER_COMMAND_HEADER;
272     if(!argv) {
273         qserver_put(client, "E :Missing Parameter");
274         return;
275     }
276     struct UserNode *cuser = getUserByNick(argv[0]);
277     if(!cuser) {
278         cuser = createTempUser(argv[0]);
279         if(!cuser) {
280             qserver_put(client, "U 0 :Unknown User");
281             return;
282         }
283         cuser->flags |= USERFLAG_ISTMPUSER;
284     }
285     client->references++;
286     client->flags |= QSERVER_FLAG_IN_USE;
287     get_userauth(cuser, qserver_parse_U_async, client);
288 }
289
290 static USERAUTH_CALLBACK(qserver_parse_U_async) {
291     struct QServerClient *qclient = data;
292     qclient->references--;
293     if(!qclient->references)
294         qclient->flags &= ~QSERVER_FLAG_IN_USE;
295     if(!user) {
296         qserver_put(qclient, "U 0 :Unknown User");
297         return;
298     }
299     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);
300 }
301
302 static void qserver_parse_C(struct QServerClient *client, char **argv, int argc) {
303     QSERVER_COMMAND_HEADER;
304     if(!argv) {
305         qserver_put(client, "E :Missing Parameter");
306         return;
307     }
308     struct ChanNode *chan = getChanByName(argv[0]);
309     if(!chan) {
310         qserver_put(client, "C 0 :Unknown Channel");
311         return;
312     }
313     char tmpStr[MAXLEN];
314     getModeString(chan->modes, tmpStr);
315     qserver_put(client, "C 1 %s %d %s :%s", chan->name, chan->usercount, tmpStr, chan->topic);
316 }
317
318 static void qserver_parse_AC(struct QServerClient *client, char **argv, int argc) {
319     QSERVER_COMMAND_HEADER;
320     struct ChanNode *chan;
321     char tmpStr[MAXLEN];
322     for(chan = getAllChans(NULL); chan; chan = getAllChans(chan)) {
323         getModeString(chan->modes, tmpStr);
324         qserver_put(client, "AC %s %d %s :%s", chan->name, chan->usercount, tmpStr, chan->topic);
325     }
326     qserver_put(client, "ACE"); //AllChannelsEnd
327 }
328
329 static USERLIST_CALLBACK(qserver_parse_ACU_async);
330 static void qserver_parse_ACU(struct QServerClient *client, char **argv, int argc) {
331     QSERVER_COMMAND_HEADER;
332     if(!argv) {
333         qserver_put(client, "E :Missing Parameter");
334         return;
335     }
336     struct ChanNode *chan = getChanByName(argv[0]);
337     if(!chan) {
338         qserver_put(client, "ACUE 0 :Unknown Channel");
339         return;
340     }
341     if(argc > 1 && !stricmp(argv[1], "1")) {
342         client->references++;
343         client->flags |= QSERVER_FLAG_IN_USE;
344         get_userlist_if_invisible(chan, qserver_parse_ACU_async, client);
345         return;
346     }
347     char tmpStr[6];
348     int tmpStrPos;
349     struct ChanUser *chanuser;
350     for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) {
351         tmpStrPos = 0;
352         if(chanuser->flags & CHANUSERFLAG_OPPED)
353             tmpStr[tmpStrPos++] = '@';
354         if(chanuser->flags & CHANUSERFLAG_HALFOPPED)
355             tmpStr[tmpStrPos++] = '%';
356         if(chanuser->flags & CHANUSERFLAG_VOICED)
357             tmpStr[tmpStrPos++] = '+';
358         if(chanuser->flags & CHANUSERFLAG_INVISIBLE)
359             tmpStr[tmpStrPos++] = '<';
360         tmpStr[tmpStrPos] = '\0';
361         qserver_put(client, "ACU %s %s %s", chanuser->user->nick, ((chanuser->user->flags & USERFLAG_ISAUTHED) ? chanuser->user->auth : "0"), tmpStr);
362     }
363     qserver_put(client, "ACUE 1");
364 }
365
366 static USERLIST_CALLBACK(qserver_parse_ACU_async) {
367     struct QServerClient *qclient = data;
368     qclient->references--;
369     if(!qclient->references)
370         qclient->flags &= ~QSERVER_FLAG_IN_USE;
371     char tmpStr[6];
372     int tmpStrPos;
373     struct ChanUser *chanuser;
374     for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) {
375         tmpStrPos = 0;
376         if(chanuser->flags & CHANUSERFLAG_OPPED)
377             tmpStr[tmpStrPos++] = '@';
378         if(chanuser->flags & CHANUSERFLAG_HALFOPPED)
379             tmpStr[tmpStrPos++] = '%';
380         if(chanuser->flags & CHANUSERFLAG_VOICED)
381             tmpStr[tmpStrPos++] = '+';
382         if(chanuser->flags & CHANUSERFLAG_INVISIBLE)
383             tmpStr[tmpStrPos++] = '<';
384         tmpStr[tmpStrPos] = '\0';
385         qserver_put(qclient, "ACU %s %s %s", chanuser->user->nick, ((chanuser->user->flags & USERFLAG_ISAUTHED) ? chanuser->user->auth : "0"), tmpStr);
386     }
387     qserver_put(qclient, "ACUE 1");
388 }
389
390 static void qserver_parse_R(struct QServerClient *client, char **argv, int argc) {
391     QSERVER_COMMAND_HEADER;
392     if(argc < 3) {
393         qserver_put(client, "E :Missing Parameter");
394         return;
395     }
396     struct ClientSocket *bot;
397     if(!strcmp(argv[0], "1")) {
398         for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
399             if(!stricmp(bot->user->nick, argv[1])) break;
400         }
401     } else {
402         struct ClientSocket *low_bot;
403         int botid = resolve_botalias(argv[1]);
404         if(botid == -1)
405             botid = atoi(argv[1]);
406         for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
407             if(bot->botid == botid) {
408                 if(bot->flags & SOCKET_FLAG_PREFERRED) break;
409                 low_bot = bot;
410             }
411         }
412         if(!bot)
413             bot = low_bot;
414     }
415     if(!bot) {
416         qserver_put(client, "R 0 :Bot not found");
417         return;
418     }
419     putsock(bot, "%s", argv[2]);
420     qserver_put(client, "R 1");
421 }