From 9898b0576ea3a0facacc8a678b64279abeb508e8 Mon Sep 17 00:00:00 2001
From: Dmitri Pal <dpal@redhat.com>
Date: Mon, 6 Apr 2009 09:42:51 -0400
Subject: [PATCH] First attempt to produce INI interface.

---
 common/ini/Makefile.am     |   17 +
 common/ini/ini.conf        |   55 ++
 common/ini/ini.d/real.conf |   52 ++
 common/ini/ini.d/test.conf |   41 ++
 common/ini/ini_config.c    | 1286 ++++++++++++++++++++++++++++++++++++++++++++
 common/ini/ini_config.h    |  160 ++++++
 common/ini/ini_config_ut.c |  701 ++++++++++++++++++++++++
 7 files changed, 2312 insertions(+), 0 deletions(-)
 create mode 100644 common/ini/Makefile.am
 create mode 100644 common/ini/ini.conf
 create mode 100644 common/ini/ini.d/real.conf
 create mode 100644 common/ini/ini.d/test.conf
 create mode 100644 common/ini/ini_config.c
 create mode 100644 common/ini/ini_config.h
 create mode 100644 common/ini/ini_config_ut.c

diff --git a/common/ini/Makefile.am b/common/ini/Makefile.am
new file mode 100644
index 0000000..a0131e9
--- /dev/null
+++ b/common/ini/Makefile.am
@@ -0,0 +1,17 @@
+#DEBUG_FLAGS=@DEBUG_VAR@
+TRACE_LEVEL=@TRACE_VAR@
+
+topdir=..
+AM_CPPFLAGS = -I$(topdir) -I$(topdir)/trace -I$(topdir)/collection $(TRACE_LEVEL)
+
+# Build static libraty
+noinst_LIBRARIES = libini_config.a
+libini_config_a_SOURCES = ini_config.c collection.h trace.h collection_tools.h collection_class.h ini_config.h
+
+# Build shared library
+#lib_LTLIBRARIES = libini.la
+#libini_la_SOURCES = ini_config.c collection.h trace.h collection_tools.h collection_class.h ini_config.h
+
+noinst_PROGRAMS = ini_config_ut
+ini_config_ut_SOURCES = ini_config_ut.c
+ini_config_ut_LDADD = libini_config.a -lm -lz ../collection/libcollection.a
diff --git a/common/ini/ini.conf b/common/ini/ini.conf
new file mode 100644
index 0000000..0d0c7ed
--- /dev/null
+++ b/common/ini/ini.conf
@@ -0,0 +1,55 @@
+# INI file that is used by unit test
+
+data = ds,mf.ds,mf.ds,m.ds,mds.,fmds.
+   dskjh = mdsmdssm ,md.sd,
+ C1         = "abcd"                            
+;test data
+
[payload removed: 1100 characters] = haha
+
+ [
+
+     ]
+
+ [ [payload removed: 1099 characters] ]
+
+
+;more data
+ 
+ = "nmjdsbfmsdn"   
+ s = "nmjdsbfmsdn  
+
+ HEX1 = 'ABV1'
+ HEX2 = 'ABV'
+ [               ttt       ]
+
+C2                  ='Ab'           
+ 
+int=-44i44
+int2=-66U
+
+c = 
+
+with creame and sugar
+
+ [               dddd       ]
+
+DC1 = 123U
+
+f=-9.0.0
+DC2 = -235L
+DC3 = -345.8907
+
+
+ [              zoo      
+
+ something
+
+
+ [               ttt       ]
+
+v=55555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555
+v1=55555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555.55555555555555555555555555555555
+
+C3 = -34756.56
+C4 = .9
diff --git a/common/ini/ini.d/real.conf b/common/ini/ini.d/real.conf
new file mode 100644
index 0000000..f026bd8
--- /dev/null
+++ b/common/ini/ini.d/real.conf
@@ -0,0 +1,52 @@
+[config]
+version = 0.1
+
+[monitor]
+description = Monitor Configuration
+sbusTimeout = 10
+sbusAddress = unix:path=/var/lib/sss/pipes/private/dbus
+servicePingTime = 10
+bad_number = 5a
+
+[services]
+description = Local service configuration
+activeServices = dp, nss, pam, info
+
+[services/dp]
+description = Data Provider Configuration
+command = /usr/libexec/sssd/sssd_dp
+
+[services/nss]
+description = NSS Responder Configuration
+unixSocket = /var/lib/sss/pipes/nss
+command = /usr/libexec/sssd/sssd_nss
+
+[services/pam]
+command = /usr/libexec/sssd/sssd_pam
+description = PAM Responder Configuration
+unixSocket = /var/lib/sss/pipes/pam
+
+[services/info]
+description = InfoPipe Configuration
+command = ./sbin/sssd_info
+
+[domains]
+domainsOrder = LOCAL, EXAMPLE.COM ,   SOMEOTHER.COM      
+
+[domains/LOCAL]
+description = Reserved domain for local configurations
+legacy = FALSE
+enumerate = 3
+
+[domains/EXAMPLE.COM]
+description = Example domain served by IPA
+provider = ipa
+server = ipaserver1.example.com
+server = ipabackupserver.example.com
+legacy = FALSE
+enumerate = 0
+binary_test = '010203'
+long_array = 1, 2; 4' ;8p .16/ 32? 
+
+
+
diff --git a/common/ini/ini.d/test.conf b/common/ini/ini.d/test.conf
new file mode 100644
index 0000000..68e9c67
--- /dev/null
+++ b/common/ini/ini.d/test.conf
@@ -0,0 +1,41 @@
+
+data = ds,mf.ds,mf.ds,m.ds,mds.,fmds.
+   dskjh = mdsmdssm ,md.sd,
+ C1         = "abcd"                            
+;test data
+
+
+
+;more data
+ 
+ = "nmjdsbfmsdn"   
+ s = "nmjdsbfmsdn  
+
+ HEX1 = 'ABV1'
+ HEX2 = 'ABV'
+ [               ttt       ]
+
+C2                  ='AbBB'           
+ 
+int=-44i44
+int2=-66U
+
+c = 
+
+
+ [               dddd1       ]
+
+DC1 = 123U
+
+f=-9.0.0
+DC2 = -235L
+DC3 = -345.8907
+
+
+ [               ttt2       ]
+
+v=55555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555
+v1=55555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555.55555555555555555555555555555555
+
+C3 = -34756.56
+C4 = .9
diff --git a/common/ini/ini_config.c b/common/ini/ini_config.c
new file mode 100644
index 0000000..cf70c9c
--- /dev/null
+++ b/common/ini/ini_config.c
@@ -0,0 +1,1286 @@
+/*
+    INI LIBRARY
+
+    Reading configuration from INI file
+    and storing as a collection.
+
+    Copyright (C) Dmitri Pal <dpal@redhat.com> 2009
+
+    INI Library is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    INI Library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with INI Library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include "config.h"
+/* For error text */
+#include <libintl.h>
+#define _(String) gettext (String)
+/* INI file is used as a collection */
+#include "collection_priv.h"
+#include "collection.h"
+#include "collection_tools.h"
+#include "trace.h"
+#include "ini_config.h"
+
+#define NAME_OVERHEAD   10
+
+#define SLASH           "/"
+
+/* Name of the special collection used to store parsing errors */
+#define FILE_ERROR_SET  "ini_file_error_set"
+
+/* Text error strings used when errors are printed out */
+#define WARNING_TXT        _("Warning")
+#define ERROR_TXT          _("Error")
+#define WRONG_COLLECTION   _("Passed in list is not a list of parse errors.\n")
+#define FAILED_TO_PROCCESS _("Internal Error. Failed to process error list.\n")
+#define ERROR_HEADER       _("Parsing errors and warnings in file: %s\n")
+#define LINE_FORMAT        _("%s (%d) on line %d: %s\n")
+
+/* Codes that parsing function can return */
+#define RET_PAIR        0
+#define RET_COMMENT     1
+#define RET_SECTION     2
+#define RET_INVALID     3
+#define RET_EMPTY       4
+#define RET_EOF         5
+#define RET_ERROR       6
+
+/* STATIC INTERNAL FUNCTIONS */
+#ifdef HAVE_PARSE_ERROR
+
+
+/* Function to return parsing error */
+inline const char *parsing_error_str(int parsing_error)
+{
+    char *placeholder= _("Unknown error.");
+    char *str_error[] = { _("Data is too long."),
+                          _("No closing bracket."),
+                          _("Section name is missing."),
+                          _("Section name is too long."),
+                          _("Equal sign is missing."),
+                          _("Property name is missing."),
+                          _("Property name is too long.")
+    };
+
+    /* Check the range */
+    if((parsing_error < 1) || (parsing_error > ERR_MAXPARSE)) return (const char *)placeholder;
+    else return (const char *)(str_error[parsing_error-1]);
+}
+
+#else
+
+
+inline const char *parsing_error_str(int parsing_error)
+{
+    char *placeholder= _("Parsing errors are not compiled.");
+    return (const char*)(placeholder);
+}
+
+#endif
+
+/* Add to collection or update - CONSIDER moving to the collection.c */
+static int add_or_update(struct collection_item *current_section,
+                         char *key,
+                         void *value,
+                         int length,
+                         int type)
+{
+    int found = COL_NOMATCH;
+	int error = EOK;
+
+    TRACE_FLOW_STRING("add_or_update", "Entry");
+
+    (void)is_item_in_collection(current_section,key,COL_TYPE_ANY,COL_TRAVERSE_IGNORE,&found);
+
+    if(found == COL_MATCH) {
+        TRACE_INFO_STRING("Updating...", "");
+        error = update_property(current_section,key,type,value,length,COL_TRAVERSE_IGNORE);
+    }
+    else {
+        TRACE_INFO_STRING("Adding...", "");
+        error = add_any_property(current_section,NULL,key,type,value,length);
+    }
+
+    TRACE_FLOW_NUMBER("add_or_update returning", error);
+    return error;
+}
+
+/***************************************************************************/
+/* Function to read single ini file and pupulate
+ * the provided collection with subcollcetions from the file */
+static int ini_to_collection(const char *filename,
+                             struct collection_item *ini_config,
+                             int error_level,
+                             struct collection_item **error_list)
+{
+    FILE *file;
+    int error;
+    int status;
+    int section_count = 0;
+    char *key = NULL;
+    char *value = NULL;
+    struct collection_item *current_section = (struct collection_item *)(NULL);
+    int length;
+    int type;
+    int ext_err = -1;
+    struct parse_error pe;
+    int line = 0;
+    int created = 0;
+
+    TRACE_FLOW_STRING("ini_to_collection", "Entry");
+
+    /* Open file for reading */
+    file = fopen(filename,"r");
+    if(file == NULL) {
+        error = errno;
+        TRACE_ERROR_NUMBER("Failed to open file - but this is OK", error);
+        return EOK;
+    }
+
+    /* Open the collection of errors */
+    if(error_list != (struct collection_item **)(NULL)) {
+        *error_list = (struct collection_item *)(NULL);
+        error = create_collection(error_list,(char *)filename,COL_CLASS_INI_PERROR);
+        if(error) {
+            TRACE_ERROR_NUMBER("Failed to create error collection", error);
+            fclose(file);
+            return EOK;
+        }
+        created = 1;
+    }
+
+    /* Read file lines */
+    while((status = read_line(file,&key,&value,&length,&ext_err)) != RET_EOF) {
+
+        line++;
+
+        switch(status) {
+            case RET_PAIR:
+                        /* Do we have a section at the top of the file ? */
+                        if(section_count == 0) {
+                            /* Check if collection already exists */
+                            error = get_collection_reference(ini_config,&current_section,INI_DEFAULT_SECTION);
+                            if(error != EOK) {
+                                /* Create default collection */
+                                if((error=create_collection(&current_section,INI_DEFAULT_SECTION,COL_CLASS_INI_SECTION)) ||
+                                   (error=add_collection_to_collection(ini_config,NULL,NULL,
+                                                                       current_section,
+                                                                       COL_ADD_MODE_REFERENCE))) {
+                                    TRACE_ERROR_NUMBER("Failed to create collection", error);
+                                    fclose(file);
+                                    destroy_collection(current_section);
+                                    if(created) destroy_collection(*error_list);
+                                    return error;
+                                }
+                            }
+                            section_count++;
+                        }
+
+                        /* Put value into the collection */
+                        if((error=add_or_update(current_section,key,value,length,COL_TYPE_STRING))) {
+                            TRACE_ERROR_NUMBER("Failed to add pair to collection", error);
+                            fclose(file);
+                            destroy_collection(current_section);
+                            if(created) destroy_collection(*error_list);
+                            return error;
+                        }
+                        break;
+
+            case RET_SECTION:
+                        /* Read a new section */
+                        destroy_collection(current_section);
+                        current_section = (struct collection_item *)(NULL);
+
+                        error = get_collection_reference(ini_config,&current_section,key);
+                        if(error != EOK) {
+                            /* Create default collection */
+                            if((error=create_collection(&current_section,key,COL_CLASS_INI_SECTION)) ||
+                               (error=add_collection_to_collection(ini_config,NULL,NULL,
+                                                                   current_section,
+                                                                   COL_ADD_MODE_REFERENCE))) {
+                                TRACE_ERROR_NUMBER("Failed to add collection", error);
+                                fclose(file);
+                                destroy_collection(current_section);
+                                if(created) destroy_collection(*error_list);
+                                return error;
+                            }
+                        }
+                        section_count++;
+                        break;
+
+            case RET_EMPTY:
+                        TRACE_INFO_STRING("Empty string", "");
+                        break;
+
+            case RET_COMMENT:
+                        TRACE_INFO_STRING("Comment", "");
+                        break;
+
+            case RET_ERROR:
+                        pe.line = line;
+                        pe.error = ext_err;
+                        error = add_binary_property(*error_list,NULL, ERROR_TXT,(void *)&pe,sizeof(pe));
+                        if(error) {
+                            TRACE_ERROR_NUMBER("Failed to add error to collection", error);
+                            fclose(file);
+                            destroy_collection(current_section);
+                            if(created) destroy_collection(*error_list);
+                            return error;
+                        }
+                        /* Exit if there was an error parsing file */
+                        if(error_level != INI_STOP_ON_NONE) {
+                            TRACE_ERROR_STRING("Invalid format of the file", "");
+                            destroy_collection(current_section);
+                            fclose(file);
+                            return EIO;
+                        }
+                        break;
+            case RET_INVALID:
+            default:
+                        pe.line = line;
+                        pe.error = ext_err;
+                        error = add_binary_property(*error_list,NULL, WARNING_TXT,(void *)&pe,sizeof(pe));
+                        if(error) {
+                            TRACE_ERROR_NUMBER("Failed to add warning to collection", error);
+                            fclose(file);
+                            destroy_collection(current_section);
+                            if(created) destroy_collection(*error_list);
+                            return error;
+                        }
+                        /* Exit if we are told to exit on warnings */
+                        if(error_level == INI_STOP_ON_ANY) {
+                            TRACE_ERROR_STRING("Invalid format of the file", "");
+                            if(created) destroy_collection(current_section);
+                            fclose(file);
+                            return EIO;
+                        }
+                        TRACE_ERROR_STRING("Invalid string", "");
+                        break;
+        }
+        ext_err = -1;
+    }
+
+    /* Close file */
+    fclose(file);
+
+    DEBUG_COLLECTION(ini_config);
+
+    destroy_collection(current_section);
+
+    DEBUG_COLLECTION(ini_config);
+
+    TRACE_FLOW_STRING("ini_to_collection", "Success Exit");
+
+    return EOK;
+}
+
+/*********************************************************************/
+/* Read configuration information from a file */
+int config_from_file(const char *application,
+                     const char *config_file,
+                     struct collection_item **ini_config,
+                     int error_level,
+                     struct collection_item **error_list)
+{
+    int error;
+    int created = 0;
+
+    TRACE_FLOW_STRING("config_from_file", "Entry");
+
+    if((ini_config == (struct collection_item **)(NULL)) ||
+       (application == NULL)) {
+        TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
+        return EINVAL;
+    }
+
+    /* Create collection if needed */
+    if(*ini_config == (struct collection_item *)(NULL)) {
+        if((error=create_collection(ini_config,(char *)application,COL_CLASS_INI_CONFIG))) {
+            TRACE_ERROR_NUMBER("Failed to create collection", error);
+            return error;
+        }
+        created = 1;
+    }
+    /* Is the collection of the right class? */
+    else if(is_of_class(*ini_config,COL_CLASS_INI_CONFIG)) {
+        TRACE_ERROR_NUMBER("Wrong collection type", EINVAL);
+        return EINVAL;
+    }
+
+    /* Do the actual work */
+    error = ini_to_collection((char *)config_file, *ini_config,error_level, error_list);
+    /* In case of error when we created collection - delete it */
+    if((error) && (created)) {
+        destroy_collection(*ini_config);
+        *ini_config = (struct collection_item *)(NULL);
+    }
+
+    TRACE_FLOW_NUMBER("config_from_file. Returns", error);
+    return error;
+}
+
+/* Read default config file and then overwrite it with a specific one from the directory */
+int config_for_app(const char *application,
+                   const char *config_file,
+                   const char *config_dir,
+                   struct collection_item **ini_config,
+                   int error_level,
+                   struct collection_item **error_set)
+{
+    int error=EOK;
+    char *file_name;
+    struct collection_item *error_list_common = (struct collection_item *)(NULL);
+    struct collection_item *error_list_specific = (struct collection_item *)(NULL);
+    struct collection_item **pass_common = (struct collection_item **)(NULL);
+    struct collection_item **pass_specific = (struct collection_item **)(NULL);
+    struct collection_item *error_file_set = (struct collection_item *)(NULL);
+    int created = 0;
+
+    TRACE_FLOW_STRING("config_to_collection", "Entry");
+
+    if(ini_config == (struct collection_item **)(NULL)) {
+        TRACE_ERROR_NUMBER("Failed to create collection", EINVAL);
+        return EINVAL;
+    }
+
+    /* Prepare error collection pointers */
+    if(error_set != (struct collection_item **)(NULL)) {
+        TRACE_INFO_STRING("Error set is not NULL", "preparing error set");
+        pass_common = &error_list_common;
+        pass_specific = &error_list_specific;
+        *error_set = (struct collection_item *)(NULL);
+        /* Construct the overarching error collection */
+        if((error=create_collection(error_set,FILE_ERROR_SET,COL_CLASS_INI_PESET))) {
+            TRACE_ERROR_NUMBER("Failed to create collection", error);
+            return error;
+        }
+    }
+    else {
+        TRACE_INFO_STRING("No error set. Errors will not be captured", "");
+        pass_common = (struct collection_item **)(NULL);
+        pass_specific = (struct collection_item **)(NULL);
+    }
+
+    /* Create collection if needed */
+    if(*ini_config == (struct collection_item *)(NULL)) {
+        TRACE_INFO_STRING("New config collection. Allocate.", "");
+        if((error=create_collection(ini_config,(char *)application,COL_CLASS_INI_CONFIG))) {
+            TRACE_ERROR_NUMBER("Failed to create collection", error);
+            destroy_collection(*error_set);
+            *error_set = (struct collection_item *)(NULL);
+            return error;
+        }
+    }
+    /* Is the collection of the right class? */
+    else if(is_of_class(*ini_config,COL_CLASS_INI_CONFIG)) {
+        TRACE_ERROR_NUMBER("Wrong collection type", EINVAL);
+        return EINVAL;
+    }
+
+    /* Read master file */
+    if(config_file != NULL) {
+        TRACE_INFO_STRING("Reading master file:", config_file);
+        if((error = ini_to_collection(config_file,*ini_config, error_level, pass_common))) {
+            TRACE_ERROR_NUMBER("Failed to read master file", error);
+            /* In case of error when we created collection - delete it */
+            if((error) && (created)) {
+                destroy_collection(*ini_config);
+                *ini_config = (struct collection_item *)(NULL);
+            }
+            /* We do not clear the error_set here */
+            return error;
+        }
+        /* Add error results if any to the overarching error collection */
+        if((pass_common != (struct collection_item **)(NULL)) &&
+           (*pass_common != (struct collection_item *)(NULL))) {
+            TRACE_INFO_STRING("Process erros resulting from file:", config_file);
+            error = add_collection_to_collection(*error_set,NULL,NULL,*pass_common,COL_ADD_MODE_EMBED);
+            if(error) {
+                if(created) {
+                    destroy_collection(*ini_config);
+                    *ini_config = (struct collection_item *)(NULL);
+                }
+                destroy_collection(*error_set);
+                *error_set = (struct collection_item *)(NULL);
+                TRACE_ERROR_NUMBER("Failed to add error collection to another error collection", error);
+                return error;
+            }
+        }
+    }
+
+    if(config_dir != NULL) {
+        /* Get specific application file */
+        file_name = malloc(strlen(config_dir) + strlen(application) + NAME_OVERHEAD);
+        if(file_name == NULL) {
+            error = errno;
+            TRACE_ERROR_NUMBER("Failed to allocate memory for file name", error);
+            /* In case of error when we created collection - delete it */
+            if((error) && (created)) {
+                destroy_collection(*ini_config);
+                *ini_config = (struct collection_item *)(NULL);
+            }
+            destroy_collection(*error_set);
+            *error_set = (struct collection_item *)(NULL);
+            return error;
+        }
+
+        sprintf(file_name,"%s%s%s.conf",config_dir, SLASH, application);
+        TRACE_INFO_STRING("Opening file:", file_name);
+
+        /* Read master file */
+	    error = ini_to_collection(file_name,*ini_config,error_level,pass_specific);
+        free(file_name);
+        if(error) {
+            TRACE_ERROR_NUMBER("Failed to read specific application file", error);
+            /* In case of error when we created collection - delete it */
+            if((error) && (created)) {
+                destroy_collection(*ini_config);
+                *ini_config = (struct collection_item *)(NULL);
+            }
+            /* We do not clear the error_set here */
+            return error;
+        }
+
+        /* Add error results if any to the overarching error collection */
+        if((pass_specific != (struct collection_item **)(NULL)) &&
+           (*pass_specific != (struct collection_item *)(NULL))) {
+            TRACE_INFO_STRING("Process erros resulting from file:", file_name);
+            error = add_collection_to_collection(*error_set,NULL,NULL,*pass_specific,COL_ADD_MODE_EMBED);
+            if(error){
+                if(created) {
+                    destroy_collection(*ini_config);
+                    *ini_config = (struct collection_item *)(NULL);
+                }
+                destroy_collection(*error_set);
+                *error_set = (struct collection_item *)(NULL);
+                TRACE_ERROR_NUMBER("Failed to add error collection to another error collection", error);
+                return error;
+            }
+        }
+    }
+
+    TRACE_FLOW_STRING("config_to_collection", "Exit");
+    return EOK;
+}
+
+/* Reads a line from the file */
+int read_line(FILE *file,char **key,void **value, int *length, int *ext_error)
+{
+
+    char *res = NULL;
+    char buf[BUFFER_SIZE+1];
+    int len = 0;
+    char *buffer = NULL;
+    int i = 0;
+    int status = RET_INVALID;
+    char *eq = NULL;
+
+    TRACE_FLOW_STRING("read_line","Entry");
+
+    *ext_error = 0;
+
+    buffer = buf;
+
+    /* Get data from file */
+    res = fgets(buffer,BUFFER_SIZE,file);
+    if(res == NULL) {
+        TRACE_ERROR_STRING("Read nothing","");
+        return RET_EOF;
+    }
+
+    len = strlen(buffer);
+    if(len == 0) {
+        TRACE_ERROR_STRING("Nothing was read.","");
+        return RET_EMPTY;
+    }
+
+    /* Added \r just in case we deal with Windows in future */
+    if((*(buffer + len - 1) != '\n') && (*(buffer + len - 1) != '\r')) {
+        TRACE_ERROR_STRING("String it too big!","");
+        *ext_error = ERR_LONGDATA;
+        return RET_ERROR;
+    }
+
+    /* Ingnore comments */
+    if((*buffer == ';') || (*buffer == '#')) {
+        TRACE_FLOW_STRING("Comment",buf);
+        return RET_COMMENT;
+    }
+
+    TRACE_INFO_STRING("BUFFER before trimming:",buffer);
+
+    /* Trucate trailing spaces and CRs */
+    while(isspace(*(buffer + len - 1))) {
+        *(buffer + len - 1) = '\0';
+        len--;
+    }
+
+    TRACE_INFO_STRING("BUFFER after trimming trailing spaces:",buffer);
+
+    /* Trucate leading spaces  */
+    while(isspace(*buffer)) {
+        buffer++;
+        len--;
+    }
+
+    TRACE_INFO_STRING("BUFFER after trimming leading spaces:",buffer);
+    TRACE_INFO_NUMBER("BUFFER length:",len);
+
+    /* Empty line */
+    if(len == 0) {
+        TRACE_FLOW_STRING("Empty line",buf);
+        return RET_EMPTY;
+    }
+
+    /* Section */
+    if(*buffer == '[') {
+        if(*(buffer+len-1) != ']') {
+            TRACE_ERROR_STRING("Invalid format for section",buf);
+            *ext_error = ERR_NOCLOSESEC;
+            return RET_ERROR;
+        }
+        buffer++;
+        len--;
+        while(isspace(*(buffer))) {
+            buffer++;
+            len--;
+        }
+        if(len == 0) {
+            TRACE_ERROR_STRING("Invalid format for section",buf);
+            *ext_error = ERR_NOSECTION;
+            return RET_ERROR;
+        }
+
+        *(buffer + len - 1) = '\0';
+        len--;
+        while(isspace(*(buffer + len - 1))) {
+            *(buffer + len - 1) = '\0';
+            len--;
+        }
+        if(len >= MAX_KEY) {
+            TRACE_ERROR_STRING("Section name is too long",buf);
+            *ext_error = ERR_SECTIONLONG;
+            return RET_ERROR;
+        }
+
+        *key = buffer;
+        return RET_SECTION;
+    }
+
+    /* Assume we are dealing with the K-V here */
+    /* Find "=" */
+    eq = strchr(buffer,'=');
+    if(eq == NULL) {
+        TRACE_ERROR_STRING("No equal sign",buf);
+        *ext_error = ERR_NOEQUAL;
+        return RET_BEST_EFFORT;
+    }
+
+    len -= eq-buffer;
+
+    /* Strip spaces around "=" */
+    i = eq - buffer - 1;
+    while((i >= 0) && isspace(*(buffer + i))) i--;
+    if(i<0) {
+        TRACE_ERROR_STRING("No key",buf);
+        *ext_error = ERR_NOKEY;
+        return RET_BEST_EFFORT;
+    }
+
+    /* Copy key into provided buffer */
+    if(i >= MAX_KEY) {
+        TRACE_ERROR_STRING("Section name is too long",buf);
+        *ext_error = ERR_LONGKEY;
+        return RET_BEST_EFFORT;
+    }
+    *key = buffer;
+    *(buffer+i+1) = '\0';
+    TRACE_INFO_STRING("KEY:",*key);
+
+    eq++;
+    len--;
+    while(isspace(*eq)) {
+        eq++;
+        len--;
+    }
+
+    *value = eq;
+    /* Make sure we include trailing 0 into data */
+    *length = len + 1;
+
+    TRACE_INFO_STRING("VALUE:",*value);
+    TRACE_INFO_NUMBER("LENGTH:",*length);
+
+    TRACE_FLOW_STRING("read_line","Exit");
+    return RET_PAIR;
+}
+
+
+/* Print errors and warnings that were detected while parsing one file */
+void print_file_parsing_errors(FILE *file,
+                               struct collection_item *error_list)
+{
+    struct collection_iterator *iterator;
+    int error;
+    struct collection_item *item = (struct collection_item *)(NULL);
+    struct collection_header *header;
+    struct parse_error *pe;
+    int count;
+
+    TRACE_FLOW_STRING("print_file_parsing_errors", "Entry");
+
+    /* If we have something to print print it */
+    if(error_list == (struct collection_item *)(NULL)) {
+        TRACE_ERROR_STRING("No error list","");
+        return;
+    }
+
+    /* Make sure we go the right collection */
+    if(!is_of_class(error_list,COL_CLASS_INI_PERROR)) {
+        TRACE_ERROR_STRING("Wrong collection class:",WRONG_COLLECTION);
+        fprintf(file,"%s\n",WRONG_COLLECTION);
+        return;
+    }
+
+    /* Bind iterator */
+    error =  bind_iterator(&iterator,error_list,COL_TRAVERSE_DEFAULT);
+    if(error) {
+        TRACE_ERROR_STRING("Error (bind):",FAILED_TO_PROCCESS);
+        fprintf(file,"%s\n",FAILED_TO_PROCCESS);
+        return;
+    }
+
+    do {
+        /* Loop through a collection */
+        error = iterate_collection(iterator, &item);
+        if(error) {
+            TRACE_ERROR_STRING("Error (iterate):",FAILED_TO_PROCCESS);
+            fprintf(file,"%s\n",FAILED_TO_PROCCESS);
+            unbind_iterator(iterator);
+            return;
+        }
+
+        /* Are we done ? */
+        if(item == (struct collection_item *)(NULL)) break;
+
+        /* Process collection header */
+        if(get_item_type(item) == COL_TYPE_COLLECTION) {
+            get_collection_count(item, &count);
+            if(count > 1) fprintf(file,ERROR_HEADER,get_item_property(item,NULL));
+            else break;
+        }
+        else {
+            /* Put error into provided format */
+            pe = (struct parse_error *)(get_item_data(item));
+            fprintf(file,LINE_FORMAT,
+                    get_item_property(item,NULL),       /* Error or warning */
+                    pe->error,                          /* Error */
+					pe->line,                           /* Line */
+                    parsing_error_str(pe->error));      /* Error str */
+        }
+
+    }
+    while(1);
+
+    /* Do not forget to unbind iterator - otherwise there will be a leak */
+    unbind_iterator(iterator);
+
+    TRACE_FLOW_STRING("print_file_parsing_errors", "Exit");
+}
+
+
+/* Print errors and warnings that were detected parsing configuration as a whole */
+void print_config_parsing_errors(FILE *file,struct collection_item *error_list)
+{
+    struct collection_iterator *iterator;
+    int error;
+    struct collection_item *item = (struct collection_item *)(NULL);
+    struct collection_item *file_errors = (struct collection_item *)(NULL);
+
+    TRACE_FLOW_STRING("print_config_parsing_errors", "Entry");
+
+    /* If we have something to print print it */
+    if(error_list == (struct collection_item *)(NULL)) {
+        TRACE_ERROR_STRING("No error list","");
+        return;
+    }
+
+    /* Make sure we go the right collection */
+    if(!is_of_class(error_list,COL_CLASS_INI_PESET)) {
+        TRACE_ERROR_STRING("Wrong collection class:",WRONG_COLLECTION);
+        fprintf(file,"%s\n",WRONG_COLLECTION);
+        return;
+    }
+
+    /* Bind iterator */
+    error =  bind_iterator(&iterator,error_list,COL_TRAVERSE_DEFAULT);
+    if(error) {
+        TRACE_ERROR_STRING("Error (bind):",FAILED_TO_PROCCESS);
+        fprintf(file,"%s\n",FAILED_TO_PROCCESS);
+        return;
+    }
+
+    do {
+        /* Loop through a collection */
+        error = iterate_collection(iterator, &item);
+        if(error) {
+            TRACE_ERROR_STRING("Error (iterate):",FAILED_TO_PROCCESS);
+            fprintf(file,"%s\n",FAILED_TO_PROCCESS);
+            unbind_iterator(iterator);
+            return;
+        }
+
+        /* Are we done ? */
+        if(item == (struct collection_item *)(NULL)) break;
+
+        /* Print per file sets of errors */
+        if(get_item_type(item) == COL_TYPE_COLLECTIONREF) {
+            /* Extract a sub collection */
+            error = get_reference_from_item(item,&file_errors);
+            if(error) {
+                TRACE_ERROR_STRING("Error (extract):",FAILED_TO_PROCCESS);
+                fprintf(file,"%s\n",FAILED_TO_PROCCESS);
+                return;
+            }
+            print_file_parsing_errors(file,file_errors);
+            destroy_collection(file_errors);
+        }
+    }
+    while(1);
+
+    /* Do not forget to unbind iterator - otherwise there will be a leak */
+    unbind_iterator(iterator);
+
+    TRACE_FLOW_STRING("print_config_parsing_errors", "Exit");
+}
+
+
+/* Function to get value from the configration handle */
+int get_config_item(const char *section,
+                    const char *name,
+                    struct collection_item *ini_config,
+                    struct collection_item **item)
+{
+    int error = EOK;
+    struct collection_item *section_handle = (struct collection_item *)(NULL);
+    char *to_find;
+    char default_section[] = INI_DEFAULT_SECTION;
+
+    TRACE_FLOW_STRING("get_config_item", "Entry");
+
+    /* Do we have the accepting memory ? */
+    if(item == (struct collection_item **)(NULL)) {
+        TRACE_ERROR_NUMBER("No buffer - invalid argument.", EINVAL);
+        return EINVAL;
+    }
+
+    /* Is the collection of a right type */
+    if(!is_of_class(ini_config,COL_CLASS_INI_CONFIG)) {
+        TRACE_ERROR_NUMBER("Wrong collection type", EINVAL);
+        return EINVAL;
+    }
+
+    *item == (struct collection_item *)(NULL);
+
+    if(section == NULL) to_find = default_section;
+    else to_find = (char *)section;
+
+    TRACE_INFO_STRING("Getting Name:", name);
+    TRACE_INFO_STRING("In Section:", section);
+
+    /* Get Subcollection */
+    error = get_collection_reference(ini_config,&section_handle,to_find);
+    /* Check error */
+    if((error) && (error != ENOENT)) {
+        TRACE_ERROR_NUMBER("Failed to get section", error);
+        return error;
+    }
+
+    /* Did we find a section */
+    if((error == ENOENT) || (section_handle == (struct collection_item *)(NULL))) {
+        /* We have not found section - return success */
+        TRACE_FLOW_STRING("get_value_from_config", "No such section");
+        return EOK;
+    }
+
+    /* Get item */
+    error = get_item(section_handle,(char *)name, COL_TYPE_STRING, COL_TRAVERSE_ONELEVEL, item);
+
+    TRACE_FLOW_NUMBER("get_config_item returning", error);
+    return error;
+}
+
+/* Get long value from config item */
+long get_long_config_value(struct collection_item *item, int strict, long def, int *error)
+{
+    char *endptr, *str;
+    long val = 0;
+
+    TRACE_FLOW_STRING("get_long_config_value", "Entry");
+
+    /* Do we have the item ? */
+    if((item == (struct collection_item *)(NULL)) ||
+       (get_item_type(item) != COL_TYPE_STRING)) {
+        TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+        if(error) *error = EINVAL;
+        return def;
+    }
+
+    if(error) *error = EOK;
+
+    /* Try to parse the value */
+    str = (char *)(get_item_data(item));
+    errno = 0;
+    val = strtol(str, &endptr, 10);
+
+    /* Check for various possible errors */
+    if (((errno == ERANGE) &&
+        ((val == LONG_MAX) ||
+         (val == LONG_MIN))) ||
+         ((errno != 0) && (val == 0)) ||
+         (endptr == str)) {
+        TRACE_ERROR_NUMBER("Conversion failed", EIO);
+        if(error) *error = EIO;
+        return def;
+    }
+
+    if ((strict) && (*endptr != '\0')) {
+        TRACE_ERROR_NUMBER("More characters than expected", EIO);
+        if(error) *error = EIO;
+        val = def;
+    }
+
+    TRACE_FLOW_NUMBER("get_long_config_value returning", val);
+    return val;
+}
+
+/* Get integer value from config item */
+inline int get_int_config_value(struct collection_item *item, int strict, int def, int *error)
+{
+    return (int)get_long_config_value(item, strict, (long)def, error);
+}
+
+/* Get unsigned integer value from config item */
+unsigned get_unsigned_config_value(struct collection_item *item, int strict, unsigned def, int *error)
+{
+    return (unsigned int)get_long_config_value(item, strict, (long)def, error);
+}
+
+/* Get unsigned long value from config item */
+unsigned long get_ulong_config_value(struct collection_item *item, int strict, unsigned long def, int *error)
+{
+    return (unsigned long)get_long_config_value(item, strict, (long)def, error);
+}
+
+/* Get double value */
+double get_double_config_value(struct collection_item *item, int strict, double def, int *error)
+{
+    char *endptr, *str;
+    double val = 0;
+
+    TRACE_FLOW_STRING("get_double_config_value", "Entry");
+
+    /* Do we have the item ? */
+    if((item == (struct collection_item *)(NULL)) ||
+       (get_item_type(item) != COL_TYPE_STRING)) {
+        TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+        if(error) *error = EINVAL;
+        return def;
+    }
+
+    if(error) *error = EOK;
+
+    /* Try to parse the value */
+    str = (char *)(get_item_data(item));
+    errno = 0;
+    val = strtod(str, &endptr);
+
+    /* Check for various possible errors */
+    if ((errno == ERANGE)  ||
+        ((errno != 0) && (val == 0)) ||
+        (endptr == str)) {
+        TRACE_ERROR_NUMBER("Conversion failed", EIO);
+        if(error) *error = EIO;
+        return def;
+    }
+
+    if ((strict) && (*endptr != '\0')) {
+        TRACE_ERROR_NUMBER("More characters than expected", EIO);
+        if(error) *error = EIO;
+        val = def;
+    }
+
+    TRACE_FLOW_NUMBER("get_double_config_value returning", val);
+    return val;
+}
+
+/* Get boolean value */
+unsigned char get_bool_config_value(struct collection_item *item, unsigned char def, int *error)
+{
+    char *str;
+    int len;
+
+    TRACE_FLOW_STRING("get_bool_config_value", "Entry");
+
+    /* Do we have the item ? */
+    if((item == (struct collection_item *)(NULL)) ||
+       (get_item_type(item) != COL_TYPE_STRING)) {
+        TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+        if(error) *error = EINVAL;
+        return def;
+    }
+
+    if(error) *error = EOK;
+
+    str = (char *)(get_item_data(item));
+    len = get_item_length(item);
+
+    /* Try to parse the value */
+    if((strncasecmp(str,"true",len) == 0) ||
+       (strncasecmp(str,"yes",len) == 0)) {
+        TRACE_FLOW_STRING("Returning", "true");
+        return '\1';
+    }
+    else if((strncasecmp(str,"false",len) == 0) ||
+            (strncasecmp(str,"no",len) == 0)) {
+        TRACE_FLOW_STRING("Returning", "false");
+        return '\0';
+    }
+
+    TRACE_ERROR_STRING("Returning", "error");
+    if(error) *error = EIO;
+    return def;
+}
+
+/* Return a string out of the value */
+inline char *get_string_config_value(struct collection_item *item, int dup, int *error)
+{
+    char *str = NULL;
+
+    TRACE_FLOW_STRING("get_string_config_value", "Entry");
+
+    /* Do we have the item ? */
+    if((item == (struct collection_item *)(NULL)) ||
+       (get_item_type(item) != COL_TYPE_STRING)) {
+        TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+        if(error) *error = EINVAL;
+        return NULL;
+    }
+
+    /* If we are told to dup the value */
+    if(dup) {
+        errno = 0;
+        str = strdup((char *)(get_item_data(item)));
+        if(str == NULL) {
+            TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+            if(error) *error = ENOMEM;
+            return NULL;
+        }
+    }
+    else str = (char *)(get_item_data(item));
+
+    if(error) *error = EOK;
+
+    TRACE_FLOW_STRING("get_string_config_value", "Exit");
+    return str;
+}
+
+/* A special hex format is assumed.
+ * The string should be taken in single quotes
+ * and consist of hex encoded value two hex digits per byte.
+ * Example: '0A2BFECC'
+ * Case does not matter.
+ */
+char *get_bin_config_value(struct collection_item *item, int *length, int *error)
+{
+    int i;
+    char *value = NULL;
+    char *buff;
+    int size = 0;
+    unsigned char hex;
+    int len;
+    char *str;
+
+    TRACE_FLOW_STRING("get_bin_config_value", "Entry");
+
+    /* Do we have the item ? */
+    if((item == (struct collection_item *)(NULL)) ||
+       (get_item_type(item) != COL_TYPE_STRING)) {
+        TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+        if(error) *error = EINVAL;
+        return NULL;
+    }
+
+    /* Check the length */
+    len = get_item_length(item)-1;
+    if((len/2) *2 != len) {
+        TRACE_ERROR_STRING("Invalid length for binary data","")
+        if(error) *error = EINVAL;
+        return NULL;
+    }
+
+    str = (char *)(get_item_data(item));
+
+    /* Is the format correct ? */
+    if((*str != '\'') ||
+       (*(str + len -1) != '\'')) {
+        TRACE_ERROR_STRING("String is not escaped","")
+        if(error) *error = EIO;
+        return NULL;
+    }
+
+    /* Check that all the symbols are ok */
+    buff = str + 1;
+    len -= 2;
+    for(i=0;i<len;i+=2) {
+        if((!isxdigit(*(buff+i))) || (!isxdigit(*(buff+i+1)))) {
+            TRACE_ERROR_STRING("Invalid encoding for binary data",buff+i)
+            if(error) *error = EIO;
+            return NULL;
+        }
+    }
+
+    /* The value is good so we can allocate memory for it */
+    value = malloc(len/2);
+    if(value == NULL) {
+        TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+        if(error) *error = ENOMEM;
+        return NULL;
+    }
+
+    /* Convert the value */
+    for(i=0;i<len;i+=2) {
+        if(isdigit(*(buff+i))) {
+            if(isdigit(*(buff+i+1))) hex = 16 * (*(buff+i) - '0') + (*(buff+i+1) - '0');
+            else hex = 16 * (*(buff+i) - '0') + (tolower(*(buff+i+1)) - 'a' + 10);
+        }
+        else {
+            if(isdigit(*(buff+i+1))) hex = 16 * (tolower(*(buff+i)) - 'a') + (*(buff+i+1) - '0');
+            else hex = 16 * (tolower(*(buff+i)) - 'a' + 10) + (tolower(*(buff+i+1)) - 'a' + 10);
+        }
+
+        *(value+size) = (char)(hex);
+        size++;
+    }
+
+    if(error) *error = EOK;
+    if(length) *length = size;
+    TRACE_FLOW_STRING("get_bin_config_value", "Exit");
+    return value;
+}
+
+/* Function to free binary configuration value */
+inline void free_bin_config_value(char *value)
+{
+    if(value) free(value);
+}
+
+/* Arrays of stings and integers */
+char **get_string_config_array(struct collection_item *item, char *sep, int *size, int *error)
+{
+    char defsep[] = ",";
+    char *copy = NULL;
+    char *dest = NULL;
+    int total = 0;
+    int lensep;
+    char *buff;
+    int count = 0;
+    int len = 0;
+    int resume_len;
+    char **array;
+    char *start;
+    int i,j,k;
+    int growlen = 0;
+
+    TRACE_FLOW_STRING("get_string_config_array", "Entry");
+
+    /* Do we have the item ? */
+    if((item == (struct collection_item *)(NULL)) ||
+       (get_item_type(item) != COL_TYPE_STRING)) {
+        TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+        if(error) *error = EINVAL;
+        return NULL;
+    }
+
+    /* Handle the separators */
+    if(sep == NULL) sep = defsep;
+    lensep = strnlen(sep,3);
+
+    /* Allocate memory for the copy of the string */
+    copy = malloc(get_item_length(item));
+    if(copy == NULL) {
+        TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+        if(error) *error = ENOMEM;
+        return NULL;
+    }
+
+    /* Loop through the string */
+    dest = copy;
+    buff = item->data;
+    start = buff;
+    for(i=0;i<item->length;i++) {
+        growlen = 1;
+        for(j=0;j<lensep;j++) {
+            if(*(buff+i) == *(sep+j)) {
+                /* If we found one of the separators trim spaces around */
+                resume_len = len;
+                while(len > 0) {
+                    if(isspace(*(start+len - 1))) len--;
+                    else break;
+                }
+                if(len > 0) {
+                    /* Save block aside */
+                    memcpy(dest,start,len);
+                    count++;
+                    dest+=len;
+                    *dest='\0';
+                    dest++;
+                    len = 0;
+                    /* Move forward and trim spaces if any */
+                    start += resume_len + 1;
+                    i++;
+                    TRACE_INFO_STRING("Remaining buffer :", start);
+                    TRACE_INFO_STRING("Other pointer :", buff + i);
+                    k = 0;
+                    while(((i+k) < item->length) && (isspace(*start))) {
+                        k++;
+                        start++;
+                    }
+                    TRACE_INFO_STRING("Remaining buffer after triming spaces:", start);
+                    if(k) i+=k-1;
+                    /* Next iteration of the loop will add 1 */
+                }
+                /* Break out of the inner loop */
+                growlen = 0;
+                break;
+            }
+        }
+        if(growlen) len++;
+    }
+
+    /* Copy the remaining piece */
+    memcpy(dest,start,len);
+    count++;
+    dest+=len;
+    dest='\0';
+    dest++;
+
+    /* Now we know how many items are there in the list */
+    array = malloc((count + 1) * sizeof(char *));
+    if(array == NULL) {
+        free(copy);
+        TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+        if(error) *error = ENOMEM;
+        return NULL;
+    }
+
+    /* Loop again to fill in the pointers */
+    start = copy;
+    for(i=0;i<count;i++) {
+        *(array+i) = start;
+        while(*start!='\0') start++;
+        start++;
+    }
+    *(array+count) = NULL;
+
+    if(error) *error = EOK;
+    if(size) *size = count;
+    TRACE_FLOW_STRING("get_string_config_array", "Exit");
+    return array;
+}
+
+/* Special function to free string config array */
+void free_string_config_array(char **str_config)
+{
+    TRACE_FLOW_STRING("free_string_config_array", "Entry");
+
+    if(str_config != NULL) {
+        if(*str_config !=NULL) free(*str_config);
+        free(str_config);
+    }
+
+    TRACE_FLOW_STRING("free_string_config_array", "Exit");
+}
+
+/* Get an array of long values */
+long *get_long_config_array(struct collection_item *item, int *size, int *error)
+{
+    char *endptr, *str;
+    long val = 0;
+    long *array;
+    int count = 0;
+
+    TRACE_FLOW_STRING("get_long_config_array", "Entry");
+
+    /* Do we have the item ? */
+    if((item == (struct collection_item *)(NULL)) ||
+       (get_item_type(item) != COL_TYPE_STRING) ||
+       (size == (int *)(NULL))) {
+        TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+        if(error) *error = EINVAL;
+        return (long *)(NULL);
+    }
+
+    /* Assume that we have maximum number of different numbers */
+    array = (long *)malloc(sizeof(long) * get_item_length(item)/2);
+    if(array == NULL) {
+        TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+        if(error) *error = ENOMEM;
+        return (long *)(NULL);
+    }
+
+    /* Now parse the string */
+    str = (char *)(get_item_data(item));
+    while(*str != '\0') {
+        errno = 0;
+        val = strtol(str, &endptr, 10);
+        if (((errno == ERANGE) &&
+            ((val == LONG_MAX) ||
+             (val == LONG_MIN))) ||
+             ((errno != 0) && (val == 0)) ||
+             (endptr == str)) {
+            TRACE_ERROR_NUMBER("Conversion failed", EIO);
+            free(array);
+            if(error) *error = EIO;
+            return (long *)(NULL);
+        }
+        /* Save value */
+        *(array + count) = val;
+        count++;
+        /* Are we done? */
+        if(*endptr == 0) break;
+        /* Advance to the next valid number */
+        str = endptr;
+        while(!isdigit(*str) && (*str != 0) && (*str != '-') && (*str != '+')) str++;
+    }
+
+    *size = count;
+    if(error) *error = EOK;
+
+    TRACE_FLOW_NUMBER("get_long_config_value returning", val);
+    return array;
+
+}
+
+/* Special function to free long config array */
+inline void free_long_config_array(long *array)
+{
+    if(array != (long *)(NULL)) free(array);
+}
+
diff --git a/common/ini/ini_config.h b/common/ini/ini_config.h
new file mode 100644
index 0000000..8bf7373
--- /dev/null
+++ b/common/ini/ini_config.h
@@ -0,0 +1,160 @@
+/*
+    INI LIBRARY
+
+    Header file for reading configuration from INI file
+    and storing as a collection.
+
+    Copyright (C) Dmitri Pal <dpal@redhat.com> 2009
+
+    INI Library is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    INI Library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with INI Library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef INI_CONFIG_H
+#define INI_CONFIG_H
+
+#include <limits.h>
+#include "collection.h"
+
+/* Name of the default (missing section in the INI file */
+#define INI_DEFAULT_SECTION "default"
+
+/* Collection classes used in INI processing */
+#define COL_CLASS_INI_BASE        20000
+#define COL_CLASS_INI_CONFIG      COL_CLASS_INI_BASE + 0 /* Class for configuration collection. Implies a collection of sections */
+#define COL_CLASS_INI_SECTION     COL_CLASS_INI_BASE + 1 /* A one level collection of key value pairs where values are always stings */
+#define COL_CLASS_INI_PERROR      COL_CLASS_INI_BASE + 2 /* A one level collection of parse errors - store parse_error structs */
+#define COL_CLASS_INI_PESET       COL_CLASS_INI_BASE + 3 /* A one level collection of parse error collections */
+
+
+/* Error levels */
+#define INI_STOP_ON_ANY     0   /* Fail if any problem is detected */
+#define INI_STOP_ON_NONE    1   /* Best effort - do not fail */
+#define INI_STOP_ON_ERROR   2   /* Fail on errors only */
+
+
+/* Parsing errors and warnings */
+#define ERR_LONGDATA        1   /* Error */
+#define ERR_NOCLOSESEC      2   /* Error */
+#define ERR_NOSECTION       3   /* Error */
+#define ERR_SECTIONLONG     4   /* Error */
+#define ERR_NOEQUAL         5   /* Warning */
+#define ERR_NOKEY           6   /* Warning */
+#define ERR_LONGKEY         7   /* Warning */
+
+#define ERR_MAXPARSE        ERR_LONGKEY
+
+#ifdef HAVE_INI_BEST_EFFORT /* Ignore bad lines in the INI files */
+#define RET_BEST_EFFORT RET_INVALID
+#else
+#define RET_BEST_EFFORT RET_ERROR
+#endif
+
+/* Internal sizes */
+/* FIXME - make them configurable via config.h */
+#define MAX_KEY         1024
+#define MAX_VALUE       PATH_MAX
+#define BUFFER_SIZE     MAX_KEY + MAX_VALUE + 3
+
+struct parse_error {
+    unsigned line;
+    int error;
+};
+
+/* Function to return parsing error */
+const char *parsing_error_str(int parsing_error);
+
+/* Read configuration information from a file */
+int config_from_file(const char *application,               /* Name of the application - will be used as name of the collection */
+                     const char *config_file,               /* Name of the config file - if NULL the collection will be empty */
+                     struct collection_item **ini_config,   /* If *ini_config is NULL a new ini object will be allocated, */
+                                                            /* otherwise the one that is pointed to will be updated. */
+                     int error_level,                       /* Error level - break for erros, warnings or best effort (don't break) */
+                     struct collection_item **error_list);  /* List of errors for a file */
+
+
+/* Read default config file and then overwrite it with a specific one from the directory */
+int config_for_app(const char *application,               /* Name of the application that will be used to get config for */
+                   const char *config_file,               /* Name of the configuration file with default settings for all apps */
+                   const char *config_dir,                /* Name of the directory where the configuration files for different apps will be dropped */
+                   struct collection_item **ini_config,   /* New config object */
+                   int error_level,                       /* Level of error tolerance */
+                   struct collection_item **error_set);   /* Collection of collections of parsing errors */
+
+/* Print errors and warnings that were detected while parsing one file */
+/* Use this function to print results of the config_from_file() call */
+void print_file_parsing_errors(FILE *file,                           /* File to send errors to */
+                               struct collection_item *error_list);  /* List of parsing errors */
+
+/* Print errors and warnings that were detected parsing configuration as a whole */
+/* Use this function to print results of the config_for_app() call */
+void print_config_parsing_errors(FILE *file,                           /* File to send errors to */
+                                 struct collection_item *error_list);  /* Collection of collections of errors */
+
+/* Get a configuration item form the configuration */
+int get_config_item(const char *section,                    /* Section. If NULL assumed default */
+                    const char *name,                       /* Name of the property to look up */
+                    struct collection_item *ini_config,     /* Collection to search */
+                    struct collection_item **item);         /* Item returned. Will be NULL is not found. /
+
+/* Convertion functions for the configuration item.
+ * Sets error to EINVAL if the item is bad.
+ * Sets error to EIO if the conversion failed.
+ * These functions do not allocate memory.
+ * They always return best effort conversion value.
+ * In case of error they return provided default.
+ * It is up to the caller to check an error and take an action.
+ */
+/* If "strict" parameter is non zero the function will fail if there are more
+ * characters after last digit.
+ */
+int get_int_config_value(struct collection_item *item, int strict, int def, int *error);
+long get_long_config_value(struct collection_item *item, int strict, long def, int *error);
+unsigned get_unsigned_config_value(struct collection_item *item, int strict, unsigned def, int *error);
+unsigned long get_ulong_config_value(struct collection_item *item, int strict, unsigned long def, int *error);
+double get_double_config_value(struct collection_item *item, int strict, double def, int *error);
+unsigned char get_bool_config_value(struct collection_item *item, unsigned char def, int *error);
+
+/* Function get_string_config_value returns pointer to the string out of item.
+ * If 'dup' is not 0 it makes a copyof the string otherwise it does not.
+ */
+char *get_string_config_value(struct collection_item *item, int dup, int *error);
+
+/* A get_bin_value and get_xxx_array functions allocate memory.
+ * It is the responsibility of the caller to free it after use.
+ * free_xxx conviniece wrappers are provided for this purpose.
+ * Functions will return NULL if conversion failed.
+ */
+/* A special hex format is assumed.
+ * The string should be taken in single quotes
+ * and consist of hex encoded value two hex digits per byte.
+ * Example: '0A2BFECC'
+ * Case does not matter.
+ */
+char *get_bin_config_value(struct collection_item *item, int *length, int *error);
+void free_bin_config_value(char *);
+
+/* Array of stings */
+/* Separator sting includes up to three different separators. If NULL comma is assumed. */
+/* The spaces are trimmed automatically around separators in the string. */
+char **get_string_config_array(struct collection_item *item, char *sep, int *size, int *error);
+/* Array of long values - separators are detected automatically. */
+/* The length of the allocated array is returned in "size" */
+long *get_long_config_array(struct collection_item *item, int *size, int *error);
+
+/* Special function to free string config array */
+void free_string_config_array(char **str_config);
+/* Special function to free long config array */
+void free_long_config_array(long *array);
+
+#endif
diff --git a/common/ini/ini_config_ut.c b/common/ini/ini_config_ut.c
new file mode 100644
index 0000000..2bb5517
--- /dev/null
+++ b/common/ini/ini_config_ut.c
@@ -0,0 +1,701 @@
+/*
+    INI LIBRARY
+
+    Unit test for the INI library.
+
+    Copyright (C) Dmitri Pal <dpal@redhat.com> 2009
+
+    INI Library is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    INI Library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with INI Library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <errno.h>
+#define TRACE_HOME
+#include "ini_config.h"
+#include "collection.h"
+#include "collection_tools.h"
+
+
+int basic_test()
+{
+    int error;
+    struct collection_item *ini_config = (struct collection_item *)(NULL);
+    struct collection_item *error_set = (struct collection_item *)(NULL);
+
+    error = config_for_app("test", "./ini/ini.conf", "./ini/ini.d", &ini_config,INI_STOP_ON_NONE,&error_set);
+    if(error) {
+        printf("Attempt to read configuration returned error: %d\n",error);
+        return error;
+    }
+
+    debug_collection(ini_config,COL_TRAVERSE_DEFAULT);
+    print_collection(ini_config);
+    print_collection(error_set);
+
+	printf("\n\n----------------------\n");
+    /* Output parsing errors (if any) */
+    print_config_parsing_errors(stdout,error_set);
+	printf("----------------------\n\n\n");
+
+
+    destroy_collection(ini_config);
+    destroy_collection(error_set);
+    return 0;
+}
+
+int single_file()
+{
+    int error;
+    struct collection_item *ini_config = (struct collection_item *)(NULL);
+    struct collection_item *error_set = (struct collection_item *)(NULL);
+
+    error = config_from_file("test", "./ini/ini.conf", &ini_config,INI_STOP_ON_NONE,&error_set);
+    if(error) {
+        printf("Attempt to read configuration returned error: %d\n",error);
+        return error;
+    }
+
+    debug_collection(ini_config,COL_TRAVERSE_DEFAULT);
+    print_collection(ini_config);
+    print_collection(error_set);
+
+	printf("\n\n----------------------\n");
+    /* Output parsing errors (if any) */
+    print_file_parsing_errors(stdout,error_set);
+	printf("----------------------\n\n\n");
+
+
+    destroy_collection(ini_config);
+    destroy_collection(error_set);
+    return 0;
+}
+
+int negative_test()
+{
+    int error;
+    int count;
+    struct collection_item *ini_config = (struct collection_item *)(NULL);
+    struct collection_item *error_set = (struct collection_item *)(NULL);
+
+    /* App name is null - expect failure */
+    error = config_for_app(NULL, NULL, NULL, NULL,INI_STOP_ON_NONE,NULL);
+    if(!error) {
+        printf("Expected error: %d got success\n",EINVAL);
+        return -1;
+    }
+
+    /* Config collection storage is NULL - expect failure */
+    error = config_for_app("real", NULL, NULL, NULL,INI_STOP_ON_NONE,NULL);
+    if(!error) {
+        printf("Expected error: %d got success\n",EINVAL);
+        return -1;
+    }
+
+    /* Config collection storage is NULL - expect failure */
+    error = config_for_app("real", "real.conf", NULL, NULL,INI_STOP_ON_NONE,NULL);
+    if(!error) {
+        printf("Expected error: %d got success\n",EINVAL);
+        return -1;
+    }
+
+    /* Expect success but empty config */
+    error = config_for_app("real", "real.conf", NULL, &ini_config,INI_STOP_ON_NONE,NULL);
+    if(error) {
+        printf("Expected success got error: %d\n",error);
+        return error;
+    }
+
+    count = 0;
+    (void)get_collection_count(ini_config,&count);
+    if(count > 1) {
+        printf("Expected empty collection but got contents with %d elements\n",count);
+        print_collection(ini_config);
+        return -1;
+    }
+
+    destroy_collection(ini_config);
+    return 0;
+
+}
+
+int real_test(const char *file)
+{
+    int error;
+    struct collection_item *ini_config = (struct collection_item *)(NULL);
+    struct collection_item *error_set = (struct collection_item *)(NULL);
+    struct collection_iterator *iterator = (struct collection_iterator *)(NULL);
+    struct collection_item *item = (struct collection_item *)(NULL);
+    int type;
+
+	printf("\n\n===== REAL TEST START ======\n");
+	printf("Reading collection\n");
+    error = config_for_app("real", file, "./ini/ini.d", &ini_config,INI_STOP_ON_NONE,&error_set);
+    if(error) {
+        printf("Attempt to read configuration returned error: %d\n",error);
+        return error;
+    }
+
+	printf("Debugging the config collection:\n");
+    debug_collection(ini_config,COL_TRAVERSE_DEFAULT);
+	printf("Debugging the error collection:\n");
+    debug_collection(error_set,COL_TRAVERSE_DEFAULT);
+
+	printf("About to print parsing errors:\n");
+	printf("\n\n----------------------\n");
+    /* Output parsing errors (if any) */
+    print_config_parsing_errors(stdout,error_set);
+	printf("----------------------\n\n\n");
+
+	printf("About to bind iterator to print the config file contents.\n");
+    /* Bind iterator */
+    error =  bind_iterator(&iterator,ini_config,COL_TRAVERSE_DEFAULT|COL_TRAVERSE_END);
+    if(error) {
+        printf("Failed to bind iterator: %d\n",error);
+        destroy_collection(ini_config);
+        destroy_collection(error_set);
+        return error;
+    }
+
+	printf("About to start iteration loop.\n");
+    do {
+        /* Loop through a collection */
+        error = iterate_collection(iterator, &item);
+        if(error) {
+            printf("Error iterating collection: %d",error);
+            unbind_iterator(iterator);
+            return error;
+        }
+
+        /* Are we done ? */
+        if(item == (struct collection_item *)(NULL)) break;
+
+        type = get_item_type(item);
+
+        /* Start of the collection */
+        if(type == COL_TYPE_COLLECTION)
+            printf("Contents of the configuration for application %s\n",get_item_property(item,NULL));
+        /* End of section */
+        else if(type == COL_TYPE_END) printf("\n");
+        /* Section header ? */
+        else if(type == COL_TYPE_COLLECTIONREF) printf("[%s]\n",get_item_property(item,NULL));
+        /* Anything else - we know they are all strings*/
+        else printf("%s = %s\n",get_item_property(item,NULL), (char *)get_item_data(item));
+    }
+    while(1);
+
+    /* Do not forget to unbind iterator - otherwise there will be a leak */
+	printf("About to clean up.\n");
+    unbind_iterator(iterator);
+
+    destroy_collection(ini_config);
+    destroy_collection(error_set);
+    return 0;
+}
+
+int get_test()
+{
+
+    int error;
+    struct collection_item *ini_config = (struct collection_item *)(NULL);
+    struct collection_item *error_set = (struct collection_item *)(NULL);
+    struct collection_iterator *iterator = (struct collection_iterator *)(NULL);
+    struct collection_item *item = (struct collection_item *)(NULL);
+    int type;
+    int number;
+    long number_long;
+    double number_double;
+    unsigned number_unsigned;
+    unsigned long number_ulong;
+    unsigned char logical;
+    char *str;
+    void *binary;
+    int length;
+    int i;
+	char **strarray;
+    char **strptr;
+    int size;
+    long *array;
+
+	printf("\n\n===== GET TEST START ======\n");
+	printf("Reading collection\n");
+    error = config_for_app("real", NULL, "./ini/ini.d", &ini_config,INI_STOP_ON_NONE,&error_set);
+    if(error) {
+        printf("Attempt to read configuration returned error: %d\n",error);
+        return error;
+    }
+
+	printf("Debugging the config collection:\n");
+    debug_collection(ini_config,COL_TRAVERSE_DEFAULT);
+	printf("Debugging the error collection:\n");
+    debug_collection(error_set,COL_TRAVERSE_DEFAULT);
+    destroy_collection(error_set);
+
+    printf("Negtive test - trying to get non existing key-value pair.\n");
+
+    /* Negative test */
+    item = (struct collection_item *)(NULL);
+    error = get_config_item("monitor1","description1", ini_config, &item);
+    if(error) {
+        printf("Expected success but got error! %d\n",error);
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    /* Item should not be found */
+    if(item != (struct collection_item *)(NULL)) {
+        printf("Expected NULL but got something else!\n");
+        destroy_collection(ini_config);
+        return -1;
+    }
+
+    /* Another negative test but section exists this time */
+    item = (struct collection_item *)(NULL);
+    error = get_config_item("monitor","description1", ini_config, &item);
+    if(error) {
+        printf("Expected success but got error! %d\n",error);
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    /* Item should not be found */
+    if(item != (struct collection_item *)(NULL)) {
+        printf("Expected NULL but got something else!\n");
+        destroy_collection(ini_config);
+        return -1;
+    }
+
+    printf("Trying to get an item.\n");
+
+    /* Positive test */
+    item = (struct collection_item *)(NULL);
+    error = get_config_item("monitor","description", ini_config, &item);
+    if(error) {
+        printf("Expected success but got error! %d\n",error);
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    /* Item should be found */
+    if(item == (struct collection_item *)(NULL)) {
+        printf("Expected item but got something NULL!\n");
+        destroy_collection(ini_config);
+        return -1;
+    }
+
+    debug_item(item);
+
+
+    printf("Get item as string without duplication from NULL item.\n");
+
+    /* Get a string without duplicication */
+    /* Negative test */
+    error = 0;
+    str = get_string_config_value(NULL, 0, &error);
+    if(!error) {
+        printf("Expected error got success.\n");
+        destroy_collection(ini_config);
+        return -1;
+    }
+
+    printf("Get item as string without duplication from correct item.\n");
+
+    /* Now get string from the right item */
+    error = 0;
+    str = get_string_config_value(item, 0, &error);
+    if(error) {
+        printf("Expected success got error %d.\n",error);
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    printf("Value: [%s]\n",str);
+
+    /* Same thing but create a dup */
+
+    printf("Get item as string with duplication from correct item.\n");
+
+    error = 0;
+    str = get_string_config_value(item, 1, &error);
+    if(error) {
+        printf("Expected success got error %d.\n",error);
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    printf("Value: [%s]\n",str);
+    free(str);
+
+
+    /* Get a badly formated number */
+    printf("Convert item to number with strict conversion.\n");
+
+    item = (struct collection_item *)(NULL);
+    error = get_config_item("monitor","bad_number", ini_config, &item);
+    if(error) {
+        printf("Expected success but got error! %d\n",error);
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    /* Item should be found */
+    if(item == (struct collection_item *)(NULL)) {
+        printf("Expected item but got something NULL!\n");
+        destroy_collection(ini_config);
+        return -1;
+    }
+
+    debug_item(item);
+
+
+    /* Now try to get value in different ways */
+    error = 0;
+    number = get_int_config_value(item, 1, 10, &error);
+    if(error) {
+        /* We expected error in this case */
+        printf("Expected error.\n");
+        if(number != 10) {
+            printf("It failed to set default value.\n");
+            destroy_collection(ini_config);
+            return -1;
+        }
+    }
+
+    printf("Convert item to number without strict conversion.\n");
+
+    error = 0;
+    number = 1;
+    number = get_int_config_value(item, 0, 10, &error);
+    if(error) {
+        /* We expected error in this case */
+        printf("Did not expect error.\n");
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    if(number != 5) {
+        /* We expected error in this case */
+        printf("We expected that the conversion will return 5.\n");
+        destroy_collection(ini_config);
+        return -1;
+    }
+
+
+
+    /* Get real integer */
+
+    printf("Fetch another item from section \"domains/LOCAL\" named \"enumerate\".\n");
+
+    item = (struct collection_item *)(NULL);
+    error = get_config_item("domains/LOCAL","enumerate", ini_config, &item);
+    if(error) {
+        printf("Expected success but got error! %d\n",error);
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    /* Item should be found */
+    if(item == (struct collection_item *)(NULL)) {
+        printf("Expected success but got NULL.\n");
+        destroy_collection(ini_config);
+        return -1;
+    }
+
+    printf("Convert item to integer.\n");
+
+    /* Take number out of it */
+    error = 0;
+    number = get_int_config_value(item, 1, 100, &error);
+    if(error) {
+        printf("Did not expect error. Got %d\n",error);
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    /* It is 3 in the file */
+    if(number != 3) {
+        printf("We expected that the conversion will return 3.\n");
+        destroy_collection(ini_config);
+        return -1;
+    }
+
+    printf("Expected 3 got %d\n", number);
+
+    printf("Convert item to long.\n");
+
+    /* Take number out of it */
+    error = 0;
+    number_long = get_long_config_value(item, 1, 100, &error);
+    if(error) {
+        printf("Did not expect error. Got %d\n",error);
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    /* It is 3 in the file */
+    if(number_long != 3) {
+        printf("We expected that the conversion will return 3.\n");
+        destroy_collection(ini_config);
+        return -1;
+    }
+
+    printf("Expected 3 got %d\n", number_long);
+
+    printf("Convert item to unsigned.\n");
+
+    /* Take number out of it */
+    error = 0;
+    number_unsigned = get_unsigned_config_value(item, 1, 100, &error);
+    if(error) {
+        printf("Did not expect error. Got %d\n",error);
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    /* It is 3 in the file */
+    if(number_unsigned != 3) {
+        printf("We expected that the conversion will return 3.\n");
+        destroy_collection(ini_config);
+        return -1;
+    }
+
+    printf("Expected 3 got %d\n", number_unsigned);
+
+    printf("Convert item to unsigned long.\n");
+
+    /* Take number out of it */
+    error = 0;
+    number_ulong = get_ulong_config_value(item, 1, 100, &error);
+    if(error) {
+        printf("Did not expect error. Got %d\n",error);
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    /* It is 3 in the file */
+    if(number_ulong != 3) {
+        printf("We expected that the conversion will return 3.\n");
+        destroy_collection(ini_config);
+        return -1;
+    }
+
+    printf("Expected 3 got %d\n", number_ulong);
+
+    printf("Convert item to double.\n");
+
+    /* Take number out of it */
+    error = 0;
+    number_double = get_double_config_value(item, 1, 100., &error);
+    if(error) {
+        printf("Did not expect error. Got %d\n",error);
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    /* It is 3 in the file */
+    if(number_double != 3.) {
+        printf("We expected that the conversion will return 3.\n");
+        destroy_collection(ini_config);
+        return -1;
+    }
+
+    printf("Expected 3 got %d\n", number_double);
+
+    printf("Convert item to bool.\n");
+
+    /* Take number out of it */
+    error = 0;
+    logical = get_bool_config_value(item, 1, &error);
+    if(!error) {
+        printf("Expect error. Got success.\n");
+        destroy_collection(ini_config);
+        return -1;
+    }
+
+    /* Get real bool item and convert it */
+    printf("Get real bool item \"legacy\" and convert it.\n");
+
+    item = (struct collection_item *)(NULL);
+    error = get_config_item("domains/LOCAL","legacy", ini_config, &item);
+    if(error) {
+        printf("Expected success but got error! %d\n",error);
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    /* Item should be found */
+    if(item == (struct collection_item *)(NULL)) {
+        printf("Expected success but got NULL.\n");
+        destroy_collection(ini_config);
+        return -1;
+    }
+
+    printf("Convert item to bool.\n");
+
+    error = 0;
+    logical = get_bool_config_value(item, 1, &error);
+    if(error) {
+        printf("Expect success got error %d.\n",error);
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    if(logical) {
+        printf("Expected false but got true - bad.\n");
+        return -1;
+    }
+
+    printf("In the files it is FALSE so we got false.\n");
+
+    printf("Get binary item\n");
+
+    item = (struct collection_item *)(NULL);
+    error = get_config_item("domains/EXAMPLE.COM","binary_test", ini_config, &item);
+    if(error) {
+        printf("Expected success but got error! %d\n",error);
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    /* Item should be found */
+    if(item == (struct collection_item *)(NULL)) {
+        printf("Expected success but got NULL.\n");
+        destroy_collection(ini_config);
+        return -1;
+    }
+
+    debug_item(item);
+
+    error = 0;
+    binary = get_bin_config_value(item, &length, &error);
+    if(error) {
+        printf("Expect success got error %d.\n",error);
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    printf("Binary value (expect 123) = ");
+    for(i=0;i<length;i++) {
+        printf("%d",*((unsigned char*)(binary)+i));
+    }
+    printf("\n");
+
+    free_bin_config_value(binary);
+
+    printf("Get string array item\n");
+
+    item = (struct collection_item *)(NULL);
+    error = get_config_item("domains","domainsorder", ini_config, &item);
+    if(error) {
+        printf("Expected success but got error! %d\n",error);
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    /* Item should be found */
+    if(item == (struct collection_item *)(NULL)) {
+        printf("Expected success but got NULL.\n");
+        destroy_collection(ini_config);
+        return -1;
+    }
+
+    debug_item(item);
+
+    printf("Get str array without size.\n");
+
+    error = 0;
+    strarray = get_string_config_array(item, ",", NULL, &error);
+    if(error) {
+        printf("Expect success got error %d.\n",error);
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    /* Can be used with this cycle */
+    strptr = strarray;
+    while(*strptr != NULL) {
+        printf("[%s]\n",*strptr);
+        strptr++;
+    }
+
+    free_string_config_array(strarray);
+
+    printf("Get str array with size.\n");
+
+    error = 0;
+    size = 0;
+    strarray = get_string_config_array(item, ",", &size, &error);
+    if(error) {
+        printf("Expect success got error %d.\n",error);
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    /* Can be used with this cycle */
+    for(i=0;i<size;i++) printf("[%s]\n",*(strarray + i));
+
+    free_string_config_array(strarray);
+
+    printf("Get long array item\n");
+
+    item = (struct collection_item *)(NULL);
+    error = get_config_item("domains/EXAMPLE.COM","long_array", ini_config, &item);
+    if(error) {
+        printf("Expected success but got error! %d\n",error);
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    /* Item should be found */
+    if(item == (struct collection_item *)(NULL)) {
+        printf("Expected success but got NULL.\n");
+        destroy_collection(ini_config);
+        return -1;
+    }
+
+    debug_item(item);
+
+    error = 0;
+    size = 0; /* Here size is not optional!!! */
+    array = get_long_config_array(item, &size, &error);
+    if(error) {
+        printf("Expect success got error %d.\n",error);
+        destroy_collection(ini_config);
+        return error;
+    }
+
+    /* Can be used with this cycle */
+    for(i=0;i<size;i++) printf("%d\n",*(array + i));
+
+    free_long_config_array(array);
+
+    printf("Done with get test!\n");
+    return EOK;
+}
+
+int main()
+{
+    int error;
+
+    if((error=basic_test()) ||
+       (error=single_file()) ||
+       (error=real_test(NULL)) ||
+		/* This should result in merged configuration */
+       (error=real_test("./ini/ini.conf")) ||
+       (error= get_test())) {
+        printf("Test failed! Error %d.\n",error);
+        return -1;
+    }
+    printf("Success!\n");
+    return 0;
+}
-- 
1.6.0.6

