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