settings: Add support for hex integers (0x prefix) via get_int()
[strongswan.git] / src / libstrongswan / settings / settings.c
index cf34fd1..b00e819 100644 (file)
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
-
-#ifdef HAVE_GLOB_H
-#include <glob.h>
-#endif /* HAVE_GLOB_H */
+#include <ctype.h>
 
 #include "settings.h"
+#include "settings_types.h"
 
 #include "collections/array.h"
 #include "collections/hashtable.h"
 #include "threading/rwlock.h"
 #include "utils/debug.h"
 
-#define MAX_INCLUSION_LEVEL            10
-
 typedef struct private_settings_t private_settings_t;
-typedef struct section_t section_t;
-typedef struct kv_t kv_t;
 
 /**
- * private data of settings
+ * Parse functions provided by the generated parser.
+ */
+bool settings_parser_parse_file(section_t *root, char *name);
+bool settings_parser_parse_string(section_t *root, char *settings);
+
+/**
+ * Private data of settings
  */
 struct private_settings_t {
 
        /**
-        * public functions
+        * Public interface
         */
        settings_t public;
 
        /**
-        * top level section
+        * Top level section
         */
        section_t *top;
 
        /**
-        * contents of loaded files and in-memory settings (char*)
+        * Contents of replaced settings (char*)
+        *
+        * FIXME: This is required because the pointer returned by get_str()
+        * is not refcounted.  Might cause ever increasing usage stats.
         */
-       linked_list_t *contents;
+       array_t *contents;
 
        /**
-        * lock to safely access the settings
+        * Lock to safely access the settings
         */
        rwlock_t *lock;
 };
 
 /**
- * section containing subsections and key value pairs
- */
-struct section_t {
-
-       /**
-        * name of the section
-        */
-       char *name;
-
-       /**
-        * fallback sections, as section_t
-        */
-       array_t *fallbacks;
-
-       /**
-        * subsections, as section_t
-        */
-       array_t *sections;
-
-       /**
-        * key value pairs, as kv_t
-        */
-       array_t *kv;
-};
-
-/**
- * Key value pair
- */
-struct kv_t {
-
-       /**
-        * key string, relative
-        */
-       char *key;
-
-       /**
-        * value as string
-        */
-       char *value;
-};
-
-/**
- * create a key/value pair
- */
-static kv_t *kv_create(char *key, char *value)
-{
-       kv_t *this;
-       INIT(this,
-               .key = strdup(key),
-               .value = value,
-       );
-       return this;
-}
-
-/**
- * destroy a key/value pair
- */
-static void kv_destroy(kv_t *this)
-{
-       free(this->key);
-       free(this);
-}
-
-/**
- * create a section with the given name
- */
-static section_t *section_create(char *name)
-{
-       section_t *this;
-       INIT(this,
-               .name = strdupnull(name),
-       );
-       return this;
-}
-
-/**
- * destroy a section
- */
-static void section_destroy(section_t *this)
-{
-       array_destroy_function(this->sections, (void*)section_destroy, NULL);
-       array_destroy_function(this->kv, (void*)kv_destroy, NULL);
-       array_destroy(this->fallbacks);
-       free(this->name);
-       free(this);
-}
-
-/**
- * Purge contents of a section, returns if section can be safely removed.
- */
-static bool section_purge(section_t *this)
-{
-       section_t *current;
-       int i;
-
-       array_destroy_function(this->kv, (void*)kv_destroy, NULL);
-       this->kv = NULL;
-       /* we ensure sections used as fallback, or configured with fallbacks (or
-        * having any such subsections) are not removed */
-       for (i = array_count(this->sections) - 1; i >= 0; i--)
-       {
-               array_get(this->sections, i, &current);
-               if (section_purge(current))
-               {
-                       array_remove(this->sections, i, NULL);
-                       section_destroy(current);
-               }
-       }
-       return !this->fallbacks && !array_count(this->sections);
-}
-
-/**
- * callback to find a section by name
- */
-static int section_find(const void *a, const void *b)
-{
-       const char *key = a;
-       const section_t *item = b;
-       return strcmp(key, item->name);
-}
-
-/**
- * callback to sort sections by name
- */
-static int section_sort(const void *a, const void *b, void *user)
-{
-       const section_t *sa = a, *sb = b;
-       return strcmp(sa->name, sb->name);
-}
-
-/**
- * callback to find a kv pair by key
- */
-static int kv_find(const void *a, const void *b)
-{
-       const char *key = a;
-       const kv_t *item = b;
-       return strcmp(key, item->key);
-}
-
-/**
- * callback to sort kv pairs by key
- */
-static int kv_sort(const void *a, const void *b, void *user)
-{
-       const kv_t *kva = a, *kvb = b;
-       return strcmp(kva->key, kvb->key);
-}
-
-/**
  * Print a format key, but consume already processed arguments
  */
 static bool print_key(char *buf, int len, char *start, char *key, va_list args)
