time values in strongswan.conf can be optionally specified in days (d), hours (h...
[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 * $Id$
16 */
17
18 #define _GNU_SOURCE
19 #include <string.h>
20 #include <stdio.h>
21 #include <errno.h>
22
23 #include "settings.h"
24
25 #include <debug.h>
26 #include <utils/linked_list.h>
27
28
29 typedef struct private_settings_t private_settings_t;
30 typedef struct section_t section_t;
31 typedef struct kv_t kv_t;
32
33 /**
34 * private data of settings
35 */
36 struct private_settings_t {
37
38 /**
39 * public functions
40 */
41 settings_t public;
42
43 /**
44 * top level section
45 */
46 section_t *top;
47
48 /**
49 * allocated file text
50 */
51 char *text;
52 };
53
54 /**
55 * section containing subsections and key value pairs
56 */
57 struct section_t {
58
59 /**
60 * name of the section
61 */
62 char *name;
63
64 /**
65 * subsections, as section_t
66 */
67 linked_list_t *sections;
68
69 /**
70 * key value pairs, as kv_t
71 */
72 linked_list_t *kv;
73 };
74
75 /**
76 * Key value pair
77 */
78 struct kv_t {
79
80 /**
81 * key string, relative
82 */
83 char *key;
84
85 /**
86 * value as string
87 */
88 char *value;
89 };
90
91 static char *find(section_t *section, char *key)
92 {
93 char *name, *pos, *value = NULL;
94 enumerator_t *enumerator;
95 kv_t *kv;
96 section_t *current, *found = NULL;
97
98 if (section == NULL)
99 {
100 return NULL;
101 }
102
103 name = strdupa(key);
104
105 pos = strchr(name, '.');
106 if (pos)
107 {
108 *pos = '\0';
109 pos++;
110 enumerator = section->sections->create_enumerator(section->sections);
111 while (enumerator->enumerate(enumerator, &current))
112 {
113 if (streq(current->name, name))
114 {
115 found = current;
116 break;
117 }
118 }
119 enumerator->destroy(enumerator);
120 if (found)
121 {
122 return find(found, pos);
123 }
124 }
125 else
126 {
127 enumerator = section->kv->create_enumerator(section->kv);
128 while (enumerator->enumerate(enumerator, &kv))
129 {
130 if (streq(kv->key, name))
131 {
132 value = kv->value;
133 break;
134 }
135 }
136 enumerator->destroy(enumerator);
137 }
138 return value;
139 }
140
141 /**
142 * Implementation of settings_t.get.
143 */
144 static char* get_str(private_settings_t *this, char *key, char *def)
145 {
146 char *value;
147
148 value = find(this->top, key);
149 if (value)
150 {
151 return value;
152 }
153 return def;
154 }
155
156 /**
157 * Implementation of settings_t.get_bool.
158 */
159 static bool get_bool(private_settings_t *this, char *key, bool def)
160 {
161 char *value;
162
163 value = find(this->top, key);
164 if (value)
165 {
166 if (strcasecmp(value, "true") == 0 ||
167 strcasecmp(value, "enabled") == 0 ||
168 strcasecmp(value, "yes") == 0 ||
169 strcasecmp(value, "1") == 0)
170 {
171 return TRUE;
172 }
173 else if (strcasecmp(value, "false") == 0 ||
174 strcasecmp(value, "disabled") == 0 ||
175 strcasecmp(value, "no") == 0 ||
176 strcasecmp(value, "0") == 0)
177 {
178 return FALSE;
179 }
180 }
181 return def;
182 }
183
184 /**
185 * Implementation of settings_t.get_int.
186 */
187 static int get_int(private_settings_t *this, char *key, int def)
188 {
189 char *value;
190 int intval;
191
192 value = find(this->top, key);
193 if (value)
194 {
195 errno = 0;
196 intval = strtol(value, NULL, 10);
197 if (errno == 0)
198 {
199 return intval;
200 }
201 }
202 return def;
203 }
204
205 /**
206 * Implementation of settings_t.get_time.
207 */
208 static u_int32_t get_time(private_settings_t *this, char *key, u_int32_t def)
209 {
210 char *value, *endptr;
211 u_int32_t timeval;
212
213 value = find(this->top, key);
214 if (value)
215 {
216 errno = 0;
217 timeval = strtol(value, &endptr, 10);
218 if (errno == 0 && timeval >= 0)
219 {
220 switch (*endptr)
221 {
222 case 'd': /* time in days */
223 timeval *= 24 * 3600;
224 break;
225 case 'h': /* time in hours */
226 timeval *= 3600;
227 break;
228 case 'm': /* time in minutes */
229 timeval *= 60;
230 break;
231 case 's': /* time in seconds */
232 default:
233 break;
234 }
235 return timeval;
236 }
237 }
238 return def;
239 }
240
241 /**
242 * destroy a section
243 */
244 static void section_destroy(section_t *this)
245 {
246 this->kv->destroy_function(this->kv, free);
247 this->sections->destroy_function(this->sections, (void*)section_destroy);
248
249 free(this);
250 }
251
252 /**
253 * parse text, truncate "skip" chars, delimited by term respecting brackets.
254 *
255 * Chars in "skip" are truncated at the beginning and the end of the resulting
256 * token. "term" contains a list of characters to read up to (first match),
257 * while "br" contains bracket counterparts found in "term" to skip.
258 */
259 static char parse(char **text, char *skip, char *term, char *br, char **token)
260 {
261 char *best = NULL;
262 char best_term = '\0';
263
264 /* skip leading chars */
265 while (strchr(skip, **text))
266 {
267 (*text)++;
268 if (!**text)
269 {
270 return 0;
271 }
272 }
273 /* mark begin of subtext */
274 *token = *text;
275 while (*term)
276 {
277 char *pos = *text;
278 int level = 1;
279
280 /* find terminator */
281 while (*pos)
282 {
283 if (*pos == *term)
284 {
285 level--;
286 }
287 else if (br && *pos == *br)
288 {
289 level++;
290 }
291 if (level == 0)
292 {
293 if (best == NULL || best > pos)
294 {
295 best = pos;
296 best_term = *term;
297 }
298 break;
299 }
300 pos++;
301 }
302 /* try next terminator */
303 term++;
304 if (br)
305 {
306 br++;
307 }
308 }
309 if (best)
310 {
311 /* update input */
312 *text = best;
313 /* null trailing bytes */
314 do
315 {
316 *best = '\0';
317 best--;
318 }
319 while (best >= *token && strchr(skip, *best));
320 /* return found terminator */
321 return best_term;
322 }
323 return 0;
324 }
325
326 /**
327 * Parse a section
328 */
329 static section_t* parse_section(char **text, char *name)
330 {
331 section_t *sub, *section;
332 bool finished = FALSE;
333 char *key, *value, *inner;
334
335 static int lev = 0;
336 lev++;
337
338 section = malloc_thing(section_t);
339 section->name = name;
340 section->sections = linked_list_create();
341 section->kv = linked_list_create();
342
343 while (!finished)
344 {
345 switch (parse(text, "\t\n ", "{=#", NULL, &key))
346 {
347 case '{':
348 if (parse(text, "\t ", "}", "{", &inner))
349 {
350 sub = parse_section(&inner, key);
351 if (sub)
352 {
353 section->sections->insert_last(section->sections, sub);
354 continue;
355 }
356 }
357 DBG1("matching '}' not found near %s", *text);
358 break;
359 case '=':
360 if (parse(text, "\t ", "\n", NULL, &value))
361 {
362 kv_t *kv = malloc_thing(kv_t);
363 kv->key = key;
364 kv->value = value;
365 section->kv->insert_last(section->kv, kv);
366 continue;
367 }
368 DBG1("parsing value failed near %s", *text);
369 break;
370 case '#':
371 parse(text, "", "\n", NULL, &value);
372 continue;
373 default:
374 finished = TRUE;
375 continue;
376 }
377 section_destroy(section);
378 return NULL;
379 }
380 return section;
381 }
382
383 /**
384 * Implementation of settings_t.destroy
385 */
386 static void destroy(private_settings_t *this)
387 {
388 if (this->top)
389 {
390 section_destroy(this->top);
391 }
392 free(this->text);
393 free(this);
394 }
395
396 /*
397 * see header file
398 */
399 settings_t *settings_create(char *file)
400 {
401 private_settings_t *this = malloc_thing(private_settings_t);
402
403 this->public.get_str = (char*(*)(settings_t*, char *key, char* def))get_str;
404 this->public.get_int = (int(*)(settings_t*, char *key, int def))get_int;
405 this->public.get_time = (u_int32_t(*)(settings_t*, char *key, u_int32_t def))get_time;
406 this->public.get_bool = (bool(*)(settings_t*, char *key, bool def))get_bool;
407 this->public.destroy = (void(*)(settings_t*))destroy;
408
409 this->top = NULL;
410 this->text = NULL;
411
412 if (file)
413 {
414 FILE *fd;
415 int len;
416 char *pos;
417
418 fd = fopen(file, "r");
419 if (fd == NULL)
420 {
421 DBG1("'%s' does not exist or is not readable", file);
422 return &this->public;
423 }
424 fseek(fd, 0, SEEK_END);
425 len = ftell(fd);
426 rewind(fd);
427 this->text = malloc(len + 1);
428 this->text[len] = '\0';
429 if (fread(this->text, 1, len, fd) != len)
430 {
431 free(this->text);
432 this->text = NULL;
433 return &this->public;
434 }
435 fclose(fd);
436
437 pos = this->text;
438 this->top = parse_section(&pos, NULL);
439 if (this->top == NULL)
440 {
441 free(this->text);
442 this->text = NULL;
443 return &this->public;
444 }
445 }
446 return &this->public;
447 }
448