Fixed settings lookup if the section/key contains dots
[strongswan.git] / src / libstrongswan / settings.c
1 /*
2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #define _GNU_SOURCE
17 #include <string.h>
18 #include <stdarg.h>
19 #include <stdio.h>
20 #include <errno.h>
21
22 #include "settings.h"
23
24 #include "debug.h"
25 #include "utils/linked_list.h"
26
27
28 typedef struct private_settings_t private_settings_t;
29 typedef struct section_t section_t;
30 typedef struct kv_t kv_t;
31
32 /**
33 * private data of settings
34 */
35 struct private_settings_t {
36
37 /**
38 * public functions
39 */
40 settings_t public;
41
42 /**
43 * top level section
44 */
45 section_t *top;
46
47 /**
48 * allocated file text
49 */
50 char *text;
51 };
52
53 /**
54 * section containing subsections and key value pairs
55 */
56 struct section_t {
57
58 /**
59 * name of the section
60 */
61 char *name;
62
63 /**
64 * subsections, as section_t
65 */
66 linked_list_t *sections;
67
68 /**
69 * key value pairs, as kv_t
70 */
71 linked_list_t *kv;
72 };
73
74 /**
75 * Key value pair
76 */
77 struct kv_t {
78
79 /**
80 * key string, relative
81 */
82 char *key;
83
84 /**
85 * value as string
86 */
87 char *value;
88 };
89
90 /**
91 * find a section by a given key, using buffered key, reusable buffer
92 */
93 static section_t *find_section_buffered(section_t *section, char *key,
94 va_list args, char *buf, int len)
95 {
96 char *pos;
97 enumerator_t *enumerator;
98 section_t *current, *found = NULL;
99
100 if (section == NULL)
101 {
102 return NULL;
103 }
104 pos = strchr(key, '.');
105 if (pos)
106 {
107 *pos = '\0';
108 pos++;
109 }
110 if (vsnprintf(buf, len, key, args) >= len)
111 {
112 return NULL;
113 }
114 enumerator = section->sections->create_enumerator(section->sections);
115 while (enumerator->enumerate(enumerator, &current))
116 {
117 if (streq(current->name, buf))
118 {
119 found = current;
120 break;
121 }
122 }
123 enumerator->destroy(enumerator);
124 if (found && pos)
125 {
126 return find_section_buffered(found, pos, args, buf, len);
127 }
128 return found;
129 }
130
131 /**
132 * find a section by a given key
133 */
134 static section_t *find_section(section_t *section, char *key, va_list args)
135 {
136 char buf[128], keybuf[512];
137
138 if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
139 {
140 return NULL;
141 }
142 return find_section_buffered(section, keybuf, args, buf, sizeof(buf));
143 }
144
145 /**
146 * Find the string value for a key, using buffered key, reusable buffer
147 */
148 static char *find_value_buffered(section_t *section, char *key, va_list args,
149 char *buf, int len)
150 {
151 char *pos, *value = NULL;
152 enumerator_t *enumerator;
153 kv_t *kv;
154 section_t *current, *found = NULL;
155
156 if (section == NULL)
157 {
158 return NULL;
159 }
160
161 pos = strchr(key, '.');
162 if (pos)
163 {
164 *pos = '\0';
165 pos++;
166
167 if (vsnprintf(buf, len, key, args) >= len)
168 {
169 return NULL;
170 }
171 enumerator = section->sections->create_enumerator(section->sections);
172 while (enumerator->enumerate(enumerator, &current))
173 {
174 if (streq(current->name, buf))
175 {
176 found = current;
177 break;
178 }
179 }
180 enumerator->destroy(enumerator);
181 if (found)
182 {
183 return find_value_buffered(found, pos, args, buf, len);
184 }
185 }
186 else
187 {
188 if (vsnprintf(buf, len, key, args) >= len)
189 {
190 return NULL;
191 }
192 enumerator = section->kv->create_enumerator(section->kv);
193 while (enumerator->enumerate(enumerator, &kv))
194 {
195 if (streq(kv->key, buf))
196 {
197 value = kv->value;
198 break;
199 }
200 }
201 enumerator->destroy(enumerator);
202 }
203 return value;
204 }
205
206 /**
207 * Find the string value for a key
208 */
209 static char *find_value(section_t *section, char *key, va_list args)
210 {
211 char buf[128], keybuf[512];
212
213 if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
214 {
215 return NULL;
216 }
217 return find_value_buffered(section, keybuf, args, buf, sizeof(buf));
218 }
219
220 /**
221 * Implementation of settings_t.get.
222 */
223 static char* get_str(private_settings_t *this, char *key, char *def, ...)
224 {
225 char *value;
226 va_list args;
227
228 va_start(args, def);
229 value = find_value(this->top, key, args);
230 va_end(args);
231 if (value)
232 {
233 return value;
234 }
235 return def;
236 }
237
238 /**
239 * Implementation of settings_t.get_bool.
240 */
241 static bool get_bool(private_settings_t *this, char *key, bool def, ...)
242 {
243 char *value;
244 va_list args;
245
246 va_start(args, def);
247 value = find_value(this->top, key, args);
248 va_end(args);
249 if (value)
250 {
251 if (strcaseeq(value, "true") ||
252 strcaseeq(value, "enabled") ||
253 strcaseeq(value, "yes") ||
254 strcaseeq(value, "1"))
255 {
256 return TRUE;
257 }
258 else if (strcaseeq(value, "false") ||
259 strcaseeq(value, "disabled") ||
260 strcaseeq(value, "no") ||
261 strcaseeq(value, "0"))
262 {
263 return FALSE;
264 }
265 }
266 return def;
267 }
268
269 /**
270 * Implementation of settings_t.get_int.
271 */
272 static int get_int(private_settings_t *this, char *key, int def, ...)
273 {
274 char *value;
275 int intval;
276 va_list args;
277
278 va_start(args, def);
279 value = find_value(this->top, key, args);
280 va_end(args);
281 if (value)
282 {
283 errno = 0;
284 intval = strtol(value, NULL, 10);
285 if (errno == 0)
286 {
287 return intval;
288 }
289 }
290 return def;
291 }
292
293 /**
294 * Implementation of settings_t.get_double.
295 */
296 static double get_double(private_settings_t *this, char *key, double def, ...)
297 {
298 char *value;
299 double dval;
300 va_list args;
301
302 va_start(args, def);
303 value = find_value(this->top, key, args);
304 va_end(args);
305 if (value)
306 {
307 errno = 0;
308 dval = strtod(value, NULL);
309 if (errno == 0)
310 {
311 return dval;
312 }
313 }
314 return def;
315 }
316
317 /**
318 * Implementation of settings_t.get_time.
319 */
320 static u_int32_t get_time(private_settings_t *this, char *key, u_int32_t def, ...)
321 {
322 char *value, *endptr;
323 u_int32_t timeval;
324 va_list args;
325
326 va_start(args, def);
327 value = find_value(this->top, key, args);
328 va_end(args);
329 if (value)
330 {
331 errno = 0;
332 timeval = strtoul(value, &endptr, 10);
333 if (errno == 0)
334 {
335 switch (*endptr)
336 {
337 case 'd': /* time in days */
338 timeval *= 24 * 3600;
339 break;
340 case 'h': /* time in hours */
341 timeval *= 3600;
342 break;
343 case 'm': /* time in minutes */
344 timeval *= 60;
345 break;
346 case 's': /* time in seconds */
347 default:
348 break;
349 }
350 return timeval;
351 }
352 }
353 return def;
354 }
355
356 /**
357 * Enumerate section names, not sections
358 */
359 static bool section_filter(void *null, section_t **in, char **out)
360 {
361 *out = (*in)->name;
362 return TRUE;
363 }
364
365 /**
366 * Implementation of settings_t.create_section_enumerator
367 */
368 static enumerator_t* create_section_enumerator(private_settings_t *this,
369 char *key, ...)
370 {
371 section_t *section;
372 va_list args;
373
374 va_start(args, key);
375 section = find_section(this->top, key, args);
376 va_end(args);
377
378 if (!section)
379 {
380 return enumerator_create_empty();
381 }
382 return enumerator_create_filter(
383 section->sections->create_enumerator(section->sections),
384 (void*)section_filter, NULL, NULL);
385 }
386
387 /**
388 * Enumerate key and values, not kv_t entries
389 */
390 static bool kv_filter(void *null, kv_t **in, char **key,
391 void *none, char **value)
392 {
393 *key = (*in)->key;
394 *value = (*in)->value;
395 return TRUE;
396 }
397
398 /**
399 * Implementation of settings_t.create_key_value_enumerator
400 */
401 static enumerator_t* create_key_value_enumerator(private_settings_t *this,
402 char *key, ...)
403 {
404 section_t *section;
405 va_list args;
406
407 va_start(args, key);
408 section = find_section(this->top, key, args);
409 va_end(args);
410
411 if (!section)
412 {
413 return enumerator_create_empty();
414 }
415 return enumerator_create_filter(
416 section->kv->create_enumerator(section->kv),
417 (void*)kv_filter, NULL, NULL);
418 }
419
420 /**
421 * destroy a section
422 */
423 static void section_destroy(section_t *this)
424 {
425 this->kv->destroy_function(this->kv, free);
426 this->sections->destroy_function(this->sections, (void*)section_destroy);
427
428 free(this);
429 }
430
431 /**
432 * parse text, truncate "skip" chars, delimited by term respecting brackets.
433 *
434 * Chars in "skip" are truncated at the beginning and the end of the resulting
435 * token. "term" contains a list of characters to read up to (first match),
436 * while "br" contains bracket counterparts found in "term" to skip.
437 */
438 static char parse(char **text, char *skip, char *term, char *br, char **token)
439 {
440 char *best = NULL;
441 char best_term = '\0';
442
443 /* skip leading chars */
444 while (strchr(skip, **text))
445 {
446 (*text)++;
447 if (!**text)
448 {
449 return 0;
450 }
451 }
452 /* mark begin of subtext */
453 *token = *text;
454 while (*term)
455 {
456 char *pos = *text;
457 int level = 1;
458
459 /* find terminator */
460 while (*pos)
461 {
462 if (*pos == *term)
463 {
464 level--;
465 }
466 else if (br && *pos == *br)
467 {
468 level++;
469 }
470 if (level == 0)
471 {
472 if (best == NULL || best > pos)
473 {
474 best = pos;
475 best_term = *term;
476 }
477 break;
478 }
479 pos++;
480 }
481 /* try next terminator */
482 term++;
483 if (br)
484 {
485 br++;
486 }
487 }
488 if (best)
489 {
490 /* update input */
491 *text = best;
492 /* null trailing bytes */
493 do
494 {
495 *best = '\0';
496 best--;
497 }
498 while (best >= *token && strchr(skip, *best));
499 /* return found terminator */
500 return best_term;
501 }
502 return 0;
503 }
504
505 /**
506 * Parse a section
507 */
508 static section_t* parse_section(char **text, char *name)
509 {
510 section_t *sub, *section;
511 bool finished = FALSE;
512 char *key, *value, *inner;
513
514 static int lev = 0;
515 lev++;
516
517 section = malloc_thing(section_t);
518 section->name = name;
519 section->sections = linked_list_create();
520 section->kv = linked_list_create();
521
522 while (!finished)
523 {
524 switch (parse(text, "\t\n ", "{=#", NULL, &key))
525 {
526 case '{':
527 if (parse(text, "\t ", "}", "{", &inner))
528 {
529 sub = parse_section(&inner, key);
530 if (sub)
531 {
532 section->sections->insert_last(section->sections, sub);
533 continue;
534 }
535 }
536 DBG1(DBG_LIB, "matching '}' not found near %s", *text);
537 break;
538 case '=':
539 if (parse(text, "\t ", "\n", NULL, &value))
540 {
541 kv_t *kv = malloc_thing(kv_t);
542 kv->key = key;
543 kv->value = value;
544 section->kv->insert_last(section->kv, kv);
545 continue;
546 }
547 DBG1(DBG_LIB, "parsing value failed near %s", *text);
548 break;
549 case '#':
550 parse(text, "", "\n", NULL, &value);
551 continue;
552 default:
553 finished = TRUE;
554 continue;
555 }
556 section_destroy(section);
557 return NULL;
558 }
559 return section;
560 }
561
562 /**
563 * Implementation of settings_t.destroy
564 */
565 static void destroy(private_settings_t *this)
566 {
567 if (this->top)
568 {
569 section_destroy(this->top);
570 }
571 free(this->text);
572 free(this);
573 }
574
575 /*
576 * see header file
577 */
578 settings_t *settings_create(char *file)
579 {
580 private_settings_t *this;
581 char *pos;
582 FILE *fd;
583 int len;
584
585 this = malloc_thing(private_settings_t);
586 this->public.get_str = (char*(*)(settings_t*, char *key, char* def, ...))get_str;
587 this->public.get_int = (int(*)(settings_t*, char *key, int def, ...))get_int;
588 this->public.get_double = (double(*)(settings_t*, char *key, double def, ...))get_double;
589 this->public.get_time = (u_int32_t(*)(settings_t*, char *key, u_int32_t def, ...))get_time;
590 this->public.get_bool = (bool(*)(settings_t*, char *key, bool def, ...))get_bool;
591 this->public.create_section_enumerator = (enumerator_t*(*)(settings_t*,char *section, ...))create_section_enumerator;
592 this->public.create_key_value_enumerator = (enumerator_t*(*)(settings_t*, char *key, ...))create_key_value_enumerator;
593 this->public.destroy = (void(*)(settings_t*))destroy;
594
595 this->top = NULL;
596 this->text = NULL;
597
598 if (file == NULL)
599 {
600 file = STRONGSWAN_CONF;
601 }
602 fd = fopen(file, "r");
603 if (fd == NULL)
604 {
605 DBG1(DBG_LIB, "'%s' does not exist or is not readable", file);
606 return &this->public;
607 }
608 fseek(fd, 0, SEEK_END);
609 len = ftell(fd);
610 rewind(fd);
611 this->text = malloc(len + 1);
612 this->text[len] = '\0';
613 if (fread(this->text, 1, len, fd) != len)
614 {
615 free(this->text);
616 this->text = NULL;
617 return &this->public;
618 }
619 fclose(fd);
620
621 pos = this->text;
622 this->top = parse_section(&pos, NULL);
623 if (this->top == NULL)
624 {
625 free(this->text);
626 this->text = NULL;
627 }
628 return &this->public;
629 }
630