@@ -290,13 +143,13 @@ static section_t *find_section_buffered(section_t *section,
        {
                found = section;
        }
-       else if (array_bsearch(section->sections, buf, section_find, &found) == -1)
+       else if (array_bsearch(section->sections, buf, settings_section_find,
+                                                  &found) == -1)
        {
                if (ensure)
                {
-                       found = section_create(buf);
-                       array_insert_create(&section->sections, ARRAY_TAIL, found);
-                       array_sort(section->sections, section_sort, NULL);
+                       found = settings_section_create(strdup(buf));
+                       settings_section_add(section, found, NULL);
                }
        }
        if (found && pos)
@@ -340,7 +193,7 @@ static void find_sections_buffered(section_t *section, char *start, char *key,
        }
        else
        {
-               array_bsearch(section->sections, buf, section_find, &found);
+               array_bsearch(section->sections, buf, settings_section_find, &found);
        }
        if (found)
        {
@@ -501,14 +354,13 @@ static kv_t *find_value_buffered(section_t *section, char *start, char *key,
                {
                        found = section;
                }
-               else if (array_bsearch(section->sections, buf, section_find,
+               else if (array_bsearch(section->sections, buf, settings_section_find,
                                                           &found) == -1)
                {
                        if (ensure)
                        {
-                               found = section_create(buf);
-                               array_insert_create(&section->sections, ARRAY_TAIL, found);
-                               array_sort(section->sections, section_sort, NULL);
+                               found = settings_section_create(strdup(buf));
+                               settings_section_add(section, found, NULL);
                        }
                }
                if (found)
@@ -532,13 +384,12 @@ static kv_t *find_value_buffered(section_t *section, char *start, char *key,
                {
                        return NULL;
                }
-               if (array_bsearch(section->kv, buf, kv_find, &kv) == -1)
+               if (array_bsearch(section->kv, buf, settings_kv_find, &kv) == -1)
                {
                        if (ensure)
                        {
-                               kv = kv_create(buf, NULL);
-                               array_insert_create(&section->kv, ARRAY_TAIL, kv);
-                               array_sort(section->kv, kv_sort, NULL);
+                               kv = settings_kv_create(strdup(buf), NULL);
+                               settings_kv_add(section, kv, NULL);
                        }
                        else if (section->fallbacks)
                        {
@@ -596,19 +447,7 @@ static void set_value(private_settings_t *this, section_t *section,
                                                         TRUE);
        if (kv)
        {
-               if (!value)
-               {
-                       kv->value = NULL;
-               }
-               else if (kv->value && (strlen(value) <= strlen(kv->value)))
-               {       /* overwrite in-place, if possible */
-                       strcpy(kv->value, value);
-               }
-               else
-               {       /* otherwise clone the string and store it in the cache */
-                       kv->value = strdup(value);
-                       this->contents->insert_last(this->contents, kv->value);
-               }
+               settings_kv_set(kv, strdupnull(value), this->contents);
        }
        this->lock->unlock(this->lock);
 }
@@ -672,11 +511,18 @@ METHOD(settings_t, get_bool, bool,
 inline int settings_value_as_int(char *value, int def)
 {
        int intval;
+       char *end;
+       int base = 10;
+
        if (value)
        {
                errno = 0;
-               intval = strtol(value, NULL, 10);
-               if (errno == 0)
+               if (value[0] == '0' && value[1] == 'x')
+               {       /* manually detect 0x prefix as we want to avoid octal encoding */
+                       base = 16;
+               }
+               intval = strtol(value, &end, base);
+               if (errno == 0 && *end == 0 && end != value)
                {
                        return intval;
                }
@@ -699,14 +545,41 @@ METHOD(settings_t, get_int, int,
 /**
  * Described in header
  */
+inline uint64_t settings_value_as_uint64(char *value, uint64_t def)
+{
+       uint64_t intval;
+       char *end;
+       int base = 10;
+
+       if (value)
+       {
+               errno = 0;
+               if (value[0] == '0' && value[1] == 'x')
+               {       /* manually detect 0x prefix as we want to avoid octal encoding */
+                       base = 16;
+               }
+               intval = strtoull(value, &end, base);
+               if (errno == 0 && *end == 0 && end != value)
+               {
+                       return intval;
+               }
+       }
+       return def;
+}
+
+/**
+ * Described in header
+ */
 inline double settings_value_as_double(char *value, double def)
 {
        double dval;
+       char *end;
+
        if (value)
        {
                errno = 0;
-               dval = strtod(value, NULL);
-               if (errno == 0)
+               dval = strtod(value, &end);
+               if (errno == 0 && *end == 0 && end != value)
                {
                        return dval;
                }
@@ -729,16 +602,24 @@ METHOD(settings_t, get_double, double,
 /**
  * Described in header
  */
-inline u_int32_t settings_value_as_time(char *value, u_int32_t def)
+inline uint32_t settings_value_as_time(char *value, uint32_t def)
 {
        char *endptr;
-       u_int32_t timeval;
+       uint32_t timeval;
        if (value)
        {
                errno = 0;
                timeval = strtoul(value, &endptr, 10);
+               if (endptr == value)
+               {
+                       return def;
+               }
                if (errno == 0)
                {
+                       while (isspace(*endptr))
+                       {
+                               endptr++;
+                       }
                        switch (*endptr)
                        {
                                case 'd':               /* time in days */
@@ -751,8 +632,10 @@ inline u_int32_t settings_value_as_time(char *value, u_int32_t def)
                                        timeval *= 60;
                                        break;
                                case 's':               /* time in seconds */
-                               default:
+                               case '\0':
                                        break;
+                               default:
+                                       return def;
                        }
                        return timeval;
                }
@@ -760,8 +643,8 @@ inline u_int32_t settings_value_as_time(char *value, u_int32_t def)
        return def;
 }
 
-METHOD(settings_t, get_time, u_int32_t,
-       private_settings_t *this, char *key, u_int32_t def, ...)
+METHOD(settings_t, get_time, uint32_t,
+       private_settings_t *this, char *key, uint32_t def, ...)
 {
        char *value;
        va_list args;
@@ -817,7 +700,7 @@ METHOD(settings_t, set_double, void,
 }
 
 METHOD(settings_t, set_time, void,
-       private_settings_t *this, char *key, u_int32_t value, ...)
+       private_settings_t *this, char *key, uint32_t value, ...)
 {
        char val[16];
        va_list args;
@@ -892,7 +775,8 @@ static bool section_filter(hashtable_t *seen, section_t **in, char **out)
 static enumerator_t *section_enumerator(section_t *section,
                                                                                enumerator_data_t *data)
 {
-       return enumerator_create_filter(array_create_enumerator(section->sections),
+       return enumerator_create_filter(
+                       array_create_enumerator(section->sections_order),
                                (void*)section_filter, data->seen, NULL);
 }
 
@@ -929,7 +813,7 @@ static bool kv_filter(hashtable_t *seen, kv_t **in, char **key,
                                          void *none, char **value)
 {
        *key = (*in)->key;
-       if (seen->get(seen, *key))
+       if (seen->get(seen, *key) || !(*in)->value)
        {
                return FALSE;
        }
@@ -943,7 +827,7 @@ static bool kv_filter(hashtable_t *seen, kv_t **in, char **key,
  */
 static enumerator_t *kv_enumerator(section_t *section, enumerator_data_t *data)
 {
-       return enumerator_create_filter(array_create_enumerator(section->kv),
+       return enumerator_create_filter(array_create_enumerator(section->kv_order),
                                        (void*)kv_filter, data->seen, NULL);
 }
 
@@ -990,474 +874,71 @@ METHOD(settings_t, add_fallback, void,
 }
 
 /**
- * parse text, truncate "skip" chars, delimited by term respecting brackets.
- *
- * Chars in "skip" are truncated at the beginning and the end of the resulting
- * token. "term" contains a list of characters to read up to (first match),
- * while "br" contains bracket counterparts found in "term" to skip.
+ * Load settings from files matching the given file pattern or from a string.
+ * All sections and values are added relative to "parent".
+ * All files (even included ones) have to be loaded successfully.
+ * If merge is FALSE the contents of parent are replaced with the parsed
+ * contents, otherwise they are merged together.
  */
-static char parse(char **text, char *skip, char *term, char *br, char **token)
+static bool load_internal(private_settings_t *this, section_t *parent,
+                                                 char *pattern, bool merge, bool string)
 {
-       char *best = NULL;
-       char best_term = '\0';
-
-       /* skip leading chars */
-       while (strchr(skip, **text))
-       {
-               (*text)++;
-               if (!**text)
-               {
-                       return 0;
-               }
-       }
-       /* mark begin of subtext */
-       *token = *text;
-       while (*term)
-       {
-               char *pos = *text;
-               int level = 1;
+       section_t *section;
+       bool loaded;
 
-               /* find terminator */
-               while (*pos)
-               {
-                       if (*pos == *term)
-                       {
-                               level--;
-                       }
-                       else if (br && *pos == *br)
-                       {
-                               level++;
-                       }
-                       if (level == 0)
-                       {
-                               if (best == NULL || best > pos)
-                               {
-                                       best = pos;
-                                       best_term = *term;
-                               }
-                               break;
-                       }
-                       pos++;
-               }
-               /* try next terminator */
-               term++;
-               if (br)
-               {
-                       br++;
-               }
-       }
-       if (best)
-       {
-               /* update input */
-               *text = best;
-               /* null trailing bytes */
-               do
-               {
-                       *best = '\0';
-                       best--;
-               }
-               while (best >= *token && strchr(skip, *best));
-               /* return found terminator */
-               return best_term;
+       if (pattern == NULL || !pattern[0])
+       {       /* TODO: Clear parent if merge is FALSE? */
+               return TRUE;
        }
-       return 0;
-}
 
-/**
- * Check if "text" starts with "pattern".
- * Characters in "skip" are skipped first. If found, TRUE is returned and "text"
- * is modified to point to the character right after "pattern".
- */
-static bool starts_with(char **text, char *skip, char *pattern)
-{
-       char *pos = *text;
-       int len = strlen(pattern);
-       while (strchr(skip, *pos))
-       {
-               pos++;
-               if (!*pos)
-               {
-                       return FALSE;
-               }
-       }
-       if (strlen(pos) < len || !strneq(pos, pattern, len))
+       section = settings_section_create(NULL);
+       loaded = string ? settings_parser_parse_string(section, pattern) :
+                                         settings_parser_parse_file(section, pattern);
+       if (!loaded)
        {
+               settings_section_destroy(section, NULL);
                return FALSE;
        }
-       *text = pos + len;
-       return TRUE;
-}
 
-/**
- * Check if what follows in "text" is an include statement.
- * If this function returns TRUE, "text" will point to the character right after
- * the include pattern, which is returned in "pattern".
- */
-static bool parse_include(char **text, char **pattern)
-{
-       char *pos = *text;
-       if (!starts_with(&pos, "\n\t ", "include"))
-       {
-               return FALSE;
-       }
-       if (starts_with(&pos, "\t ", "="))
-       {       /* ignore "include = value" */
-               return FALSE;
-       }
-       *text = pos;
-       return parse(text, "\t ", "\n", NULL, pattern) != 0;
-}
-
-/**
- * Forward declaration.
- */
-static bool parse_files(linked_list_t *contents, char *file, int level,
-                                               char *pattern, section_t *section);
-
-/**
- * Parse a section
- */
-static bool parse_section(linked_list_t *contents, char *file, int level,
-                                                 char **text, section_t *section)
-{
-       bool finished = FALSE;
-       char *key, *value, *inner;
+       this->lock->write_lock(this->lock);
+       settings_section_extend(parent, section, this->contents, !merge);
+       this->lock->unlock(this->lock);
 
-       while (!finished)
-       {
-               if (parse_include(text, &value))
-               {
-                       if (!parse_files(contents, file, level, value, section))
-                       {
-                               DBG1(DBG_LIB, "failed to include '%s'", value);
-                               return FALSE;
-                       }
-                       continue;
-               }
-               switch (parse(text, "\t\n ", "{=#", NULL, &key))
-               {
-                       case '{':
-                               if (parse(text, "\t ", "}", "{", &inner))
-                               {
-                                       section_t *sub;
-                                       if (!strlen(key))
-                                       {
-                                               DBG1(DBG_LIB, "skipping section without name in '%s'",
-                                                        section->name);
-                                               continue;
-                                       }
-                                       if (array_bsearch(section->sections, key, section_find,
-                                                                         &sub) == -1)
-                                       {
-                                               sub = section_create(key);
-                                               if (parse_section(contents, file, level, &inner, sub))
-                                               {
-                                                       array_insert_create(&section->sections, ARRAY_TAIL,
-                                                                                               sub);
-                                                       array_sort(section->sections, section_sort, NULL);
-                                                       continue;
-                                               }
-                                               section_destroy(sub);
-                                       }
-                                       else
-                                       {       /* extend the existing section */
-                                               if (parse_section(contents, file, level, &inner, sub))
-                                               {
-                                                       continue;
-                                               }
-                                       }
-                                       DBG1(DBG_LIB, "parsing subsection '%s' failed", key);
-                                       break;
-                               }
-                               DBG1(DBG_LIB, "matching '}' not found near %s", *text);
-                               break;
-                       case '=':
-                               if (parse(text, "\t ", "\n", NULL, &value))
-                               {
-                                       kv_t *kv;
-                                       if (!strlen(key))
-                                       {
-                                               DBG1(DBG_LIB, "skipping value without key in '%s'",
-                                                        section->name);
-                                               continue;
-                                       }
-                                       if (array_bsearch(section->kv, key, kv_find, &kv) == -1)
-                                       {
-                                               kv = kv_create(key, value);
-                                               array_insert_create(&section->kv, ARRAY_TAIL, kv);
-                                               array_sort(section->kv, kv_sort, NULL);
-                                       }
-                                       else
-                                       {       /* replace with the most recently read value */
-                                               kv->value = value;
-                                       }
-                                       continue;
-                               }
-                               DBG1(DBG_LIB, "parsing value failed near %s", *text);
-                               break;
-                       case '#':
-                               parse(text, "", "\n", NULL, &value);
-                               continue;
-                       default:
-                               finished = TRUE;
-                               continue;
-               }
-               return FALSE;
-       }
+       settings_section_destroy(section, NULL);
        return TRUE;
 }
 
-/**
- * Parse a file and add the settings to the given section.
- */
-static bool parse_file(linked_list_t *contents, char *file, int level,
-                                          section_t *section)
-{
-       bool success;
-       char *text, *pos;
-       struct stat st;
-       FILE *fd;
-       int len;
-
-       DBG2(DBG_LIB, "loading config file '%s'", file);
-       if (stat(file, &st) == -1)
-       {
-               if (errno == ENOENT)
-               {
-#ifdef STRONGSWAN_CONF
-                       if (streq(file, STRONGSWAN_CONF))
-                       {
-                               DBG2(DBG_LIB, "'%s' does not exist, ignored", file);
-                       }
-                       else
-#endif
-                       {
-                               DBG1(DBG_LIB, "'%s' does not exist, ignored", file);
-                       }
-                       return TRUE;
-               }
-               DBG1(DBG_LIB, "failed to stat '%s': %s", file, strerror(errno));
-               return FALSE;
-       }
-       else if (!S_ISREG(st.st_mode))
-       {
-               DBG1(DBG_LIB, "'%s' is not a regular file", file);
-               return FALSE;
-       }
-       fd = fopen(file, "r");
-       if (fd == NULL)
-       {
-               DBG1(DBG_LIB, "'%s' is not readable", file);
-               return FALSE;
-       }
-       fseek(fd, 0, SEEK_END);
-       len = ftell(fd);
-       rewind(fd);
-       text = malloc(len + 2);
-       text[len] = text[len + 1] = '\0';
-       if (fread(text, 1, len, fd) != len)
-       {
-               free(text);
-               fclose(fd);
-               return FALSE;
-       }
-       fclose(fd);
-
-       pos = text;
-       success = parse_section(contents, file, level, &pos, section);
-       if (!success)
-       {
-               free(text);
-       }
-       else
-       {
-               contents->insert_last(contents, text);
-       }
-       return success;
-}
-
-/**
- * Load the files matching "pattern", which is resolved with glob(3), if
- * available.
- * If the pattern is relative, the directory of "file" is used as base.
- */
-static bool parse_files(linked_list_t *contents, char *file, int level,
-                                               char *pattern, section_t *section)
-{
-       bool success = TRUE;
-       char pat[PATH_MAX];
-
-       if (level > MAX_INCLUSION_LEVEL)
-       {
-               DBG1(DBG_LIB, "maximum level of %d includes reached, ignored",
-                        MAX_INCLUSION_LEVEL);
-               return TRUE;
-       }
-
-       if (!strlen(pattern))
-       {
-               DBG1(DBG_LIB, "empty include pattern, ignored");
-               return TRUE;
-       }
-
-       if (!file || pattern[0] == '/')
-       {       /* absolute path */
-               if (snprintf(pat, sizeof(pat), "%s", pattern) >= sizeof(pat))
-               {
-                       DBG1(DBG_LIB, "include pattern too long, ignored");
-                       return TRUE;
-               }
-       }
-       else
-       {       /* base relative paths to the directory of the current file */
-               char *dir = path_dirname(file);
-               if (snprintf(pat, sizeof(pat), "%s/%s", dir, pattern) >= sizeof(pat))
-               {
-                       DBG1(DBG_LIB, "include pattern too long, ignored");
-                       free(dir);
-                       return TRUE;
-               }
-               free(dir);
-       }
-#ifdef HAVE_GLOB_H
-       {
-               int status;
-               glob_t buf;
-
-               status = glob(pat, GLOB_ERR, NULL, &buf);
-               if (status == GLOB_NOMATCH)
-               {
-                       DBG1(DBG_LIB, "no files found matching '%s', ignored", pat);
-               }
-               else if (status != 0)
-               {
-                       DBG1(DBG_LIB, "expanding file pattern '%s' failed", pat);
-                       success = FALSE;
-               }
-               else
-               {
-                       char **expanded;
-                       for (expanded = buf.gl_pathv; *expanded != NULL; expanded++)
-                       {
-                               success &= parse_file(contents, *expanded, level + 1, section);
-                               if (!success)
-                               {
-                                       break;
-                               }
-                       }
-               }
-               globfree(&buf);
-       }
-#else /* HAVE_GLOB_H */
-       /* if glob(3) is not available, try to load pattern directly */
-       success = parse_file(contents, pat, level + 1, section);
-#endif /* HAVE_GLOB_H */
-       return success;
-}
-
-/**
- * Recursivly extends "base" with "extension".
- */
-static void section_extend(section_t *base, section_t *extension)
+METHOD(settings_t, load_files, bool,
+       private_settings_t *this, char *pattern, bool merge)
 {
-       enumerator_t *enumerator;
-       section_t *sec;
-       kv_t *kv;
-
-       enumerator = array_create_enumerator(extension->sections);
-       while (enumerator->enumerate(enumerator, (void**)&sec))
-       {
-               section_t *found;
-               if (array_bsearch(base->sections, sec->name, section_find,
-                       &found) != -1)
-               {
-                       section_extend(found, sec);
-               }
-               else
-               {
-                       array_remove_at(extension->sections, enumerator);
-                       array_insert_create(&base->sections, ARRAY_TAIL, sec);
-                       array_sort(base->sections, section_sort, NULL);
-               }
-       }
-       enumerator->destroy(enumerator);
-
-       enumerator = array_create_enumerator(extension->kv);
-       while (enumerator->enumerate(enumerator, (void**)&kv))
-       {
-               kv_t *found;
-               if (array_bsearch(base->kv, kv->key, kv_find, &found) != -1)
-               {
-                       found->value = kv->value;
-               }
-               else
-               {
-                       array_remove_at(extension->kv, enumerator);
-                       array_insert_create(&base->kv, ARRAY_TAIL, kv);
-                       array_sort(base->kv, kv_sort, NULL);
-               }
-       }
-       enumerator->destroy(enumerator);
+       return load_internal(this, this->top, pattern, merge, FALSE);
 }
 
-/**
- * Load settings from files matching the given file pattern.
- * All sections and values are added relative to "parent".
- * All files (even included ones) have to be loaded successfully.
- */
-static bool load_files_internal(private_settings_t *this, section_t *parent,
-                                                               char *pattern, bool merge)
+METHOD(settings_t, load_files_section, bool,
+       private_settings_t *this, char *pattern, bool merge, char *key, ...)
 {
-       char *text;
-       linked_list_t *contents;
        section_t *section;
+       va_list args;
 
-       if (pattern == NULL)
-       {
-#ifdef STRONGSWAN_CONF
-               pattern = STRONGSWAN_CONF;
-#else
-               return FALSE;
-#endif
-       }
-
-       contents = linked_list_create();
-       section = section_create(NULL);
+       va_start(args, key);
+       section = ensure_section(this, this->top, key, args);
+       va_end(args);
 
-       if (!parse_files(contents, NULL, 0, pattern, section))
+       if (!section)
        {
-               contents->destroy_function(contents, (void*)free);
-               section_destroy(section);
                return FALSE;
        }
-
-       this->lock->write_lock(this->lock);
-       if (!merge)
-       {
-               section_purge(parent);
-       }
-       /* extend parent section */
-       section_extend(parent, section);
-       /* move contents of loaded files to main store */
-       while (contents->remove_first(contents, (void**)&text) == SUCCESS)
-       {
-               this->contents->insert_last(this->contents, text);
-       }
-       this->lock->unlock(this->lock);
-
-       section_destroy(section);
-       contents->destroy(contents);
-       return TRUE;
+       return load_internal(this, section, pattern, merge, FALSE);
 }
 
-METHOD(settings_t, load_files, bool,
-       private_settings_t *this, char *pattern, bool merge)
+METHOD(settings_t, load_string, bool,
+       private_settings_t *this, char *settings, bool merge)
 {
-       return load_files_internal(this, this->top, pattern, merge);
+       return load_internal(this, this->top, settings, merge, TRUE);
 }
 
-METHOD(settings_t, load_files_section, bool,
-       private_settings_t *this, char *pattern, bool merge, char *key, ...)
+METHOD(settings_t, load_string_section, bool,
+       private_settings_t *this, char *settings, bool merge, char *key, ...)
 {
        section_t *section;
        va_list args;
@@ -1470,22 +951,19 @@ METHOD(settings_t, load_files_section, bool,
        {
                return FALSE;
        }
-       return load_files_internal(this, section, pattern, merge);
+       return load_internal(this, section, settings, merge, TRUE);
 }
 
 METHOD(settings_t, destroy, void,
        private_settings_t *this)
 {
-       section_destroy(this->top);
-       this->contents->destroy_function(this->contents, (void*)free);
+       settings_section_destroy(this->top, NULL);
+       array_destroy_function(this->contents, (void*)free, NULL);
        this->lock->destroy(this->lock);
        free(this);
 }
 
-/*
- * see header file
- */
-settings_t *settings_create(char *file)
+static private_settings_t *settings_create_base()
 {
        private_settings_t *this;
 
@@ -1507,14 +985,37 @@ settings_t *settings_create(char *file)
                        .add_fallback = _add_fallback,
                        .load_files = _load_files,
                        .load_files_section = _load_files_section,
+                       .load_string = _load_string,
+                       .load_string_section = _load_string_section,
                        .destroy = _destroy,
                },
-               .top = section_create(NULL),
-               .contents = linked_list_create(),
+               .top = settings_section_create(NULL),
+               .contents = array_create(0, 0),
                .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
        );
+       return this;
+}
+
+/*
+ * see header file
+ */
+settings_t *settings_create(char *file)
+{
+       private_settings_t *this = settings_create_base();
 
        load_files(this, file, FALSE);
 
        return &this->public;
 }
+
+/*
+ * see header file
+ */
+settings_t *settings_create_string(char *settings)
+{
+       private_settings_t *this = settings_create_base();
+
+       load_string(this, settings, FALSE);
+
+       return &this->public;
+}