added config parser & incoming connection handler
[NextIRCd.git] / src / config.c
1 /* config.c - NextIRCd
2  * Copyright (C) 2012-2013  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 <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <ctype.h>
22 #include "config.h"
23 #include "tools.h"
24
25 static char *config_file = "ircd.conf";
26
27 struct GlobalConfig global_config;
28 static struct ConfigReloadCallback *config_reload_callbacks = NULL;
29
30 struct ConfigReloadCallback {
31         confreload_callback_t *callback;
32         struct ConfigReloadCallback *next;
33 };
34
35 #define CONFIG_READ_BUF 512
36
37 struct ConfigFile {
38         FILE *file;
39         int pending_bytes;
40         char *content_buffer;
41         int return_value;
42 };
43
44 #define ENTRYTYPE_BLOCK   1
45 #define ENTRYTYPE_STRING  2
46 #define ENTRYTYPE_INTEGER 3
47
48 struct ConfigEntry {
49         char *name;
50         int type;
51         union {
52                 struct ConfigEntry *elements;
53                 char *str_value;
54                 int int_value;
55         } data;
56         struct ConfigEntry *next;
57 };
58
59 static char *parse_config_recursive(struct ConfigEntry *centry, char *buffer, struct ConfigFile *configfile);
60 static void free_entry_rekursiv(struct ConfigEntry *centry);
61 static int parse_config_block(struct ConfigEntry *centry);
62
63 #define PARSER_FLAG_ESCAPED    0x01
64 #define PARSER_FLAG_BLOCK      0x02
65 #define PARSER_FLAG_STRING     0x04
66 #define PARSER_FLAG_EXPECT_END 0x08
67 #define PARSER_FLAG_NEXTCHAR   0x10
68 #define PARSER_FLAG_INTEGER    0x20
69 #define PARSER_FLAG_EOBN       0x40 /* End of Block name */
70 #define PARSER_FLAG_COMMAND    0x80
71 static char *parse_config_recursive(struct ConfigEntry *centry, char *buffer, struct ConfigFile *configfile) {
72         int flags = 0;
73         int type = (centry ? 0 : ENTRYTYPE_BLOCK);
74         char cbuf[1024];
75         int cbufpos = 0;
76         struct ConfigEntry *sub_entrys = NULL;
77         parse_config_recursive_process:
78         while(*buffer) {
79                 if(flags & PARSER_FLAG_NEXTCHAR) {
80                         buffer++;
81                         if(*buffer == '\0')
82                                 break;
83                 }
84                 flags |= PARSER_FLAG_NEXTCHAR;
85                 if(flags & PARSER_FLAG_EOBN) {
86                         flags &= ~(PARSER_FLAG_EOBN | PARSER_FLAG_NEXTCHAR);
87                         struct ConfigEntry *new_entry = malloc(sizeof(*new_entry));
88                         if (!new_entry) return buffer;
89                         new_entry->name = strdup(cbuf);
90                         buffer = parse_config_recursive(new_entry, buffer, 0);
91                         if(centry) {
92                                 if(sub_entrys)
93                                         new_entry->next = sub_entrys;
94                                 else
95                                         new_entry->next = NULL;
96                                 sub_entrys = new_entry;
97                                 centry->data.elements = sub_entrys;
98                         } else {
99                                 configfile->return_value |= parse_config_block(new_entry);
100                                 free_entry_rekursiv(new_entry);
101                         }
102                 }
103                 if(flags & PARSER_FLAG_ESCAPED) {
104                         cbuf[cbufpos++] = *buffer;
105                         flags &= ~PARSER_FLAG_ESCAPED;
106                         continue;
107                 }
108                 if(flags & PARSER_FLAG_EXPECT_END) {
109                         if(*buffer == ';') {
110                                 if(centry)
111                                         centry->type = type;
112                                 return (buffer+1);
113                         }
114                         continue;
115                 }
116                 if(flags & PARSER_FLAG_STRING) {
117                         if(*buffer == '"') {
118                                 cbuf[cbufpos] = '\0';
119                                 flags &= ~PARSER_FLAG_STRING;
120                                 if(type == ENTRYTYPE_STRING) {
121                                         flags |= PARSER_FLAG_EXPECT_END;
122                                         if(centry)
123                                                 centry->data.str_value = strdup(cbuf);
124                                 } else if(type == ENTRYTYPE_BLOCK) {
125                                         flags |= PARSER_FLAG_EOBN;
126                                 }
127                         } else if(*buffer == '\\')
128                                 flags |= PARSER_FLAG_ESCAPED;
129                         else
130                                 cbuf[cbufpos++] = *buffer;
131                         continue;
132                 }
133                 if(flags & PARSER_FLAG_INTEGER) {
134                         if(!isdigit(*buffer)) {
135                                 cbuf[cbufpos] = '\0';
136                                 flags &= ~PARSER_FLAG_INTEGER;
137                                 if(type == ENTRYTYPE_INTEGER) {
138                                         flags |= PARSER_FLAG_EXPECT_END;
139                                         /* a pointer should be big enough to store a int value in it ;) */
140                                         if(centry)
141                                                 centry->data.int_value = atoi(cbuf);
142                                 }
143                                 if(*buffer == ';') {
144                                         if(centry)
145                                                 centry->type = type;
146                                         return (buffer+1);
147                                 }
148                         } else
149                                 cbuf[cbufpos++] = *buffer;
150                         continue;
151                 }
152                 if(flags & PARSER_FLAG_COMMAND) {
153                         int found_command = 0;
154                         char *tmp_buffer;
155                         switch(*buffer) {
156                                 case '/':
157                                         tmp_buffer = buffer;
158                                         buffer = strchr(buffer, '\r');
159                                         if(!buffer)
160                                                 buffer = strchr(tmp_buffer, '\n');
161                                         if(!buffer)
162                                                 buffer = tmp_buffer + strlen(tmp_buffer)-1;
163                                         found_command = 1;
164                                         break;
165                                 case '*':
166                                         //simple search for the next */
167                                         buffer = strstr(buffer, "*/")+1;
168                                         found_command = 1;
169                         }
170                         flags &= ~PARSER_FLAG_COMMAND;
171                         if(found_command)
172                                 continue;
173                 }
174                 switch(*buffer) {
175                         case '\\':
176                                 flags |= PARSER_FLAG_ESCAPED;
177                                 break;
178                         case '/':
179                                 if(!(flags & PARSER_FLAG_STRING)) {
180                                         flags |= PARSER_FLAG_COMMAND;
181                                 }
182                                 break;
183                         case '{':
184                                 flags |= PARSER_FLAG_BLOCK;
185                                 type = ENTRYTYPE_BLOCK;
186                                 break;
187                         case '}':
188                                 if(flags & PARSER_FLAG_BLOCK)
189                                         flags &= ~PARSER_FLAG_BLOCK;
190                                 flags |= PARSER_FLAG_EXPECT_END;
191                                 break;
192                         case '"':
193                                 flags |= PARSER_FLAG_STRING;
194                                 if(!type)
195                                         type = ENTRYTYPE_STRING;
196                                 cbufpos = 0;
197                                 break;
198                         case ';':
199                                 if(centry)
200                                         centry->type = type;
201                                 return (buffer+1);
202                         case '0':
203                         case '1':
204                         case '2':
205                         case '3':
206                         case '4':
207                         case '5':
208                         case '6':
209                         case '7':
210                         case '8':
211                         case '9':
212                                 if(!type)
213                                         type = ENTRYTYPE_INTEGER;
214                                 flags |= PARSER_FLAG_INTEGER;
215                                 cbufpos = 0;
216                                 cbuf[cbufpos++] = *buffer;
217                                 break;
218                         default:
219                                 break;
220                 }
221         }
222         /* try to get more data */
223         if(configfile->pending_bytes) {
224                 int read_bytes = configfile->pending_bytes;
225                 if(read_bytes > CONFIG_READ_BUF)
226                         read_bytes = CONFIG_READ_BUF;
227                 read_bytes = fread(configfile->content_buffer, 1, read_bytes, configfile->file);
228                 configfile->content_buffer[read_bytes] = '\0';
229                 configfile->pending_bytes -= read_bytes;
230                 buffer = configfile->content_buffer;
231                 goto parse_config_recursive_process;
232         }
233         if(centry)
234                 centry->type = type;
235         return buffer; //end of the buffer
236 }
237
238 static void free_entry_rekursiv(struct ConfigEntry *centry) {
239         if(centry->type == ENTRYTYPE_BLOCK) {
240                 struct ConfigEntry *subentry, *nextentry;
241                 for(subentry = centry->data.elements; subentry; subentry = nextentry) {
242                         nextentry = subentry->next;
243                         free_entry_rekursiv(subentry);
244                 }
245         } else if(centry->type == ENTRYTYPE_STRING && centry->data.str_value) {
246                 free(centry->data.str_value);
247         }
248         free(centry->name);
249         free(centry);
250 }
251
252 static int parse_config_port_block(struct ConfigEntry *centry);
253
254 static int parse_config_block(struct ConfigEntry *centry) {
255         int ret = 0;
256         if(!stricmp(centry->name, "Port"))
257                 ret |= parse_config_port_block(centry);
258         else {
259                 // ERROR - unknown block name
260         }
261         return ret;
262 }
263
264 static int parse_config_port_block(struct ConfigEntry *centry) {
265         struct ConfigPortObject *port;
266         struct ConfigEntry *sentry;
267         int ret = 0;
268         if(centry->type != ENTRYTYPE_BLOCK)
269                 return 1;
270         port = calloc(1, sizeof(*port));
271         for(sentry = centry->data.elements; sentry; sentry = sentry->next) {
272                 if(!stricmp(sentry->name, "port") && sentry->type == ENTRYTYPE_INTEGER)
273                         port->port = sentry->data.int_value;
274                 else if(!stricmp(sentry->name, "secure") && sentry->type == ENTRYTYPE_INTEGER)
275                         port->secure = (sentry->data.int_value != 0);
276                 else if(!stricmp(sentry->name, "server") && sentry->type == ENTRYTYPE_INTEGER)
277                         port->server = (sentry->data.int_value != 0);
278                 else if(!stricmp(sentry->name, "ip4only") && sentry->type == ENTRYTYPE_INTEGER)
279                         port->ip4only = (sentry->data.int_value != 0);
280                 else if(!stricmp(sentry->name, "ip6only") && sentry->type == ENTRYTYPE_INTEGER)
281                         port->ip6only = (sentry->data.int_value != 0);
282                 else if(!stricmp(sentry->name, "bind") && sentry->type == ENTRYTYPE_STRING) {
283                         port->bind_addr = sentry->data.str_value;
284                         sentry->data.str_value = NULL; //prevents freeing later
285                 } else {
286                         // ERROR - unknown field in port block
287                         ret = 1;
288                 }
289         }
290         if(port->port && !(port->ip4only && port->ip6only)) {
291                 port->next = global_config.ports;
292                 global_config.ports = port;
293         } else
294                 ret = 1;
295         return ret;
296 }
297
298
299
300
301 void init_config(char *configfile) {
302         if(configfile)
303                 config_file = configfile;
304         memset(&global_config, 0, sizeof(global_config));
305         reload_config();
306 }
307
308 void reload_config() {
309         struct ConfigFile config;
310         config.file = fopen(config_file, "rb");
311         if(!config.file) {
312                 //ERROR
313         printf("Error loading config file.\n");
314                 return;
315         }
316         fseek(config.file, 0, SEEK_END);
317         config.pending_bytes = ftell(config.file);
318         rewind(config.file);
319         
320         char content_buffer[CONFIG_READ_BUF+1];
321         config.content_buffer = content_buffer;
322         
323         int read_bytes = config.pending_bytes;
324         if(read_bytes > CONFIG_READ_BUF)
325                 read_bytes = CONFIG_READ_BUF;
326         read_bytes = fread(config.content_buffer, 1, read_bytes, config.file);
327         config.content_buffer[read_bytes] = '\0';
328         config.pending_bytes -= read_bytes;
329         
330         config.return_value = 0;
331         parse_config_recursive(NULL, config.content_buffer, &config);
332         
333         fclose(config.file);
334         
335         if(!config.return_value) {
336                 //call all the callbacks
337         } else {
338                 printf("Error loading config file (%d).\n", config.return_value);
339         }
340 }
341
342 void reload_config_callback(confreload_callback_t *callback) {
343         struct ConfigReloadCallback *reload_callback = malloc(sizeof(*reload_callback));
344         reload_callback->callback = callback;
345         reload_callback->next = config_reload_callbacks;
346         config_reload_callbacks = reload_callback;
347 }
348