WIP: Fix the Proxy-State issue.
[libradsec.git] / gconfig.c
1 /* Copyright (c) 2007-2008, UNINETT AS */
2 /* See LICENSE for licensing information. */
3
4 #include <string.h>
5 #include <stdarg.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <limits.h>
9 #include <glob.h>
10 #include <sys/types.h>
11 #include <ctype.h>
12 #include <libgen.h>
13 #include <errno.h>
14 #include "debug.h"
15 #include "util.h"
16 #include "gconfig.h"
17
18 /* returns NULL on error, where to continue parsing if token and ok. E.g. "" will return token with empty string */
19 char *strtokenquote(char *s, char **token, char *del, char *quote, char *comment) {
20     char *t = s, *q, *r;
21
22     if (!t || !token || !del)
23         return NULL;
24     while (*t && strchr(del, *t))
25         t++;
26     if (!*t || (comment && strchr(comment, *t))) {
27         *token = NULL;
28         return t + 1; /* needs to be non-NULL, but value doesn't matter */
29     }
30     if (quote && (q = strchr(quote, *t))) {
31         t++;
32         r = t;
33         while (*t && *t != *q)
34             t++;
35         if (!*t || (t[1] && !strchr(del, t[1])))
36             return NULL;
37         *t = '\0';
38         *token = r;
39         return t + 1;
40     }
41     *token = t;
42     t++;
43     while (*t && !strchr(del, *t))
44         t++;
45     *t = '\0';
46     return t + 1;
47 }
48
49 int pushgconfdata(struct gconffile **cf, const char *data) {
50     int i;
51     struct gconffile *newcf;
52
53     if (!*cf) {
54         newcf = malloc(sizeof(struct gconffile) * 2);
55         if (!newcf)
56             return 0;
57         memset(newcf, 0, sizeof(struct gconffile) * 2);
58     } else {
59         for (i = 0; (*cf)[i].data || (*cf)[i].path; i++);
60         newcf = realloc(*cf, sizeof(struct gconffile) * (i + 2));
61         if (!newcf)
62             return 0;
63         memmove(newcf + 1, newcf, sizeof(struct gconffile) * (i + 1));
64         memset(newcf, 0, sizeof(struct gconffile));
65     }
66     newcf[0].data = data;
67     *cf = newcf;
68     return 1;
69 }
70
71 FILE *pushgconffile(struct gconffile **cf, FILE *file, const char *description) {
72     int i;
73     struct gconffile *newcf;
74     char *desc;
75
76     if (!file) {
77         debug(DBG_INFO, "could not read config from %s", description);
78         return NULL;
79     }
80     debug(DBG_DBG, "reading config from %s", description);
81
82     desc = stringcopy(description, 0);
83     if (!desc)
84         goto errmalloc;
85
86     if (!*cf) {
87         newcf = malloc(sizeof(struct gconffile) * 2);
88         if (!newcf)
89             goto errmalloc;
90         memset(newcf, 0, sizeof(struct gconffile) * 2);
91     } else {
92         for (i = 0; (*cf)[i].data || (*cf)[i].path; i++);
93         newcf = realloc(*cf, sizeof(struct gconffile) * (i + 2));
94         if (!newcf)
95             goto errmalloc;
96         memmove(newcf + 1, newcf, sizeof(struct gconffile) * (i + 1));
97         memset(newcf, 0, sizeof(struct gconffile));
98     }
99     newcf[0].file = file;
100     newcf[0].path = desc;
101     *cf = newcf;
102     return file;
103
104 errmalloc:
105     free(desc);
106     fclose(file);
107     debug(DBG_ERR, "malloc failed");
108     return NULL;
109 }
110
111 FILE *pushgconfpath(struct gconffile **cf, const char *path) {
112     FILE *f;
113
114     f = fopen(path, "r");
115     return pushgconffile(cf, f, path);
116 }
117
118 FILE *pushgconfpaths(struct gconffile **cf, const char *cfgpath) {
119     int i;
120     FILE *f = NULL;
121     glob_t globbuf;
122     char *path, *curfile = NULL, *dir;
123
124     /* if cfgpath is relative, make it relative to current config */
125     if (*cfgpath == '/')
126         path = (char *)cfgpath;
127     else {
128         /* dirname may modify its argument */
129         curfile = stringcopy((*cf)->path, 0);
130         if (!curfile) {
131             debug(DBG_ERR, "malloc failed");
132             goto exit;
133         }
134         dir = dirname(curfile);
135         path = malloc(strlen(dir) + strlen(cfgpath) + 2);
136         if (!path) {
137             debug(DBG_ERR, "malloc failed");
138             goto exit;
139         }
140         strcpy(path, dir);
141         path[strlen(dir)] = '/';
142         strcpy(path + strlen(dir) + 1, cfgpath);
143     }
144     memset(&globbuf, 0, sizeof(glob_t));
145     if (glob(path, 0, NULL, &globbuf)) {
146         debug(DBG_WARN, "could not glob %s", path);
147         goto exit;
148     }
149
150     for (i = globbuf.gl_pathc - 1; i >= 0; i--) {
151         f = pushgconfpath(cf, globbuf.gl_pathv[i]);
152         if (!f)
153             break;
154     }
155     globfree(&globbuf);
156
157 exit:
158     if (curfile) {
159         free(curfile);
160         free(path);
161     }
162     return f;
163 }
164
165 int popgconf(struct gconffile **cf) {
166     int i;
167
168     if (!*cf)
169         return 0;
170     for (i = 0; (*cf)[i].data || (*cf)[i].path; i++);
171     if (i && (*cf)[0].file) {
172         fclose((*cf)[0].file);
173         if ((*cf)[0].path) {
174             debug(DBG_DBG, "closing config file %s", (*cf)[0].path);
175             free((*cf)[0].path);
176         }
177     }
178     if (i < 2) {
179         free(*cf);
180         *cf = NULL;
181         return 0;
182     }
183     memmove(*cf, *cf + 1, sizeof(struct gconffile) * i);
184     return 1;
185 }
186
187 void freegconfmstr(char **mstr) {
188     int i;
189
190     if (mstr) {
191         for (i = 0; mstr[i]; i++)
192             free(mstr[i]);
193         free(mstr);
194     }
195 }
196
197 void freegconf(struct gconffile **cf) {
198     int i;
199
200     if (!*cf)
201         return;
202
203     for (i = 0; (*cf)[i].data || (*cf)[i].path; i++) {
204         if ((*cf)[i].file) {
205             fclose((*cf)[i].file);
206             if ((*cf)[i].path) {
207                 debug(DBG_DBG, "closing config file %s", (*cf)[i].path);
208                 free((*cf)[i].path);
209             }
210         }
211     }
212     free(*cf);
213     *cf = NULL;
214 }
215
216 struct gconffile *openconfigfile(const char *file) {
217     struct gconffile *cf = NULL;
218
219     if (!pushgconfpath(&cf, file)) {
220         debug(DBG_ERR, "could not read config file %s\n%s", file, strerror(errno));
221         return NULL;
222     }
223     debug(DBG_DBG, "reading config file %s", file);
224     return cf;
225 }
226
227 /* Parses config with following syntax:
228  * One of these:
229  * option-name value
230  * option-name = value
231  * Or:
232  * option-name value {
233  *     option-name [=] value
234  *     ...
235  * }
236  */
237
238 int getlinefromcf(struct gconffile *cf, char *line, const size_t size) {
239     size_t i, pos;
240
241     if (!cf)
242         return 0;
243
244     if (cf->file)
245         return fgets(line, size, cf->file) ? 1 : 0;
246     else if (cf->data) {
247         pos = cf->datapos;
248         if (!cf->data[pos])
249             return 0;
250         for (i = pos; cf->data[i] && cf->data[i] != '\n'; i++);
251         if (cf->data[i] == '\n')
252             i++;
253         if (i - pos > size - 1)
254             i = size - 1 + pos;
255         memcpy(line, cf->data + pos, i - pos);
256         line[i - pos] = '\0';
257         cf->datapos = i;
258         return 1;
259     }
260     return 0;
261 }
262
263 int getconfigline(struct gconffile **cf, char *block, char **opt, char **val, int *conftype) {
264     char line[1024];
265     char *tokens[3], *s;
266     int tcount;
267
268     *opt = NULL;
269     *val = NULL;
270     *conftype = 0;
271
272     if (!cf || !*cf || (!(*cf)->file && !(*cf)->data))
273         return 1;
274
275     for (;;) {
276         if (!getlinefromcf(*cf, line, 1024)) {
277             if (popgconf(cf))
278                 continue;
279             return 1;
280         }
281         s = line;
282         for (tcount = 0; tcount < 3; tcount++) {
283             s = strtokenquote(s, &tokens[tcount], " \t\r\n", "\"'", tcount ? NULL : "#");
284             if (!s) {
285                 debug(DBG_ERR, "Syntax error in line starting with: %s", line);
286                 return 0;
287             }
288             if (!tokens[tcount])
289                 break;
290         }
291         if (!tcount || **tokens == '#')
292             continue;
293
294         if (**tokens == '}') {
295             if (block)
296                 return 1;
297             debug(DBG_ERR, "configuration error, found } with no matching {");
298             return 0;
299         }
300         break;
301     }
302
303     switch (tcount) {
304     case 2:
305         *opt = stringcopy(tokens[0], 0);
306         if (!*opt)
307             goto errmalloc;
308         *val = stringcopy(tokens[1], 0);
309         if (!*val)
310             goto errmalloc;
311         *conftype = CONF_STR;
312         break;
313     case 3:
314         if (tokens[1][0] == '=' && tokens[1][1] == '\0') {
315             *opt = stringcopy(tokens[0], 0);
316             if (!*opt)
317                 goto errmalloc;
318             *val = stringcopy(tokens[2], 0);
319             if (!*val)
320                 goto errmalloc;
321             *conftype = CONF_STR;
322             break;
323         }
324         if (tokens[2][0] == '{' && tokens[2][1] == '\0') {
325             *opt = stringcopy(tokens[0], 0);
326             if (!*opt)
327                 goto errmalloc;
328             *val = stringcopy(tokens[1], 0);
329             if (!*val)
330                 goto errmalloc;
331             *conftype = CONF_CBK;
332             break;
333         }
334         /* fall through */
335     default:
336         if (block)
337             debug(DBG_ERR, "configuration error in block %s, line starting with %s", block, tokens[0]);
338         else
339             debug(DBG_ERR, "configuration error, syntax error in line starting with %s", tokens[0]);
340         return 0;
341     }
342
343     if (**val)
344         return 1;
345
346     debug(DBG_ERR, "configuration error, option %s needs a non-empty value", *opt);
347     goto errexit;
348
349 errmalloc:
350     debug(DBG_ERR, "malloc failed");
351 errexit:
352     free(*opt);
353     *opt = NULL;
354     free(*val);
355     *val = NULL;
356     return 0;
357 }
358
359 uint8_t hexdigit2int(char d) {
360     if (d >= '0' && d <= '9')
361         return d - '0';
362     if (d >= 'a' && d <= 'f')
363         return 10 + d - 'a';
364     if (d >= 'A' && d <= 'F')
365         return 10 + d - 'A';
366     return 0;
367 }
368
369 void unhex(char *s) {
370     char *t;
371     for (t = s; *t; s++) {
372         if (*t == '%' && isxdigit((int)t[1]) && isxdigit((int)t[2])) {
373             *s = 16 * hexdigit2int(t[1]) + hexdigit2int(t[2]);
374             t += 3;
375         } else
376             *s = *t++;
377     }
378     *s = '\0';
379 }
380
381 typedef int (*t_fptr)(struct gconffile **, void *, char *, char *, char *);
382
383 /* returns 1 if ok, 0 on error */
384 /* caller must free returned values also on error */
385 int getgenericconfig(struct gconffile **cf, char *block, ...) {
386     va_list ap;
387     char *opt = NULL, *val, *word, *optval, **str = NULL, ***mstr = NULL, **newmstr, *endptr;
388     uint8_t *bln = NULL;
389     long int *lint = NULL;
390     int type = 0, conftype = 0, n;
391     t_fptr cbk = NULL;
392     void *cbkarg = NULL;
393
394     for (;;) {
395         free(opt);
396         if (!getconfigline(cf, block, &opt, &val, &conftype))
397             return 0;
398         if (!opt)
399             return 1;
400
401         if (conftype == CONF_STR && !strcasecmp(opt, "include")) {
402             if (!pushgconfpaths(cf, val)) {
403                 debug(DBG_ERR, "failed to include config file %s", val);
404                 goto errexit;
405             }
406             free(val);
407             continue;
408         }
409
410         va_start(ap, block);
411         while ((word = va_arg(ap, char *))) {
412             type = va_arg(ap, int);
413             switch (type) {
414             case CONF_STR:
415                 str = va_arg(ap, char **);
416                 if (!str)
417                     goto errparam;
418                 break;
419             case CONF_MSTR:
420                 mstr = va_arg(ap, char ***);
421                 if (!mstr)
422                     goto errparam;
423                 break;
424             case CONF_BLN:
425                 bln = va_arg(ap, uint8_t *);
426                 if (!bln)
427                     goto errparam;
428                 break;
429             case CONF_LINT:
430                 lint = va_arg(ap, long int *);
431                 if (!lint)
432                     goto errparam;
433                 break;
434             case CONF_CBK:
435                 cbk = va_arg(ap, t_fptr);
436                 if (!cbk)
437                     goto errparam;
438                 cbkarg = va_arg(ap, void *);
439                 break;
440             default:
441                 goto errparam;
442             }
443             if (!strcasecmp(opt, word))
444                 break;
445         }
446         va_end(ap);
447
448         if (!word) {
449             if (block)
450                 debug(DBG_ERR, "configuration error in block %s, unknown option %s", block, opt);
451             debug(DBG_ERR, "configuration error, unknown option %s", opt);
452             goto errexit;
453         }
454
455         if (((type == CONF_STR || type == CONF_MSTR || type == CONF_BLN || type == CONF_LINT) && conftype != CONF_STR) ||
456             (type == CONF_CBK && conftype != CONF_CBK)) {
457             if (block)
458                 debug(DBG_ERR, "configuration error in block %s, wrong syntax for option %s", block, opt);
459             debug(DBG_ERR, "configuration error, wrong syntax for option %s", opt);
460             goto errexit;
461         }
462
463         switch (type) {
464         case CONF_STR:
465             if (*str) {
466                 debug(DBG_ERR, "configuration error, option %s already set to %s", opt, *str);
467                 goto errexit;
468             }
469             unhex(val);
470             *str = val;
471             break;
472         case CONF_MSTR:
473             if (*mstr)
474                 for (n = 0; (*mstr)[n]; n++);
475             else
476                 n = 0;
477             newmstr = realloc(*mstr, sizeof(char *) * (n + 2));
478             if (!newmstr) {
479                 debug(DBG_ERR, "malloc failed");
480                 goto errexit;
481             }
482             unhex(val);
483             newmstr[n] = val;
484             newmstr[n + 1] = NULL;
485             *mstr = newmstr;
486             break;
487         case CONF_BLN:
488             if (!strcasecmp(val, "on"))
489                 *bln = 1;
490             else if (!strcasecmp(val, "off"))
491                 *bln = 0;
492             else {
493                 if (block)
494                     debug(DBG_ERR, "configuration error in block %s, value for option %s must be on or off, not %s", block, opt, val);
495                 else
496                     debug(DBG_ERR, "configuration error, value for option %s must be on or off, not %s", opt, val);
497                 goto errexit;
498             }
499             break;
500         case CONF_LINT:
501             endptr = NULL;
502             *lint = strtol(val, &endptr, 0);
503             if (*lint == LONG_MIN || *lint == LONG_MAX || !endptr || endptr == val || *endptr != '\0') {
504                 if (block)
505                     debug(DBG_ERR, "configuration error in block %s, value for option %s must be an integer, not %s", block, opt, val);
506                 else
507                     debug(DBG_ERR, "configuration error, value for option %s must be an integer, not %s", opt, val);
508                 goto errexit;
509             }
510             break;
511         case CONF_CBK:
512             optval = malloc(strlen(opt) + strlen(val) + 2);
513             if (!optval) {
514                 debug(DBG_ERR, "malloc failed");
515                 goto errexit;
516             }
517             sprintf(optval, "%s %s", opt, val);
518             if (!cbk(cf, cbkarg, optval, opt, val)) {
519                 free(optval);
520                 goto errexit;
521             }
522             free(val);
523             free(optval);
524             continue;
525         default:
526             goto errparam;
527         }
528         if (block)
529             debug(DBG_DBG, "getgenericconfig: block %s: %s = %s", block, opt, val);
530         else
531             debug(DBG_DBG, "getgenericconfig: %s = %s", opt, val);
532         if (type == CONF_BLN || type == CONF_LINT)
533             free(val);
534     }
535
536 errparam:
537     debug(DBG_ERR, "getgenericconfig: internal parameter error");
538 errexit:
539     free(opt);
540     free(val);
541     return 0;
542 }
543
544 /* Local Variables: */
545 /* c-file-style: "stroustrup" */
546 /* End: */