Statistics
| Branch: | Tag: | Revision:

root / src / libstrongswan / settings.c @ 3797b8e767a247f64a706b0a4dc8656e8377fbab

History | View | Annotate | Download (10.4 KB)

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
92
 */
93
static section_t *find_section(section_t *section, char *key, va_list args)
94
{
95
    char name[512], *pos;
96
    enumerator_t *enumerator;
97
    section_t *current, *found = NULL;
98
99
    if (section == NULL)
100
    {
101
        return NULL;
102
    }
103
    if (vsnprintf(name, sizeof(name), key, args) >= sizeof(name))
104
    {
105
        return NULL;
106
    }
107
108
    pos = strchr(name, '.');
109
    if (pos)
110
    {
111
        *pos = '\0';
112
        pos++;
113
    }
114
    enumerator = section->sections->create_enumerator(section->sections);
115
    while (enumerator->enumerate(enumerator, &current))
116
    {
117
        if (streq(current->name, name))
118
        {
119
            found = current;
120
            break;
121
        }
122
    }
123
    enumerator->destroy(enumerator);
124
    if (found && pos)
125
    {
126
        return find_section(found, pos, args);
127
    }
128
    return found;
129
}
130
131
static char *find_value(section_t *section, char *key, va_list args)
132
{
133
    char name[512], *pos, *value = NULL;
134
    enumerator_t *enumerator;
135
    kv_t *kv;
136
    section_t *current, *found = NULL;
137
138
    if (section == NULL)
139
    {
140
        return NULL;
141
    }
142
143
    if (vsnprintf(name, sizeof(name), key, args) >= sizeof(name))
144
    {
145
        return NULL;
146
    }
147
148
    pos = strchr(name, '.');
149
    if (pos)
150
    {
151
        *pos = '\0';
152
        pos++;
153
        enumerator = section->sections->create_enumerator(section->sections);
154
        while (enumerator->enumerate(enumerator, &current))
155
        {
156
            if (streq(current->name, name))
157
            {
158
                found = current;
159
                break;
160
            }
161
        }
162
        enumerator->destroy(enumerator);
163
        if (found)
164
        {
165
            return find_value(found, pos, args);
166
        }
167
    }
168
    else
169
    {
170
        enumerator = section->kv->create_enumerator(section->kv);
171
        while (enumerator->enumerate(enumerator, &kv))
172
        {
173
            if (streq(kv->key, name))
174
            {
175
                value = kv->value;
176
                break;
177
            }
178
        }
179
        enumerator->destroy(enumerator);
180
    }
181
    return value;
182
}
183
184
/**
185
 * Implementation of settings_t.get.
186
 */
187
static char* get_str(private_settings_t *this, char *key, char *def, ...)
188
{
189
    char *value;
190
    va_list args;
191
192
    va_start(args, def);
193
    value = find_value(this->top, key, args);
194
    va_end(args);
195
    if (value)
196
    {
197
        return value;
198
    }
199
    return def;
200
}
201
202
/**
203
 * Implementation of settings_t.get_bool.
204
 */
205
static bool get_bool(private_settings_t *this, char *key, bool def, ...)
206
{
207
    char *value;
208
    va_list args;
209
210
    va_start(args, def);
211
    value = find_value(this->top, key, args);
212
    va_end(args);
213
    if (value)
214
    {
215
        if (strcaseeq(value, "true") ||
216
            strcaseeq(value, "enabled") ||
217
            strcaseeq(value, "yes") ||
218
            strcaseeq(value, "1"))
219
        {
220
            return TRUE;
221
        }
222
        else if (strcaseeq(value, "false") ||
223
                 strcaseeq(value, "disabled") ||
224
                 strcaseeq(value, "no") ||
225
                 strcaseeq(value, "0"))
226
        {
227
            return FALSE;
228
        }
229
    }
230
    return def;
231
}
232
233
/**
234
 * Implementation of settings_t.get_int.
235
 */
236
static int get_int(private_settings_t *this, char *key, int def, ...)
237
{
238
    char *value;
239
    int intval;
240
    va_list args;
241
242
    va_start(args, def);
243
    value = find_value(this->top, key, args);
244
    va_end(args);
245
    if (value)
246
    {
247
        errno = 0;
248
        intval = strtol(value, NULL, 10);
249
        if (errno == 0)
250
        {
251
            return intval;
252
        }
253
    }
254
    return def;
255
}
256
257
/**
258
 * Implementation of settings_t.get_time.
259
 */
260
static u_int32_t get_time(private_settings_t *this, char *key, u_int32_t def, ...)
261
{
262
    char *value, *endptr;
263
    u_int32_t timeval;
264
    va_list args;
265
266
    va_start(args, def);
267
    value = find_value(this->top, key, args);
268
    va_end(args);
269
    if (value)
270
    {
271
        errno = 0;
272
        timeval = strtol(value, &endptr, 10);
273
        if (errno == 0 && timeval >= 0)
274
        {
275
            switch (*endptr)
276
            {
277
                case 'd':        /* time in days */
278
                    timeval *= 24 * 3600;
279
                    break;
280
                case 'h':        /* time in hours */
281
                    timeval *= 3600;
282
                    break;
283
                case 'm':        /* time in minutes */
284
                    timeval *= 60;
285
                    break;
286
                case 's':        /* time in seconds */
287
                    default:
288
                    break;
289
            }
290
            return timeval;
291
        }
292
    }
293
    return def;
294
}
295
296
/**
297
 * Enumerate section names, not sections
298
 */
299
static bool section_filter(void *null, section_t **in, char **out)
300
{
301
    *out = (*in)->name;
302
    return TRUE;
303
}
304
305
/**
306
 * Implementation of settings_t.create_section_enumerator
307
 */
308
static enumerator_t* create_section_enumerator(private_settings_t *this,
309
                                               char *key, ...)
310
{
311
    section_t *section;
312
    va_list args;
313
314
    va_start(args, key);
315
    section = find_section(this->top, key, args);
316
    va_end(args);
317
318
    if (!section)
319
    {
320
        return enumerator_create_empty();
321
    }
322
    return enumerator_create_filter(
323
                    section->sections->create_enumerator(section->sections),
324
                    (void*)section_filter, NULL, NULL);
325
}
326
327
/**
328
 * Enumerate key and values, not kv_t entries
329
 */
330
static bool kv_filter(void *null, kv_t **in, char **key,
331
                      void *none, char **value)
332
{
333
    *key = (*in)->key;
334
    *value = (*in)->value;
335
    return TRUE;
336
}
337
338
/**
339
 * Implementation of settings_t.create_key_value_enumerator
340
 */
341
static enumerator_t* create_key_value_enumerator(private_settings_t *this,
342
                                                 char *key, ...)
343
{
344
    section_t *section;
345
    va_list args;
346
347
    va_start(args, key);
348
    section = find_section(this->top, key, args);
349
    va_end(args);
350
351
    if (!section)
352
    {
353
        return enumerator_create_empty();
354
    }
355
    return enumerator_create_filter(
356
                    section->kv->create_enumerator(section->kv),
357
                    (void*)kv_filter, NULL, NULL);
358
}
359
360
/**
361
 * destroy a section
362
 */
363
static void section_destroy(section_t *this)
364
{
365
    this->kv->destroy_function(this->kv, free);
366
    this->sections->destroy_function(this->sections, (void*)section_destroy);
367
368
    free(this);
369
}
370
371
/**
372
 * parse text, truncate "skip" chars, delimited by term respecting brackets.
373
 *
374
 * Chars in "skip" are truncated at the beginning and the end of the resulting
375
 * token. "term" contains a list of characters to read up to (first match),
376
 * while "br" contains bracket counterparts found in "term" to skip.
377
 */
378
static char parse(char **text, char *skip, char *term, char *br, char **token)
379
{
380
    char *best = NULL;
381
    char best_term = '\0';
382
383
    /* skip leading chars */
384
    while (strchr(skip, **text))
385
    {
386
        (*text)++;
387
        if (!**text)
388
        {
389
            return 0;
390
        }
391
    }
392
    /* mark begin of subtext */
393
    *token = *text;
394
    while (*term)
395
    {
396
        char *pos = *text;
397
        int level = 1;
398
399
        /* find terminator */
400
        while (*pos)
401
        {
402
            if (*pos == *term)
403
            {
404
                level--;
405
            }
406
            else if (br && *pos == *br)
407
            {
408
                level++;
409
            }
410
            if (level == 0)
411
            {
412
                if (best == NULL || best > pos)
413
                {
414
                    best = pos;
415
                    best_term = *term;
416
                }
417
                break;
418
            }
419
            pos++;
420
        }
421
        /* try next terminator */
422
        term++;
423
        if (br)
424
        {
425
            br++;
426
        }
427
    }
428
    if (best)
429
    {
430
        /* update input */
431
        *text = best;
432
        /* null trailing bytes */
433
        do
434
        {
435
            *best = '\0';
436
            best--;
437
        }
438
        while (best >= *token && strchr(skip, *best));
439
        /* return found terminator */
440
        return best_term;
441
    }
442
    return 0;
443
}
444
445
/**
446
 * Parse a section
447
 */
448
static section_t* parse_section(char **text, char *name)
449
{
450
    section_t *sub, *section;
451
    bool finished = FALSE;
452
    char *key, *value, *inner;
453
454
    static int lev = 0;
455
    lev++;
456
457
    section = malloc_thing(section_t);
458
    section->name = name;
459
    section->sections = linked_list_create();
460
    section->kv = linked_list_create();
461
462
    while (!finished)
463
    {
464
        switch (parse(text, "\t\n ", "{=#", NULL, &key))
465
        {
466
            case '{':
467
                if (parse(text, "\t ", "}", "{", &inner))
468
                {
469
                    sub = parse_section(&inner, key);
470
                    if (sub)
471
                    {
472
                        section->sections->insert_last(section->sections, sub);
473
                        continue;
474
                    }
475
                }
476
                DBG1("matching '}' not found near %s", *text);
477
                break;
478
            case '=':
479
                if (parse(text, "\t ", "\n", NULL, &value))
480
                {
481
                    kv_t *kv = malloc_thing(kv_t);
482
                    kv->key = key;
483
                    kv->value = value;
484
                    section->kv->insert_last(section->kv, kv);
485
                    continue;
486
                }
487
                DBG1("parsing value failed near %s", *text);
488
                break;
489
            case '#':
490
                parse(text, "", "\n", NULL, &value);
491
                continue;
492
            default:
493
                finished = TRUE;
494
                continue;
495
        }
496
        section_destroy(section);
497
        return NULL;
498
    }
499
    return section;
500
}
501
502
/**
503
 * Implementation of settings_t.destroy
504
 */
505
static void destroy(private_settings_t *this)
506
{
507
    if (this->top)
508
    {
509
        section_destroy(this->top);
510
    }
511
    free(this->text);
512
    free(this);
513
}
514
515
/*
516
 * see header file
517
 */
518
settings_t *settings_create(char *file)
519
{
520
    private_settings_t *this;
521
    char *pos;
522
    FILE *fd;
523
    int len;
524
525
    this = malloc_thing(private_settings_t);
526
    this->public.get_str = (char*(*)(settings_t*, char *key, char* def, ...))get_str;
527
    this->public.get_int = (int(*)(settings_t*, char *key, int def, ...))get_int;
528
    this->public.get_time = (u_int32_t(*)(settings_t*, char *key, u_int32_t def, ...))get_time;
529
    this->public.get_bool = (bool(*)(settings_t*, char *key, bool def, ...))get_bool;
530
    this->public.create_section_enumerator = (enumerator_t*(*)(settings_t*,char *section, ...))create_section_enumerator;
531
    this->public.create_key_value_enumerator = (enumerator_t*(*)(settings_t*, char *key, ...))create_key_value_enumerator;
532
    this->public.destroy = (void(*)(settings_t*))destroy;
533
534
    this->top = NULL;
535
    this->text = NULL;
536
537
    if (file == NULL)
538
    {
539
        file = STRONGSWAN_CONF;
540
    }
541
    fd = fopen(file, "r");
542
    if (fd == NULL)
543
    {
544
        DBG1("'%s' does not exist or is not readable", file);
545
        return &this->public;
546
    }
547
    fseek(fd, 0, SEEK_END);
548
    len = ftell(fd);
549
    rewind(fd);
550
    this->text = malloc(len + 1);
551
    this->text[len] = '\0';
552
    if (fread(this->text, 1, len, fd) != len)
553
    {
554
        free(this->text);
555
        this->text = NULL;
556
        return &this->public;
557
    }
558
    fclose(fd);
559
560
    pos = this->text;
561
    this->top = parse_section(&pos, NULL);
562
    if (this->top == NULL)
563
    {
564
        free(this->text);
565
        this->text = NULL;
566
    }
567
    return &this->public;
568
}
569