From 742fed5063da5a95e335c27d5789e8eacf83ce1a Mon Sep 17 00:00:00 2001
From: armatusmiles <akurotych@gmail.com>
Date: Tue, 10 Jul 2018 23:11:20 +0300
Subject: [PATCH] Add http_client and core_client

---
 core_server/CMakeLists.txt                    |   10 +-
 core_server/client/(DELETE)CMakeLists.txt     |   26 +
 core_server/client/client.pri                 |   15 +
 core_server/client/dap_client.c               |  409 +++
 core_server/client/dap_client.h               |  111 +
 core_server/client/dap_client_internal.c      |  722 ++++++
 core_server/client/dap_client_internal.h      |   73 +
 core_server/client/dap_client_remote.c        |  266 ++
 core_server/client/dap_client_remote.h        |   87 +
 core_server/client/sxmlc/sxmlc.c              | 2291 +++++++++++++++++
 core_server/client/sxmlc/sxmlc.h              |  829 ++++++
 core_server/client/sxmlc/sxmlsearch.c         |  639 +++++
 core_server/client/sxmlc/sxmlsearch.h         |  232 ++
 core_server/dap_server.h                      |    2 +-
 enc_server/CMakeLists.txt                     |    2 +-
 enc_server/dap_enc_http.c                     |    2 +-
 enc_server/dap_enc_ks.c                       |    4 +-
 http_server/CMakeLists.txt                    |   13 +-
 http_server/dap_http.c                        |    2 +-
 http_server/dap_http.h                        |    8 +-
 http_server/dap_http_simple.c                 |    4 +-
 http_server/http_client/CMakeLists.txt        |   18 +
 http_server/http_client/dap_http_client.c     |  446 ++++
 http_server/http_client/dap_http_client.h     |  102 +
 .../http_client/dap_http_client_simple.c      |  326 +++
 .../http_client/dap_http_client_simple.h      |   20 +
 http_server/http_client/dap_http_header.c     |  193 ++
 http_server/http_client/dap_http_header.h     |   49 +
 http_server/http_client/http.pri              |   15 +
 udp_server/dap_udp_client.h                   |    4 +-
 udp_server/dap_udp_server.h                   |    2 +-
 31 files changed, 6901 insertions(+), 21 deletions(-)
 create mode 100644 core_server/client/(DELETE)CMakeLists.txt
 create mode 100644 core_server/client/client.pri
 create mode 100644 core_server/client/dap_client.c
 create mode 100644 core_server/client/dap_client.h
 create mode 100644 core_server/client/dap_client_internal.c
 create mode 100644 core_server/client/dap_client_internal.h
 create mode 100644 core_server/client/dap_client_remote.c
 create mode 100644 core_server/client/dap_client_remote.h
 create mode 100755 core_server/client/sxmlc/sxmlc.c
 create mode 100755 core_server/client/sxmlc/sxmlc.h
 create mode 100755 core_server/client/sxmlc/sxmlsearch.c
 create mode 100755 core_server/client/sxmlc/sxmlsearch.h
 create mode 100644 http_server/http_client/CMakeLists.txt
 create mode 100644 http_server/http_client/dap_http_client.c
 create mode 100644 http_server/http_client/dap_http_client.h
 create mode 100644 http_server/http_client/dap_http_client_simple.c
 create mode 100644 http_server/http_client/dap_http_client_simple.h
 create mode 100644 http_server/http_client/dap_http_header.c
 create mode 100644 http_server/http_client/dap_http_header.h
 create mode 100644 http_server/http_client/http.pri

diff --git a/core_server/CMakeLists.txt b/core_server/CMakeLists.txt
index 863b482..770c602 100644
--- a/core_server/CMakeLists.txt
+++ b/core_server/CMakeLists.txt
@@ -1,22 +1,24 @@
 cmake_minimum_required(VERSION 2.8)
 project (dap_core_server C)
   
-set(DAP_CORE_SERVER_SRCS  dap_server.c  )
+set(DAP_CORE_SERVER_SRCS
+    dap_server.c
+    client/dap_client.c
+    client/dap_client_internal.c
+    client/dap_client_remote.c)
 
+include_directories(client)
 include_directories("${INCLUDE_DIRECTORIES} ${dap_client_INCLUDE_DIRS}")
 include_directories("${INCLUDE_DIRECTORIES} ${dap_core_INCLUDE_DIRS}")
 include_directories("${INCLUDE_DIRECTORIES} ${dap_crypto_INCLUDE_DIRS}")
 include_directories("${INCLUDE_DIRECTORIES} ${dap_enc_server_INCLUDE_DIRS}")
 
-
 add_definitions ("-DDAP_SERVER")
-
 add_definitions ("${dap_client_DEFINITIONS}")
 add_definitions ("${dap_core_DEFINITIONS}")
 add_definitions ("${dap_crypto_DEFINITIONS}")
 add_definitions ("${dap_enc_server_DEFINITIONS}")
 
-
 add_library(${PROJECT_NAME} STATIC ${DAP_CORE_SERVER_SRCS})
 set(${PROJECT_NAME}_DEFINITIONS CACHE INTERNAL "${PROJECT_NAME}: Definitions" FORCE)
 
diff --git a/core_server/client/(DELETE)CMakeLists.txt b/core_server/client/(DELETE)CMakeLists.txt
new file mode 100644
index 0000000..75d5408
--- /dev/null
+++ b/core_server/client/(DELETE)CMakeLists.txt
@@ -0,0 +1,26 @@
+cmake_minimum_required(VERSION 2.8)
+project (dap_client)
+
+set(CMAKE_VERBOSE_MAKEFILE ON)
+set(CMAKE_COLOR_MAKEFILE   ON)
+set(CMAKE_CXX_STANDARD 11)
+
+set(CLIENT_SRCS dap_client.c dap_client_internal.c dap_client_remote.c sxmlc/sxmlc.c sxmlc/sxmlsearch.c)
+ 
+add_library(${PROJECT_NAME} STATIC ${CLIENT_SRCS})
+
+include_directories("${dap_core_INCLUDE_DIRS}")
+include_directories("${dap_crypto_INCLUDE_DIRS}")
+include_directories("${dap_http_INCLUDE_DIRS}")
+include_directories("${dap_core_server_INCLUDE_DIRS}")
+add_definitions ("${dap_core_DEFINITIONS}")
+add_definitions ("${dap_crypto_DEFINITIONS}")
+add_definitions ("${dap_http_DEFINITIONS}")
+add_definitions ("${dap_core_server_DEFINITIONS}")
+
+set(${PROJECT_NAME}_DEFINITIONS CACHE INTERNAL "${PROJECT_NAME}: Definitions" FORCE)
+
+set(${PROJECT_NAME}_INCLUDE_DIRS ${PROJECT_SOURCE_DIR} CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE)
+
+
+
diff --git a/core_server/client/client.pri b/core_server/client/client.pri
new file mode 100644
index 0000000..77b4320
--- /dev/null
+++ b/core_server/client/client.pri
@@ -0,0 +1,15 @@
+HEADERS += $$PWD/dap_client.h \
+    $$PWD/dap_client_internal.h \
+    $$PWD/dap_client_remote.h \
+    $$PWD/sxmlc/sxmlc.h \
+    $$PWD/sxmlc/sxmlsearch.h
+
+
+SOURCES += $$PWD/dap_client.c \
+    $$PWD/dap_client_internal.c \
+    $$PWD/dap_client_remote.c \
+    $$PWD/sxmlc/sxmlc.c \
+    $$PWD/sxmlc/sxmlsearch.c
+
+
+INCLUDEPATH += $$PWD
diff --git a/core_server/client/dap_client.c b/core_server/client/dap_client.c
new file mode 100644
index 0000000..2867b78
--- /dev/null
+++ b/core_server/client/dap_client.c
@@ -0,0 +1,409 @@
+#include <string.h>
+
+#include "dap_common.h"
+
+#include "../../http_server/http_client/dap_http_client.h"
+#include "../../http_server/http_client/dap_http_client_simple.h"
+
+#include "dap_client.h"
+#include "dap_client_internal.h"
+#include "dap_enc_key.h"
+
+
+#define LOG_TAG "dap_client"
+
+void m_stage_fsm_operator(dap_client_t * a_client, void * a_arg);
+
+/**
+ * @brief dap_client_init
+ * @return
+ */
+int dap_client_init()
+{
+    static bool s_is_first_time=true;
+    if (s_is_first_time ){
+        log_it(L_INFO, "Init DAP client module");
+        dap_http_client_init();
+        dap_http_client_simple_init();
+        dap_client_internal_init();
+        s_is_first_time = false;
+    }
+    return 0;
+}
+
+/**
+ * @brief dap_client_deinit
+ */
+void dap_client_deinit()
+{
+    dap_client_internal_deinit();
+    dap_http_client_deinit();
+    log_it(L_INFO, "Deinit DAP client module");
+}
+
+/**
+ * @brief dap_client_new
+ * @param a_stage_status_callback
+ * @param a_stage_status_error_callback
+ * @return
+ */
+dap_client_t * dap_client_new(dap_client_callback_t a_stage_status_callback
+                              ,dap_client_callback_t a_stage_status_error_callback )
+{
+    // ALLOC MEM FOR dap_client
+    dap_client_t *l_client = DAP_NEW_Z(dap_client_t);
+    if (!l_client)
+        goto MEM_ALLOC_ERR;
+
+    l_client->_internal  = DAP_NEW_Z(dap_client_internal_t);
+    if (!l_client->_internal)
+        goto MEM_ALLOC_ERR;
+
+    // CONSTRUCT dap_client object
+    DAP_CLIENT_INTERNAL(l_client)->client = l_client;
+    DAP_CLIENT_INTERNAL(l_client)->stage_status_callback = a_stage_status_callback;
+    DAP_CLIENT_INTERNAL(l_client)->stage_status_error_callback = a_stage_status_error_callback;
+
+    dap_client_internal_new(DAP_CLIENT_INTERNAL(l_client) );
+
+    return l_client;
+
+MEM_ALLOC_ERR:
+    log_it(L_ERROR, "dap_client_new can not allocate memory");
+    if (l_client)
+        if(l_client->_internal)
+            free(l_client->_internal);
+
+    if (l_client)
+        free (l_client);
+    return NULL;
+}
+
+/**
+ * @brief sap_client_reset
+ * @param a_client
+ */
+void dap_client_reset(dap_client_t * a_client)
+{
+    dap_client_internal_t * l_client_internal = DAP_CLIENT_INTERNAL(a_client);
+    if(l_client_internal->auth_cookie){
+        free(l_client_internal->auth_cookie);
+        l_client_internal->auth_cookie = NULL;
+    }
+
+    if(l_client_internal->session_key){
+        dap_enc_key_delete(l_client_internal->session_key);
+        l_client_internal->session_key = NULL;
+
+    }
+    if(l_client_internal->session_key_id){
+        free(l_client_internal->session_key_id);
+        l_client_internal->session_key_id = NULL;
+    }
+    if ( l_client_internal->stream_key ){
+        dap_enc_key_delete(l_client_internal->stream_key );
+        l_client_internal->stream_key = NULL;
+    }
+    l_client_internal->es_stream = NULL;
+
+    l_client_internal->stage = DAP_CLIENT_STAGE_BEGIN;
+    l_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_DONE ;
+    l_client_internal->stage_target = DAP_CLIENT_STAGE_BEGIN ;
+}
+
+/**
+ * @brief dap_client_delete
+ * @param a_client
+ */
+void dap_client_delete(dap_client_t * a_client)
+{
+    dap_client_internal_delete(DAP_CLIENT_INTERNAL(a_client));
+    free(a_client);
+}
+
+/**
+ * @brief dap_client_go_stage
+ * @param a_client
+ * @param a_stage_end
+ */
+void dap_client_go_stage(dap_client_t * a_client, dap_client_stage_t a_stage_target, dap_client_callback_t a_stage_end_callback)
+{
+    // ----- check parameters -----
+    if(NULL == a_client) {
+        log_it(L_ERROR, "dap_client_go_stage, a_client == NULL");
+        return;
+    }
+    if(NULL == a_stage_end_callback) {
+        log_it(L_ERROR, "dap_client_go_stage, a_stage_end_callback == NULL");
+        return;
+    }
+    dap_client_internal_t * l_client_internal = DAP_CLIENT_INTERNAL(a_client);
+
+    l_client_internal->stage_target = a_stage_target;
+    l_client_internal->stage_target_done_callback = a_stage_end_callback;
+
+    if(a_stage_target != l_client_internal->stage ){ // Going to stages downstairs
+        switch(l_client_internal->stage_status ){
+            case DAP_CLIENT_STAGE_STATUS_ABORTING:
+                log_it(L_ERROR, "Already aborting the stage %s"
+                        , dap_client_stage_str(l_client_internal->stage));
+            break;
+            case DAP_CLIENT_STAGE_STATUS_IN_PROGRESS:{
+                log_it(L_WARNING, "Aborting the stage %s"
+                        , dap_client_stage_str(l_client_internal->stage));
+            }break;
+            case DAP_CLIENT_STAGE_STATUS_DONE:
+            case DAP_CLIENT_STAGE_STATUS_ERROR:
+            default: {
+                log_it(L_DEBUG, "Start transitions chain to %",dap_client_stage_str(l_client_internal->stage_target) );
+                int step = (a_stage_target > l_client_internal->stage)?1:-1;
+                dap_client_internal_stage_transaction_begin(l_client_internal,l_client_internal->stage+step,m_stage_fsm_operator);
+            }
+        }
+    }else{  // Same stage
+        log_it(L_ERROR,"We're already on stage %s",dap_client_stage_str(a_stage_target));
+    }
+}
+
+/**
+ * @brief m_stage_fsm_operator
+ * @param a_client
+ * @param a_arg
+ */
+void m_stage_fsm_operator(dap_client_t * a_client, void * a_arg)
+{
+    (void)a_arg;
+    dap_client_internal_t * l_client_internal = DAP_CLIENT_INTERNAL(a_client);
+    if (l_client_internal->stage_target == l_client_internal->stage){
+        log_it(L_WARNING, "FSM Op: current stage %s is same as target one, nothing to do",
+              dap_client_stage_str( l_client_internal->stage ) );
+        l_client_internal->stage_status_done_callback = NULL;
+
+        return;
+    }
+
+    int step = (l_client_internal->stage_target > l_client_internal->stage)?1:-1;
+    dap_client_stage_t l_stage_next = l_client_internal->stage+step;
+    log_it(L_NOTICE, "FSM Op: current stage %s, go to %s (target %s)"
+           ,dap_client_stage_str(l_client_internal->stage), dap_client_stage_str(l_stage_next)
+           ,dap_client_stage_str(l_client_internal->stage_target));
+    dap_client_internal_stage_transaction_begin(l_client_internal,
+                                                l_stage_next, m_stage_fsm_operator
+                                                );
+
+}
+
+/**
+ * @brief dap_client_request_enc
+ * @param a_client
+ * @param a_path
+ * @param a_suburl
+ * @param a_query
+ * @param a_request
+ * @param a_request_size
+ * @param a_response_proc
+ * @param a_response_error
+ */
+void dap_client_request_enc(dap_client_t * a_client, const char * a_path, const char * a_suburl,const char* a_query, void * a_request, size_t a_request_size,
+                                dap_client_callback_data_size_t a_response_proc, dap_client_callback_int_t a_response_error )
+{
+    dap_client_internal_t * l_client_internal = DAP_CLIENT_INTERNAL(a_client);
+    dap_client_internal_request_enc(l_client_internal, a_path, a_suburl, a_query,a_request,a_request_size, a_response_proc,a_response_error);
+}
+
+/**
+ * @brief dap_client_set_uplink
+ * @param a_client
+ * @param a_addr
+ * @param a_port
+ */
+void dap_client_set_uplink(dap_client_t * a_client,const char* a_addr, uint16_t a_port)
+{
+    if(a_addr == NULL){
+        log_it(L_ERROR,"Address is NULL");
+        return;
+    }
+    DAP_CLIENT_INTERNAL(a_client)->uplink_addr = strdup(a_addr);
+    DAP_CLIENT_INTERNAL(a_client)->uplink_port = a_port;
+}
+
+/**
+ * @brief dap_client_set_credentials
+ * @param a_client
+ * @param a_user
+ * @param a_password
+ */
+void dap_client_set_credentials(dap_client_t * a_client,const char* a_user, const char * a_password)
+{
+    if(a_user == NULL){
+        log_it(L_ERROR,"Username is NULL");
+        return;
+    }
+    if(a_password == NULL){
+        log_it(L_ERROR,"Password is NULL");
+        return;
+    }
+    DAP_CLIENT_INTERNAL(a_client)->uplink_user = strdup(a_user);
+}
+
+
+/**
+ * @brief dap_client_error_str
+ * @param a_client_error
+ * @return
+ */
+const char * dap_client_error_str(dap_client_error_t a_client_error)
+{
+    switch(a_client_error){
+        case DAP_CLIENT_ERROR_ENC_NO_KEY: return "ENC_NO_KEY";
+        case DAP_CLIENT_ERROR_ENC_WRONG_KEY: return "ENC_WRONG_KEY";
+        case DAP_CLIENT_ERROR_AUTH_WRONG_REPLY: return "AUTH_WRONG_REPLY";
+        case DAP_CLIENT_ERROR_AUTH_WRONG_COOKIE: return "AUTH_WRONG_COOKIE";
+        case DAP_CLIENT_ERROR_AUTH_WRONG_CREDENTIALS: return "AUTH_WRONG_CREDENTIALS";
+        case DAP_CLIENT_ERROR_NETWORK_CONNECTION_TIMEOUT: return "NETWORK_CONNECTION_TIMEOUT";
+        case DAP_CLIENT_ERROR_NETWORK_CONNECTION_REFUSE: return "NETWORK_CONNECTION_REFUSE";
+        case DAP_CLIENT_ERROR_NETWORK_DISCONNECTED: return "NETWORK_DISCONNECTED";
+        case DAP_CLIENT_ERROR_STREAM_RESPONSE_WRONG: return "STREAM_RESPONSE_WRONG";
+        case DAP_CLIENT_ERROR_STREAM_RESPONSE_TIMEOUT: return "STREAM_RESPONSE_TIMEOUT";
+        case DAP_CLIENT_ERROR_STREAM_FREEZED: return "STREAM_FREEZED";
+        case DAP_CLIENT_ERROR_STREAM_CTL_ERROR: return "STREAM_CTL_ERROR";
+        case DAP_CLIENT_ERROR_STREAM_CTL_ERROR_AUTH: return "STREAM_CTL_ERROR_AUTH";
+        case DAP_CLIENT_ERROR_STREAM_CTL_ERROR_RESPONSE_FORMAT: return "STREAM_CTL_ERROR_RESPONSE_FORMAT";
+
+        case DAP_CLIENT_ERROR_LICENSE: return "LICENSE_ERROR";
+        default : return "UNDEFINED";
+    }
+}
+
+/**
+ * @brief dap_client_get_error_str
+ * @param a_client
+ * @return
+ */
+const char * dap_client_get_error_str(dap_client_t * a_client)
+{
+   return dap_client_error_str( DAP_CLIENT_INTERNAL(a_client)->last_error );
+}
+/**
+ * @brief dap_client_get_stage
+ * @param a_client
+ * @return
+ */
+dap_client_stage_t dap_client_get_stage(dap_client_t * a_client)
+{
+    return DAP_CLIENT_INTERNAL(a_client)->stage;
+}
+
+/**
+ * @brief dap_client_get_stage_status_str
+ * @param a_client
+ * @return
+ */
+const char * dap_client_get_stage_status_str(dap_client_t *a_client){
+    return dap_client_stage_status_str(DAP_CLIENT_INTERNAL(a_client)->stage_status);
+}
+
+/**
+ * @brief dap_client_stage_status_str
+ * @param a_stage_status
+ * @return
+ */
+const char * dap_client_stage_status_str(dap_client_stage_status_t a_stage_status)
+{
+    switch(a_stage_status){
+        case DAP_CLIENT_STAGE_STATUS_NONE: return "NONE";
+        case DAP_CLIENT_STAGE_STATUS_IN_PROGRESS: return "IN_PROGRESS";
+        case DAP_CLIENT_STAGE_STATUS_ERROR: return "ERROR";
+        case DAP_CLIENT_STAGE_STATUS_DONE: return "DONE";
+        default: return "UNDEFINED";
+    }
+}
+
+/**
+ * @brief dap_client_get_stage_str
+ * @param a_client
+ * @return
+ */
+const char * dap_client_get_stage_str(dap_client_t *a_client)
+{
+    return dap_client_stage_str(DAP_CLIENT_INTERNAL(a_client)->stage);
+}
+
+/**
+ * @brief dap_client_stage_str
+ * @param a_stage
+ * @return
+ */
+const char * dap_client_stage_str(dap_client_stage_t a_stage)
+{
+    switch(a_stage){
+        case DAP_CLIENT_STAGE_BEGIN: return "BEGIN";
+        case DAP_CLIENT_STAGE_ENC: return "ENC";
+        case DAP_CLIENT_STAGE_AUTH: return "AUTH";
+        case DAP_CLIENT_STAGE_STREAM_CTL: return "STREAM_CTL";
+        case DAP_CLIENT_STAGE_STREAM: return "STREAM";
+        case DAP_CLIENT_STAGE_NETCONF: return "NETCONF";
+        case DAP_CLIENT_STAGE_TUNNEL: return "TUNNEL";
+        default: return "UNDEFINED";
+    }
+}
+/**
+ * @brief dap_client_get_stage_status
+ * @param a_client
+ * @return
+ */
+dap_client_stage_status_t dap_client_get_stage_status(dap_client_t * a_client)
+{
+    return DAP_CLIENT_INTERNAL(a_client)->stage_status;
+}
+
+/**
+ * @brief dap_client_get_key_stream
+ * @param a_client
+ * @return
+ */
+dap_enc_key_t * dap_client_get_key_stream(dap_client_t * a_client){
+    return DAP_CLIENT_INTERNAL(a_client)->stream_key;
+}
+
+/**
+ * @brief sap_client_get_uplink_addr
+ * @param a_client
+ * @return
+ */
+const char* dap_client_get_uplink_addr(dap_client_t * a_client)
+{
+    return DAP_CLIENT_INTERNAL(a_client)->uplink_addr;
+}
+
+/**
+ * @brief dap_client_get_uplink_port
+ * @param a_client
+ * @return
+ */
+uint16_t dap_client_get_uplink_port(dap_client_t * a_client)
+{
+    return DAP_CLIENT_INTERNAL(a_client)->uplink_port;
+}
+
+/**
+ * @brief dap_client_get_auth_cookie
+ * @param a_client
+ * @return
+ */
+const char * dap_client_get_auth_cookie(dap_client_t * a_client)
+{
+    return DAP_CLIENT_INTERNAL(a_client)->auth_cookie;
+}
+
+/**
+ * @brief dap_client_get_stream_id
+ * @param a_client
+ * @return
+ */
+const char * dap_client_get_stream_id(dap_client_t * a_client)
+{
+    return DAP_CLIENT_INTERNAL(a_client)->stream_id;
+}
+
+
diff --git a/core_server/client/dap_client.h b/core_server/client/dap_client.h
new file mode 100644
index 0000000..d83a887
--- /dev/null
+++ b/core_server/client/dap_client.h
@@ -0,0 +1,111 @@
+#ifndef _DAP_CLIENT_H_
+#define _DAP_CLIENT_H_
+#include <stdint.h>
+#include <stddef.h>
+
+/**
+ * @brief The dap_client_stage enum. Top level of client's state machine
+ **/
+
+typedef struct dap_enc_key dap_enc_key_t;
+
+typedef enum dap_client_stage {
+    DAP_CLIENT_STAGE_BEGIN=0,
+    DAP_CLIENT_STAGE_ENC=1,
+    DAP_CLIENT_STAGE_STREAM_CTL=2,
+    DAP_CLIENT_STAGE_STREAM=3,
+    DAP_CLIENT_STAGE_NETCONF=4,
+    DAP_CLIENT_STAGE_TUNNEL=5,
+    DAP_CLIENT_STAGE_AUTH=6
+} dap_client_stage_t;
+
+typedef enum dap_client_stage_status {
+    DAP_CLIENT_STAGE_STATUS_NONE=0,
+    // Enc init stage
+    DAP_CLIENT_STAGE_STATUS_IN_PROGRESS,
+    DAP_CLIENT_STAGE_STATUS_ABORTING,
+    DAP_CLIENT_STAGE_STATUS_ERROR,
+    DAP_CLIENT_STAGE_STATUS_DONE,
+} dap_client_stage_status_t;
+
+typedef enum dap_client_error {
+    DAP_CLIENT_ERROR_NO = 0,
+    DAP_CLIENT_ERROR_ENC_NO_KEY,
+    DAP_CLIENT_ERROR_ENC_WRONG_KEY,
+    DAP_CLIENT_ERROR_AUTH_WRONG_REPLY,
+    DAP_CLIENT_ERROR_AUTH_WRONG_COOKIE,
+    DAP_CLIENT_ERROR_AUTH_WRONG_CREDENTIALS,
+    DAP_CLIENT_ERROR_NETWORK_CONNECTION_TIMEOUT,
+    DAP_CLIENT_ERROR_NETWORK_CONNECTION_REFUSE,
+    DAP_CLIENT_ERROR_NETWORK_DISCONNECTED,
+    DAP_CLIENT_ERROR_STREAM_CTL_ERROR,
+    DAP_CLIENT_ERROR_STREAM_CTL_ERROR_AUTH,
+    DAP_CLIENT_ERROR_STREAM_CTL_ERROR_RESPONSE_FORMAT,
+    DAP_CLIENT_ERROR_STREAM_RESPONSE_WRONG,
+    DAP_CLIENT_ERROR_STREAM_RESPONSE_TIMEOUT,
+    DAP_CLIENT_ERROR_STREAM_FREEZED,
+    DAP_CLIENT_ERROR_LICENSE,
+} dap_client_error_t;
+
+
+/**
+ * @brief The dap_client struct
+ */
+typedef struct dap_client{
+    void * _internal;
+    void * _inheritor;
+} dap_client_t;
+
+typedef void (*dap_client_callback_t) (dap_client_t *, void*);
+typedef void (*dap_client_callback_int_t) (dap_client_t *, int);
+typedef void (*dap_client_callback_data_size_t) (dap_client_t *, void *, size_t);
+
+#define DAP_UPLINK_PATH_ENC_INIT "1901248124123459"
+#define DAP_UPLINK_PATH_DB "01094787531354"
+#define DAP_UPLINK_PATH_STREAM_CTL "091348758013553"
+#define DAP_UPLINK_PATH_STREAM "874751843144"
+#define DAP_UPLINK_PATH_LICENSE "license"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DAP_CLIENT_PROTOCOL_VERSION 20
+
+int dap_client_init();
+void dap_client_deinit();
+
+dap_client_t * dap_client_new(dap_client_callback_t a_stage_status_callback
+                              , dap_client_callback_t a_stage_status_error_callback );
+void dap_client_delete(dap_client_t * a_client);
+
+void dap_client_set_uplink(dap_client_t * a_client,const char* a_addr, uint16_t a_port);
+const char* sap_client_get_uplink_addr(dap_client_t * a_client);
+uint16_t sap_client_get_uplink_port(dap_client_t * a_client);
+
+void dap_client_set_credentials(dap_client_t * a_client,const char* a_user, const char * a_password);
+dap_enc_key_t * sap_client_get_key_stream(dap_client_t * a_client);
+
+void dap_client_go_stage(dap_client_t * a_client, dap_client_stage_t a_stage_end, dap_client_callback_t a_stage_end_callback);
+
+void dap_client_reset(dap_client_t * a_client);
+
+void dap_client_request_enc(dap_client_t * a_client, const char * a_path,const char * a_suburl,const char* a_query, void * a_request, size_t a_request_size,
+                                dap_client_callback_data_size_t a_response_proc, dap_client_callback_int_t a_response_error);
+
+const char * dap_client_get_stage_str(dap_client_t * a_client);
+const char * dap_client_stage_str(dap_client_stage_t a_stage);
+
+const char * dap_client_get_stage_status_str(dap_client_t * a_client);
+const char * dap_client_stage_status_str(dap_client_stage_status_t a_stage_status);
+const char * dap_client_error_str(dap_client_error_t a_client_error);
+const char * dap_client_get_error_str(dap_client_t * a_client);
+
+dap_client_stage_t dap_client_get_stage(dap_client_t * a_client);
+dap_client_stage_status_t dap_client_get_stage_status(dap_client_t * a_client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/core_server/client/dap_client_internal.c b/core_server/client/dap_client_internal.c
new file mode 100644
index 0000000..c2f22d8
--- /dev/null
+++ b/core_server/client/dap_client_internal.c
@@ -0,0 +1,722 @@
+#include "dap_client_internal.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "dap_enc_key.h"
+#include "dap_enc.h"
+#include "dap_enc_base64.h"
+#include "dap_common.h"
+#include "sxmlc/sxmlc.h"
+#include "liboqs/kex_rlwe_msrln16/kex_rlwe_msrln16.h"
+#include "liboqs/kex/kex.h"
+#include "dap_enc_msrln16.h"
+
+#include "../../http_server/http_client/dap_http_client_simple.h"
+#include "dap_client_internal.h"
+
+#define LOG_TAG "dap_client_internal"
+
+dap_enc_key_t * s_key_domain = NULL;
+static void s_stage_status_after(dap_client_internal_t * a_client_internal);
+
+void m_enc_init_response(dap_client_t *, void *, size_t);
+void m_enc_init_error(dap_client_t *, int);
+
+// AUTH stage callbacks
+void m_auth_response(dap_client_t *, void *, size_t);
+void m_auth_error(dap_client_t *, int);
+int m_auth_response_parse(XMLEvent event, const XMLNode* node, SXML_CHAR* text, const int n, SAX_Data* sd);
+
+// STREAM_CTL stage callbacks
+void m_stream_ctl_response(dap_client_t *, void *, size_t);
+void m_stream_ctl_error(dap_client_t *, int);
+
+void m_request_response(void * a_response,size_t a_response_size,void * a_obj);
+void m_request_error(int,void *);
+
+/**
+ * @brief dap_client_internal_init
+ * @return
+ */
+int dap_client_internal_init()
+{
+    OQS_RAND* rand = OQS_RAND_new(OQS_RAND_alg_urandom_chacha20);        
+    s_key_domain = dap_enc_key_new_generate(DAP_ENC_KEY_TYPE_RLWE_MSRLN16,16);
+    dap_enc_msrln16_key_t* msrln16_key = DAP_ENC_KEY_TYPE_RLWE_MSRLN16(s_key_domain);
+    msrln16_key->kex = OQS_KEX_rlwe_msrln16_new(rand);
+    return 0;
+}
+
+/**
+ * @brief dap_client_internal_deinit
+ */
+void dap_client_internal_deinit()
+{
+    dap_enc_key_delete(s_key_domain);
+    s_key_domain = NULL;
+}
+
+/**
+ * @brief dap_client_internal_new
+ * @param a_client_internal
+ */
+void dap_client_internal_new(dap_client_internal_t * a_client_internal)
+{
+    a_client_internal->stage = DAP_CLIENT_STAGE_BEGIN; // start point of state machine
+    a_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_DONE;
+}
+
+/**
+ * @brief dap_client_internal_delete
+ * @param a_client_internal
+ */
+void dap_client_internal_delete(dap_client_internal_t * a_client_internal)
+{
+    if(a_client_internal->uplink_addr)
+        free(a_client_internal->uplink_addr);
+    if(a_client_internal->uplink_user)
+        free(a_client_internal->uplink_user);
+    if(a_client_internal->uplink_password)
+        free(a_client_internal->uplink_password);
+    if(a_client_internal->session_key_id)
+        free(a_client_internal->session_key_id);
+}
+
+
+/**
+ * @brief s_client_internal_stage_status_proc
+ * @param a_client
+ */
+static void s_stage_status_after(dap_client_internal_t * a_client_internal)
+{
+    switch(a_client_internal->stage_status){
+        case DAP_CLIENT_STAGE_STATUS_IN_PROGRESS:{
+            switch( a_client_internal->stage){
+                case DAP_CLIENT_STAGE_ENC:{
+                    log_it(L_INFO,"Go to stage ENC: prepare the request");
+                    //Stage 1 : generate private key and alice message
+                    dap_enc_msrln16_key_t* msrln16_key = DAP_ENC_KEY_TYPE_RLWE_MSRLN16(s_key_domain);
+                    uint8_t* out_msg = NULL;
+                    size_t out_msg_size = 0;
+                    OQS_KEX_rlwe_msrln16_alice_0(msrln16_key->kex,&msrln16_key->private_key,&out_msg,&out_msg_size);
+
+                    char *sendMsg = malloc(out_msg_size * 2  + 1024);
+
+                    char* encrypt_msg = malloc(out_msg_size * 2);
+                    dap_enc_base64_encode(out_msg,out_msg_size, encrypt_msg,DAP_ENC_STANDARD_B64);
+
+                    strcat(sendMsg,encrypt_msg);
+
+                    dap_client_internal_request( a_client_internal, DAP_UPLINK_PATH_ENC_INIT "/gd4y5yh78w42aaagh",
+                        sendMsg,strlen(sendMsg), m_enc_init_response, m_enc_init_error );
+
+                    free(encrypt_msg);
+                    free(sendMsg);
+
+                }break;
+                case DAP_CLIENT_STAGE_AUTH:{
+                    log_it(L_INFO,"Go to stage AUTH: prepare the request");
+
+                    /// uplink_user checks
+                    if ( a_client_internal->uplink_user == NULL){
+                        log_it(L_ERROR,"Can't AUTH with NULL uplink user");
+                        a_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_ERROR;
+                        s_stage_status_after(a_client_internal); // be carefull to not to loop!
+                        break;
+                    }else if ( a_client_internal->uplink_user[0] == 0  ){
+                        log_it(L_ERROR,"Can't AUTH with empty uplink user");
+                        a_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_ERROR;
+                        s_stage_status_after(a_client_internal); // be carefull to not to loop!
+                        break;
+                    }
+                    /// uplink_password checks
+                    if ( a_client_internal->uplink_password == NULL){
+                        log_it(L_ERROR,"Can't AUTH with NULL uplink password");
+                        a_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_ERROR;
+                        s_stage_status_after(a_client_internal); // be carefull to not to loop!
+                        break;
+                    }else if ( a_client_internal->uplink_password[0] == 0  ){
+                        log_it(L_ERROR,"Can't AUTH with empty uplink password");
+                        a_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_ERROR;
+                        s_stage_status_after(a_client_internal); // be carefull to not to loop!
+                        break;
+                    }
+
+                    size_t l_request_size = strlen( a_client_internal->uplink_user)
+                            + strlen( a_client_internal->uplink_password)+2+10;
+                    char *l_request = DAP_NEW_Z_SIZE (char,l_request_size) ;
+
+                    snprintf(l_request, l_request_size,"%s %s %d",a_client_internal->uplink_user,
+                             a_client_internal->uplink_password, DAP_CLIENT_PROTOCOL_VERSION);
+                    log_it(L_DEBUG,"AUTH request size %u",strlen(l_request));
+
+                    // If we was authorized before - reset it
+                    if ( a_client_internal->auth_cookie ){
+                        DAP_DELETE (a_client_internal->auth_cookie );
+                        a_client_internal->auth_cookie = NULL;
+                    }
+                    // Until we haven't PROTO version before this step - we set the current one
+                    a_client_internal->uplink_protocol_version = DAP_PROTOCOL_VERSION;
+                    dap_client_internal_request_enc(a_client_internal,
+                                                   DAP_UPLINK_PATH_DB,
+                                                    "auth","login",l_request,l_request_size,
+                                                    m_auth_response, m_auth_error);
+
+                }break;
+                case DAP_CLIENT_STAGE_STREAM_CTL:{
+                    log_it(L_INFO,"Go to stage STREAM_CTL: prepare the request");
+
+                    size_t l_request_size = strlen( a_client_internal->uplink_user)
+                            + strlen( a_client_internal->uplink_password)+2+10;
+                    char *l_request = DAP_NEW_Z_SIZE (char,l_request_size) ;
+
+                    snprintf(l_request, l_request_size,"%s %s %d",a_client_internal->uplink_user,
+                             a_client_internal->uplink_password, DAP_CLIENT_PROTOCOL_VERSION);
+                    log_it(L_DEBUG,"STREAM_CTL request size %u",strlen(l_request));
+
+                    a_client_internal->uplink_protocol_version = DAP_PROTOCOL_VERSION;
+
+                    dap_client_internal_request_enc(a_client_internal,
+                                                   DAP_UPLINK_PATH_STREAM_CTL,
+                                                    "socket_forward","sf=1",l_request,l_request_size,
+                                                    m_stream_ctl_response, m_stream_ctl_error);
+                }break;
+                case DAP_CLIENT_STAGE_STREAM:{
+                    log_it(L_INFO,"Go to stage STREAM: prepare the request");
+
+                    size_t l_request_size = strlen( a_client_internal->uplink_user)
+                            + strlen( a_client_internal->uplink_password)+2+10;
+                    char *l_request = DAP_NEW_Z_SIZE (char,l_request_size) ;
+
+                    snprintf(l_request, l_request_size,"%s %s %d",a_client_internal->uplink_user,
+                             a_client_internal->uplink_password, DAP_CLIENT_PROTOCOL_VERSION);
+                    log_it(L_DEBUG,"STREAM request size %u",strlen(l_request));
+
+                    dap_client_internal_request_enc(a_client_internal,
+                                                   DAP_UPLINK_PATH_STREAM,
+                                                    "socket_forward","sf=1",l_request,l_request_size,
+                                                    m_stream_ctl_response, m_stream_ctl_error);
+                }break;
+                default:{
+                    log_it(L_ERROR,"Undefined proccessing actions for stage status %s",
+                                dap_client_stage_status_str(a_client_internal->stage_status));
+                    a_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_ERROR;
+                    s_stage_status_after(a_client_internal); // be carefull to not to loop!
+                }
+            }
+        } break;
+        case DAP_CLIENT_STAGE_STATUS_ERROR:{
+            log_it(L_ERROR, "Error state, doing callback if present");
+            if( a_client_internal->stage_status_error_callback ){
+                a_client_internal->stage_status_error_callback(a_client_internal->client,NULL);
+                // Expecting that its one-shot callback
+                //a_client_internal->stage_status_error_callback = NULL;
+            }
+            a_client_internal->stage = DAP_CLIENT_STAGE_ENC;
+            // Trying the step again
+            a_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_IN_PROGRESS;
+            s_stage_status_after(a_client_internal);
+        } break;
+        case DAP_CLIENT_STAGE_STATUS_DONE :{
+            log_it(L_INFO, "Stage status %s is done",
+                   dap_client_stage_str(a_client_internal->stage) );
+            bool l_is_last_stage=( a_client_internal->stage == a_client_internal->stage_target );
+            if( a_client_internal->stage_status_done_callback ){
+                a_client_internal->stage_status_done_callback(a_client_internal->client,NULL);
+                // Expecting that its one-shot callback
+                //a_client_internal->stage_status_done_callback = NULL;
+            }else
+                log_it(L_WARNING,"Stage done callback is not present");
+
+            if (l_is_last_stage ){
+                log_it(L_NOTICE, "Stage %s is achieved",
+                       dap_client_stage_str(a_client_internal->stage));
+                if( a_client_internal->stage_target_done_callback ){
+                    a_client_internal->stage_target_done_callback(a_client_internal->client,NULL);
+                    // Expecting that its one-shot callback
+                    a_client_internal->stage_target_done_callback = NULL;
+                }
+            }
+        }break;
+        default: log_it(L_ERROR,"Undefined proccessing actions for stage status %s",
+                        dap_client_stage_status_str(a_client_internal->stage_status));
+    }
+
+    if( a_client_internal->stage_status_callback )
+        a_client_internal->stage_status_callback( a_client_internal->client,NULL );
+}
+
+
+/**
+ * @brief dap_client_internal_stage_transaction_begin
+ * @param a_client_internal
+ * @param a_stage_next
+ * @param a_done_callback
+ */
+void dap_client_internal_stage_transaction_begin(dap_client_internal_t * a_client_internal, dap_client_stage_t a_stage_next,
+                                                 dap_client_callback_t a_done_callback)
+{
+    a_client_internal->stage_status_done_callback = a_done_callback;
+    a_client_internal->stage = a_stage_next;
+    a_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_IN_PROGRESS;
+    s_stage_status_after(a_client_internal);
+}
+
+/**
+ * @brief dap_client_internal_request
+ * @param a_client_internal
+ * @param a_path
+ * @param a_request
+ * @param a_request_size
+ * @param a_response_proc
+ */
+void dap_client_internal_request(dap_client_internal_t * a_client_internal, const char * a_path, void * a_request,
+                    size_t a_request_size,  dap_client_callback_data_size_t a_response_proc, dap_client_callback_int_t a_response_error)
+{
+    a_client_internal->request_response_callback = a_response_proc;
+    a_client_internal->request_error_callback = a_response_error;
+    a_client_internal->is_encrypted = false;
+    char l_url[2048];
+    if(a_path)
+        snprintf(l_url,1024,"http://%s:%u/%s",a_client_internal->uplink_addr, a_client_internal->uplink_port, a_path );
+    else
+        snprintf(l_url,1024,"http://%s:%u",a_client_internal->uplink_addr, a_client_internal->uplink_port );
+
+    dap_http_client_simple_request(l_url, a_request?"POST":"GET","text/text", a_request, a_request_size,NULL,
+                                       m_request_response, m_request_error, a_client_internal,NULL);
+}
+
+/**
+ * @brief dap_client_internal_request_enc
+ * @param a_client_internal
+ * @param a_path
+ * @param a_sub_url
+ * @param a_query
+ * @param a_request
+ * @param a_request_size
+ * @param a_response_proc
+ * @param a_response_error
+ */
+void dap_client_internal_request_enc(dap_client_internal_t * a_client_internal, const char * a_path,
+                                     const char * a_sub_url, const char * a_query
+                                    , void * a_request, size_t a_request_size
+                                    ,dap_client_callback_data_size_t a_response_proc
+                                     , dap_client_callback_int_t a_response_error)
+{
+    log_it(L_DEBUG,"Encrypted request: sub_url '%s' query '%s'",a_sub_url?a_sub_url:"", a_query?a_query:"" );
+    size_t l_sub_url_size = a_sub_url?strlen(a_sub_url): 0;
+    size_t l_query_size = a_query?strlen(a_query):0;
+    size_t l_url_size;
+
+    char l_url[1024];
+    snprintf(l_url,1024,"http://%s:%u",a_client_internal->uplink_addr, a_client_internal->uplink_port );
+    l_url_size = strlen(l_url);
+
+    char *l_sub_url_enc = l_sub_url_size ? (char*) calloc(1,2*(l_sub_url_size+16) ): NULL;
+    char *l_query_enc = l_query_size ? (char*) calloc(1,(l_query_size+16)*2):NULL;
+
+    size_t l_url_full_size_max  = 2*l_sub_url_size + 2*(l_query_size+16) + 5 + l_url_size;
+    char * l_url_full = (char*) calloc(1, l_url_full_size_max);
+
+    size_t l_request_enc_size_max = a_request_size ?a_request_size*2+16 : 0;
+    char * l_request_enc = a_request_size? (char*) calloc(1,l_request_enc_size_max ) : NULL;
+    size_t l_request_enc_size = 0;
+
+    a_client_internal->request_response_callback = a_response_proc;
+    a_client_internal->request_error_callback = a_response_error;
+    a_client_internal->is_encrypted = true;
+    dap_enc_data_type_t l_enc_type;
+
+    if( a_client_internal->uplink_protocol_version >= 21  )
+        l_enc_type = DAP_ENC_DATA_TYPE_B64_URLSAFE;
+    else
+        l_enc_type = DAP_ENC_DATA_TYPE_B64;
+
+    if ( l_sub_url_size )
+        dap_enc_code(a_client_internal->session_key,a_sub_url,l_sub_url_size,l_sub_url_enc,l_enc_type);
+
+    if ( l_query_size )
+        dap_enc_code(a_client_internal->session_key,a_query,l_query_size,l_query_enc,l_enc_type);
+
+
+    if ( a_request_size )
+        l_request_enc_size = dap_enc_code(a_client_internal->session_key, a_request, a_request_size
+                                          , l_request_enc, DAP_ENC_DATA_TYPE_RAW );
+
+    if (a_path){
+        if( l_sub_url_size ){
+            if( l_query_size ){
+                snprintf(l_url_full,l_url_full_size_max-1,"%s/%s/%s?%s",l_url,a_path, l_sub_url_enc, l_query_enc );
+            }else{
+                snprintf(l_url_full,l_url_full_size_max-1,"%s/%s/%s",l_url,a_path, l_sub_url_enc);
+            }
+        }else{
+            snprintf(l_url_full,l_url_full_size_max-1,"%s/%s",l_url,a_path);
+        }
+    }else{
+        snprintf(l_url_full,l_url_full_size_max-1,"%s",l_url);
+    }
+
+    char l_key_hdr_str[1024];
+    snprintf(l_key_hdr_str,sizeof(l_key_hdr_str),"KeyID: %s",a_client_internal->session_key_id );
+    if ( a_client_internal->auth_cookie){
+        size_t l_cookie_hdr_str_size = strlen(a_client_internal->auth_cookie)+100;
+        char * l_cookie_hdr_str= DAP_NEW_Z_SIZE(char,l_cookie_hdr_str_size);
+        snprintf(l_cookie_hdr_str,l_cookie_hdr_str_size,"Cookie: %s",a_client_internal->auth_cookie  );
+
+        dap_http_client_simple_request(l_url_full, a_request?"POST":"GET","text/text",
+                                       l_request_enc, l_request_enc_size, l_cookie_hdr_str,
+                                           m_request_response, m_request_error, a_client_internal, l_key_hdr_str);
+        DAP_DELETE(l_cookie_hdr_str);
+    }else
+        dap_http_client_simple_request(l_url_full, a_request?"POST":"GET","text/text",
+                                       l_request_enc, l_request_enc_size, NULL,
+                                           m_request_response, m_request_error, a_client_internal, l_key_hdr_str);
+    if( l_sub_url_enc )
+        free(l_sub_url_enc);
+
+    if( l_query_enc )
+        free(l_query_enc);
+
+    if( l_url_full )
+        free(l_url_full);
+
+    if( l_request_enc )
+        free(l_request_enc);
+}
+
+/**
+ * @brief m_request_error
+ * @param a_err_code
+ * @param a_obj
+ */
+void m_request_error(int a_err_code, void * a_obj)
+{
+    dap_client_internal_t * a_client_internal = (dap_client_internal_t *) a_obj;
+    a_client_internal->request_error_callback(a_client_internal->client, a_err_code );
+}
+
+/**
+ * @brief m_request_response
+ * @param a_response
+ * @param a_response_size
+ * @param a_obj
+ */
+void m_request_response(void * a_response,size_t a_response_size,void * a_obj)
+{
+    dap_client_internal_t * a_client_internal = (dap_client_internal_t *) a_obj;
+    if( a_client_internal->is_encrypted){
+        size_t l_response_dec_size_max = a_response_size ?a_response_size*2+16 : 0;
+        char * l_response_dec = a_response_size? (char*) calloc(1,l_response_dec_size_max ) : NULL;
+        size_t l_response_dec_size = 0;
+        if ( a_response_size )
+            l_response_dec_size = dap_enc_decode(a_client_internal->session_key,
+                                             a_response, a_response_size, l_response_dec, DAP_ENC_DATA_TYPE_RAW );
+
+        a_client_internal->request_response_callback(a_client_internal->client, l_response_dec, l_response_dec_size );
+
+        if( l_response_dec )
+            free ( l_response_dec );
+    }else{
+        a_client_internal->request_response_callback(a_client_internal->client, a_response, a_response_size );
+    }
+}
+
+
+/**
+ * @brief m_enc_init_response
+ * @param a_client
+ * @param a_response
+ * @param a_response_size
+ */
+void m_enc_init_response(dap_client_t * a_client, void * a_response,size_t a_response_size)
+{
+    dap_client_internal_t * l_client_internal = DAP_CLIENT_INTERNAL(a_client);
+    if( a_response_size > 2000 &&  a_response_size < 4000){
+        if( l_client_internal->stage == DAP_CLIENT_STAGE_ENC ){
+            char *msg_index = strchr(a_response,' ');
+            int key_size = (void*)msg_index - a_response;
+            int msg_size = a_response_size - key_size - 1;
+            char* encoded_key = malloc(key_size);
+            memset(encoded_key,0,key_size);
+            uint8_t *encoded_msg = malloc(msg_size);
+            dap_enc_base64_decode(a_response,key_size,encoded_key,DAP_ENC_STANDARD_B64);
+            dap_enc_base64_decode(msg_index+1,msg_size,encoded_msg,DAP_ENC_STANDARD_B64);
+            dap_enc_msrln16_key_t* msrln16_key = DAP_ENC_KEY_TYPE_RLWE_MSRLN16(s_key_domain);
+            OQS_KEX_rlwe_msrln16_alice_1(msrln16_key->kex, msrln16_key->private_key, encoded_msg, 2048,&msrln16_key->public_key,&msrln16_key->public_length);
+            aes_key_from_msrln_pub(s_key_domain);
+            l_client_internal->session_key_id = (char*)malloc(33);
+            memcpy(l_client_internal->session_key_id,encoded_key,33);
+            l_client_internal->session_key_id[32] = 0;
+            l_client_internal->session_key = s_key_domain;
+            free(encoded_key);
+            free(encoded_msg);
+            l_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_DONE;
+            s_stage_status_after(l_client_internal);
+        }
+        else{
+            log_it(L_WARNING,"Initialized encryption but current stage is %s (%s)",
+            dap_client_get_stage_str(a_client),dap_client_get_stage_status_str(a_client));
+        }
+    }else if( a_response_size>1){
+        s_stage_status_after(l_client_internal);
+        log_it(L_ERROR, "Wrong response (size %u data '%s')",a_response_size,(char*) a_response);
+        l_client_internal->last_error = DAP_CLIENT_ERROR_ENC_NO_KEY;
+        l_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_ERROR;
+        s_stage_status_after(l_client_internal);
+    }else{
+        log_it(L_ERROR, "Wrong response (size %u)",a_response_size);
+        l_client_internal->last_error = DAP_CLIENT_ERROR_ENC_NO_KEY;
+        l_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_ERROR;
+        s_stage_status_after(l_client_internal);
+    }
+}
+
+/**
+ * @brief m_enc_init_error
+ * @param a_client
+ * @param a_err_code
+ */
+void m_enc_init_error(dap_client_t * a_client, int a_err_code)
+{
+    dap_client_internal_t * l_client_internal = DAP_CLIENT_INTERNAL(a_client);
+    //dap_client_internal_t * l_client_internal = DAP_CLIENT_INTERNAL(a_client);
+    log_it(L_ERROR,"Can't init ecnryption session, err code %d",a_err_code);
+
+    l_client_internal->last_error = DAP_CLIENT_ERROR_NETWORK_CONNECTION_REFUSE ;
+    l_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_ERROR;
+    s_stage_status_after(l_client_internal);
+}
+
+/**
+ * @brief m_auth_response Process AUTH response
+ * @param a_client
+ * @param a_data
+ * @param a_data_size
+ */
+void m_auth_response(dap_client_t * a_client, void * a_data, size_t a_data_size)
+{
+    dap_client_internal_t * l_client_internal = DAP_CLIENT_INTERNAL(a_client);
+
+    log_it(L_DEBUG, "AUTH response %u bytes length recieved", a_data_size);
+    char * l_response_str = DAP_NEW_Z_SIZE(char, a_data_size+1);
+    memcpy(l_response_str, a_data,a_data_size);
+
+    if(a_data_size <10 ){
+        log_it(L_ERROR, "AUTH Wrong reply: '%s'", l_response_str);
+    }else{
+        XMLDoc l_doc;
+        XMLDoc_init(&l_doc);
+
+        SAX_Callbacks l_sax;
+        SAX_Callbacks_init(&l_sax);
+
+        l_sax.all_event = m_auth_response_parse;
+
+        XMLDoc_parse_buffer_SAX( C2SX(l_response_str ), C2SX("auth"),&l_sax, a_client );
+        XMLDoc_free(&l_doc);
+
+        DAP_DELETE(l_response_str);
+        if ( l_client_internal->auth_cookie ){
+            log_it(L_DEBUG, "Cookie is present in reply");
+            if( l_client_internal->stage == DAP_CLIENT_STAGE_AUTH ){ // We are in proper stage
+                l_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_DONE;
+                s_stage_status_after(l_client_internal);
+            }else{
+                log_it(L_WARNING,"Expected to be stage AUTH but current stage is %s (%s)",
+                       dap_client_get_stage_str(a_client),dap_client_get_stage_status_str(a_client));
+
+            }
+        }else {
+            if( l_client_internal->last_error == DAP_CLIENT_ERROR_AUTH_WRONG_CREDENTIALS ){
+                log_it (L_WARNING, "Wrong user or/and password");
+            }else{
+                log_it(L_WARNING, "Cookie is not present in reply!");
+                l_client_internal->last_error = DAP_CLIENT_ERROR_AUTH_WRONG_COOKIE ;
+                l_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_ERROR;
+            }
+
+            s_stage_status_after(l_client_internal);
+        }
+    }
+}
+
+/**
+ * @brief m_auth_response_parse Parse XML reply after authorization
+ * @param event
+ * @param node
+ * @param text
+ * @param n
+ * @param sd
+ * @return
+ */
+int m_auth_response_parse(XMLEvent event, const XMLNode* node, SXML_CHAR* text, const int n, SAX_Data* sd)
+{
+    (void)n;
+    dap_client_t * l_client = (dap_client_t *) sd->user;
+    dap_client_internal_t * l_client_internal = DAP_CLIENT_INTERNAL( l_client );
+
+    switch (event) {
+        //case XML_EVENT_START_NODE: last_parsed_node = strdup ( node->text) return start_node(node, sd);
+        case XML_EVENT_TEXT:
+//            log_it(L_DEBUG, "Node text '%s'", text );
+            if( l_client_internal->last_parsed_node ){
+                free(l_client_internal->last_parsed_node);
+                l_client_internal->last_parsed_node = NULL;
+            }
+            l_client_internal->last_parsed_node = strdup(text);
+            return true;
+        break;
+        case XML_EVENT_END_NODE: {
+            if(node == NULL)
+                break;
+           // log_it(L_DEBUG,"Parsed <%s>%s</%s> tag", node->tag, l_client_internal->last_parsed_node
+           //        ?l_client_internal->last_parsed_node:"(NULL)", node->tag);
+            if (strcmp(node->tag, "err_str") == 0 ){
+                log_it(L_ERROR,"Error string in reply: '%s'", l_client_internal->last_parsed_node?l_client_internal->last_parsed_node: "(NULL))" );
+
+                l_client_internal->last_error = DAP_CLIENT_ERROR_AUTH_WRONG_CREDENTIALS ;
+                l_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_ERROR;
+            }else if ( strcmp ( node->tag, "cookie") == 0 ){
+                //log_it (L_DEBUG, "Cookie %s", l_client_internal->last_parsed_node?l_client_internal->last_parsed_node:"(NULL)");
+                l_client_internal->auth_cookie = strdup (l_client_internal->last_parsed_node?l_client_internal->last_parsed_node:"(NULL)");
+            }else if ( strcmp ( node->tag, "server_protocol_version" ) == 0  ){
+                if( l_client_internal->last_parsed_node ) {
+                    sscanf(l_client_internal->last_parsed_node,"%u",&l_client_internal->uplink_protocol_version);
+                    if (l_client_internal->uplink_protocol_version == 0){
+                        log_it(L_WARNING, "No uplink protocol version, setting up the default, %u",DAP_PROTOCOL_VERSION);
+                    }
+                    log_it (L_NOTICE, "Uplink protocol version %u", l_client_internal->uplink_protocol_version);
+                }
+
+            }
+            if( l_client_internal->last_parsed_node ){
+                free(l_client_internal->last_parsed_node);
+                l_client_internal->last_parsed_node = NULL;
+            }
+            return true;
+        }break;
+        default: return true;
+        //case XML_EVENT_TEXT: return new_text(text, sd);
+    }
+    return true;
+}
+
+/**
+ * @brief m_auth_error
+ * @param a_client
+ * @param a_error
+ */
+void m_auth_error(dap_client_t * a_client, int a_error)
+{
+    log_it(L_WARNING, "AUTH error %d", a_error);
+    dap_client_internal_t * l_client_internal = DAP_CLIENT_INTERNAL(a_client);
+
+    l_client_internal->last_error = DAP_CLIENT_ERROR_AUTH_WRONG_REPLY ;
+    l_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_ERROR;
+    s_stage_status_after(l_client_internal);
+}
+
+/**
+ * @brief m_stream_ctl_response
+ * @param a_client
+ * @param a_data
+ * @param a_data_size
+ */
+void m_stream_ctl_response(dap_client_t * a_client, void * a_data, size_t a_data_size)
+{
+    dap_client_internal_t * l_client_internal = DAP_CLIENT_INTERNAL(a_client);
+
+    log_it(L_DEBUG, "STREAM_CTL response %u bytes length recieved", a_data_size);
+    char * l_response_str = DAP_NEW_Z_SIZE(char, a_data_size+1);
+    memcpy(l_response_str, a_data,a_data_size);
+
+    if( a_data_size<4 ){
+        log_it(L_ERROR, "STREAM_CTL Wrong reply: '%s'", l_response_str);
+        l_client_internal->last_error = DAP_CLIENT_ERROR_STREAM_CTL_ERROR_RESPONSE_FORMAT;
+        l_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_ERROR;
+        s_stage_status_after(l_client_internal);
+    }else if ( strcmp(l_response_str, "ERROR") == 0 ){
+        log_it(L_WARNING, "STREAM_CTL Got ERROR from the remote site,expecting thats ERROR_AUTH");
+        l_client_internal->last_error = DAP_CLIENT_ERROR_STREAM_CTL_ERROR_AUTH;
+        l_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_ERROR;
+        s_stage_status_after(l_client_internal);
+    }else {
+        int l_arg_count;
+        char l_stream_id[25]={0};
+        char *l_stream_key = DAP_NEW_Z_SIZE(char,4096*3);
+        void * l_stream_key_raw = DAP_NEW_Z_SIZE(char,4096);
+        uint32_t l_remote_protocol_version;
+
+        l_arg_count = sscanf(l_response_str,"%25s %4096s %u"
+                             ,l_stream_id,l_stream_key,&l_remote_protocol_version );
+        if (l_arg_count <2 ){
+            log_it(L_WARNING, "STREAM_CTL Need at least 2 arguments in reply (got %d)",l_arg_count);
+            l_client_internal->last_error = DAP_CLIENT_ERROR_STREAM_CTL_ERROR_RESPONSE_FORMAT;
+            l_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_ERROR;
+            s_stage_status_after(l_client_internal);
+        }else{
+
+            if( l_arg_count >2){
+                l_client_internal->uplink_protocol_version = l_remote_protocol_version;
+                log_it(L_DEBUG,"Uplink protocol version %u",l_remote_protocol_version);
+            }else
+                log_it(L_WARNING,"No uplink protocol version, use the default version %d"
+                       ,l_client_internal->uplink_protocol_version = DAP_PROTOCOL_VERSION);
+
+            if(strlen(l_stream_id)<13){
+                //log_it(L_DEBUG, "Stream server id %s, stream key length(base64 encoded) %u"
+                //       ,l_stream_id,strlen(l_stream_key) );
+                log_it(L_DEBUG, "Stream server id %s, stream key '%s'"
+                       ,l_stream_id,l_stream_key );
+
+                //l_stream_key_raw_size = enc_base64_decode(l_stream_key,strlen(l_stream_key),l_stream_key_raw);
+                // Delete old key if present
+                if(l_client_internal->stream_key)
+                    dap_enc_key_delete(l_client_internal->stream_key);
+
+                strncpy(l_client_internal->stream_id,l_stream_id,sizeof(l_client_internal->stream_id)-1);
+                l_client_internal->stream_key = dap_enc_key_new_from_str(DAP_ENC_KEY_TYPE_AES,l_stream_key);
+
+                //streamSocket->connectToHost(SapSession::me()->address(),SapSession::me()->port().toUShort(),QIODevice::ReadWrite);
+
+                if( l_client_internal->stage == DAP_CLIENT_STAGE_STREAM_CTL ){ // We are on the right stage
+                    l_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_DONE;
+                    s_stage_status_after(l_client_internal);
+                }else{
+                    log_it(L_WARNING,"Expected to be stage STREAM_CTL but current stage is %s (%s)",
+                           dap_client_get_stage_str(a_client),dap_client_get_stage_status_str(a_client));
+
+                }
+            }else{
+                log_it(L_WARNING,"Wrong stream id response");
+                l_client_internal->last_error = DAP_CLIENT_ERROR_STREAM_CTL_ERROR_RESPONSE_FORMAT;
+                l_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_ERROR;
+                s_stage_status_after(l_client_internal);
+            }
+
+        }
+        DAP_DELETE(l_stream_key);
+        DAP_DELETE(l_stream_key_raw);
+    }
+}
+
+/**
+ * @brief m_stream_ctl_error
+ * @param a_client
+ * @param a_error
+ */
+void m_stream_ctl_error(dap_client_t * a_client, int a_error)
+{
+   log_it(L_WARNING, "STREAM_CTL error %d",a_error);
+
+   dap_client_internal_t * l_client_internal = DAP_CLIENT_INTERNAL(a_client);
+
+   l_client_internal->last_error = DAP_CLIENT_ERROR_STREAM_CTL_ERROR;
+   l_client_internal->stage_status = DAP_CLIENT_STAGE_STATUS_ERROR;
+
+   s_stage_status_after(l_client_internal);
+
+}
diff --git a/core_server/client/dap_client_internal.h b/core_server/client/dap_client_internal.h
new file mode 100644
index 0000000..bc91491
--- /dev/null
+++ b/core_server/client/dap_client_internal.h
@@ -0,0 +1,73 @@
+#ifndef _DAP_CLIENT_INTERNAL_H_
+#define _DAP_CLIENT_INTERNAL_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "dap_client.h"
+
+typedef struct dap_events_socket_t dap_events_socket_t;
+typedef struct dap_enc_key dap_enc_key_t;
+typedef struct dap_http_client dap_http_client_t;
+
+typedef struct dap_client_internal
+{
+    dap_client_t * client;
+
+    dap_http_client_t * http_client;
+
+    dap_events_socket_t * es_stream;
+
+    dap_enc_key_t * session_key;
+    dap_enc_key_t * stream_key;
+    char stream_id[25];
+
+    char  * session_key_id;
+    char * auth_cookie;
+
+    char  * uplink_addr;
+    uint16_t uplink_port;
+    char  * uplink_user;
+    char  * uplink_password;
+
+    uint32_t uplink_protocol_version;
+
+    char * last_parsed_node;
+
+
+    dap_client_stage_t stage_target;
+    dap_client_callback_t stage_target_done_callback;
+
+    dap_client_stage_t stage;
+    dap_client_stage_status_t stage_status;
+    dap_client_error_t last_error;
+
+    dap_client_callback_t stage_status_callback;
+
+    dap_client_callback_t stage_status_done_callback;
+    dap_client_callback_t stage_status_error_callback;
+
+    bool is_encrypted;
+    dap_client_callback_data_size_t request_response_callback;
+    dap_client_callback_int_t request_error_callback;
+} dap_client_internal_t;
+
+#define DAP_CLIENT_INTERNAL(a) ((dap_client_internal_t*) a->_internal )
+
+
+int dap_client_internal_init();
+void dap_client_internal_deinit();
+
+void dap_client_internal_stage_transaction_begin(dap_client_internal_t * dap_client_internal_t, dap_client_stage_t a_stage_next,
+                                                 dap_client_callback_t a_done_callback);
+
+void dap_client_internal_request(dap_client_internal_t * a_client_internal, const char * a_path, void * a_request,
+                    size_t a_request_size,  dap_client_callback_data_size_t a_response_proc, dap_client_callback_int_t a_response_error);
+
+void dap_client_internal_request_enc(dap_client_internal_t * a_client_internal, const char * a_path, const char * a_sub_url,
+                                     const char * a_query, void * a_request, size_t a_request_size,
+                                dap_client_callback_data_size_t a_response_proc, dap_client_callback_int_t a_error_proc);
+
+void dap_client_internal_new(dap_client_internal_t * a_client_internal);
+void dap_client_internal_delete(dap_client_internal_t * a_client_internal);
+
+#endif
diff --git a/core_server/client/dap_client_remote.c b/core_server/client/dap_client_remote.c
new file mode 100644
index 0000000..b18eb8b
--- /dev/null
+++ b/core_server/client/dap_client_remote.c
@@ -0,0 +1,266 @@
+/*
+ Copyright (c) 2017-2018 (c) Project "DeM Labs Inc" https://github.com/demlabsinc
+  All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+    DAP (Deus Applicaions Prototypes) 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.
+
+    DAP 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 any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <sys/epoll.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <ev.h>
+
+#include "dap_common.h"
+#include "dap_loop.h"
+#include "dap_client_remote.h"
+
+#ifdef DAP_SERVER
+#include "../dap_server.h"
+#endif
+
+
+
+#define LOG_TAG "dap_client_remote"
+
+
+/**
+ * @brief dap_client_init Init clients module
+ * @return Zero if ok others if no
+ */
+int dap_client_remote_init()
+{
+    log_it(L_NOTICE,"Initialized socket client module");
+    return 0;
+}
+
+/**
+ * @brief dap_client_deinit Deinit clients module
+ */
+void dap_client_remote_deinit()
+{
+
+}
+
+/**
+ * @brief dap_client_remote_create Create new client and add it to the list
+ * @param sh Server instance
+ * @param s Client's socket
+ * @return Pointer to the new list's node
+ */
+dap_client_remote_t * dap_client_create(dap_server_t * sh, int s, ev_io* w_client)
+{
+#ifdef DAP_SERVER
+    pthread_mutex_lock(&sh->mutex_on_hash);
+#endif
+    log_it(L_DEBUG,"Client structure create");
+
+    dap_client_remote_t * ret=DAP_NEW_Z(dap_client_remote_t);
+    ret->socket=s;
+#ifdef DAP_SERVER
+    ret->server=sh;
+#endif
+    ret->watcher_client = w_client;
+    ret->_ready_to_read=true;
+
+#ifdef DAP_SERVER
+    HASH_ADD_INT( sh->clients, socket, ret);
+    if(sh->client_new_callback)
+        sh->client_new_callback(ret,NULL); // Init internal structure
+
+    pthread_mutex_unlock(&sh->mutex_on_hash);
+#endif
+    return ret;
+}
+
+/**
+ * @brief dap_client_find
+ * @param sock
+ * @param sh
+ * @return
+ */
+dap_client_remote_t * dap_client_find(int sock, struct dap_server * sh)
+{
+    dap_client_remote_t * ret=NULL;
+#ifdef DAP_SERVER
+    pthread_mutex_lock(&sh->mutex_on_hash);
+    HASH_FIND_INT(sh->clients,&sock,ret);
+    pthread_mutex_unlock(&sh->mutex_on_hash);
+#endif
+    return ret;
+}
+
+/**
+ * @brief dap_client_ready_to_read
+ * @param sc
+ * @param isReady
+ */
+void dap_client_ready_to_read(dap_client_remote_t * sc,bool is_ready)
+{
+    if(is_ready != sc->_ready_to_read) {
+
+        uint32_t events = 0;
+        sc->_ready_to_read=is_ready;
+
+        if(sc->_ready_to_read)
+            events |= EV_READ;
+
+        if(sc->_ready_to_write)
+            events |= EV_WRITE;
+
+        ev_io_set(sc->watcher_client, sc->socket, events );
+    }
+}
+
+/**
+ * @brief dap_client_ready_to_write
+ * @param sc
+ * @param isReady
+ */
+void dap_client_ready_to_write(dap_client_remote_t * sc,bool is_ready)
+{
+    if(is_ready != sc->_ready_to_write) {
+
+        uint32_t events = 0;
+        sc->_ready_to_write=is_ready;
+
+        if(sc->_ready_to_read)
+            events |= EV_READ;
+
+        if(sc->_ready_to_write)
+            events |= EV_WRITE;
+
+        ev_io_set(sc->watcher_client, sc->socket, events );
+    }
+}
+
+
+/**
+ * @brief safe_client_remove Removes the client from the list
+ * @param sc Client instance
+ */
+void dap_client_remove(dap_client_remote_t *sc, struct dap_server * sh)
+{
+#ifdef DAP_SERVER
+    pthread_mutex_lock(&sh->mutex_on_hash);
+
+    log_it(L_DEBUG, "Client structure remove");
+    HASH_DEL(sc->server->clients,sc);
+
+    if(sc->server->client_delete_callback)
+        sc->server->client_delete_callback(sc,NULL); // Init internal structure
+    if(sc->_inheritor)
+        free(sc->_inheritor);
+
+    if(sc->socket)
+        close(sc->socket);
+    free(sc);
+    pthread_mutex_unlock(&sh->mutex_on_hash);
+#endif
+}
+
+/**
+ * @brief dap_client_write Write data to the client
+ * @param sc Client instance
+ * @param data Pointer to data
+ * @param data_size Size of data to write
+ * @return Number of bytes that were placed into the buffer
+ */
+size_t dap_client_write(dap_client_remote_t *sc, const void * data, size_t data_size)
+{
+     data_size = ((sc->buf_out_size+data_size)<(sizeof(sc->buf_out)))?data_size:(sizeof(sc->buf_out)-sc->buf_out_size );
+     memcpy(sc->buf_out+sc->buf_out_size,data,data_size);
+     sc->buf_out_size+=data_size;
+     return data_size;
+}
+
+/**
+ * @brief dap_client_write_f Write formatted text to the client
+ * @param a_client Client instance
+ * @param a_format Format
+ * @return Number of bytes that were placed into the buffer
+ */
+size_t dap_client_write_f(dap_client_remote_t *a_client, const char * a_format,...)
+{
+    size_t max_data_size = sizeof(a_client->buf_out)-a_client->buf_out_size;
+    va_list ap;
+    va_start(ap,a_format);
+    int ret=vsnprintf(a_client->buf_out+a_client->buf_out_size,max_data_size,a_format,ap);
+    va_end(ap);
+    if(ret>0){
+        a_client->buf_out_size+=ret;
+        return ret;
+    }else{
+        log_it(L_ERROR,"Can't write out formatted data '%s'",a_format);
+        return 0;
+    }
+}
+
+/**
+ * @brief dap_client_read Read data from input buffer
+ * @param sc Client instasnce
+ * @param data Pointer to memory where to store the data
+ * @param data_size Size of data to read
+ * @return Actual bytes number that were read
+ */
+size_t dap_client_read(dap_client_remote_t *sc, void * data, size_t data_size)
+{
+	
+    //log_it(L_DEBUG, "Size of package: %d\n", (int)data_size);
+    //
+    // hexdump(data, data_size);  packet dump
+
+    if (data_size < sc->buf_in_size) {
+        memcpy(data, sc->buf_in, data_size);
+        memmove(data, sc->buf_in + data_size, sc->buf_in_size - data_size);
+    } else {
+        if (data_size > sc->buf_in_size) {
+            data_size = sc->buf_in_size;
+        }
+        memcpy(data, sc->buf_in, data_size);
+    }
+    sc->buf_in_size -= data_size;
+    return data_size;
+}
+
+
+/**
+ * @brief shrink_client_buf_in Shrink input buffer (shift it left)
+ * @param cl Client instance
+ * @param shrink_size Size on wich we shrink the buffer with shifting it left
+ */
+void dap_client_shrink_buf_in(dap_client_remote_t * cl, size_t shrink_size)
+{
+    if((shrink_size==0)||(cl->buf_in_size==0) ){
+        //log_it(L_WARNINGNG, "DBG_#003");
+        return;
+    }else if(cl->buf_in_size>shrink_size){
+        size_t buf_size=cl->buf_in_size-shrink_size;
+        void * buf = malloc(buf_size);
+        memcpy(buf,cl->buf_in+ shrink_size,buf_size );
+        memcpy(cl->buf_in,buf,buf_size);
+        cl->buf_in_size=buf_size;
+        //log_it(L_WARNINGNG, "DBG_#004");
+        free(buf);
+    }else {
+        //log_it(L_WARNINGNG, "DBG_#005");
+        cl->buf_in_size=0;
+    }
+
+}
diff --git a/core_server/client/dap_client_remote.h b/core_server/client/dap_client_remote.h
new file mode 100644
index 0000000..a5782db
--- /dev/null
+++ b/core_server/client/dap_client_remote.h
@@ -0,0 +1,87 @@
+/*
+ Copyright (c) 2017-2018 (c) Project "DeM Labs Inc" https://github.com/demlabsinc
+  All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+    DAP (Deus Applicaions Prototypes) 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.
+
+    DAP 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 any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#pragma once
+#ifndef _DAP_CLIENT_H
+#define _DAP_CLIENT_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include "uthash.h"
+#include <ev.h>
+
+
+typedef struct dap_server dap_server_t;
+struct dap_client_remote;
+
+typedef void (*dap_client_remote_callback_t) (struct dap_client_remote *,void * arg); // Callback for specific client operations
+
+#define DAP_CLIENT_REMOTE_BUF 100000
+
+typedef struct dap_client_remote{
+    int socket;
+    bool signal_close;
+
+    bool _ready_to_write;
+    bool _ready_to_read;
+
+    uint32_t buf_out_zero_count;
+    char buf_in[DAP_CLIENT_REMOTE_BUF+1]; // Internal buffer for input data
+    size_t buf_in_size; // size of data that is in the input buffer
+
+    char buf_out[DAP_CLIENT_REMOTE_BUF+1]; // Internal buffer for output data
+
+    char hostaddr[1024]; // Address
+    char service[128];
+
+    size_t buf_out_size; // size of data that is in the output buffer
+
+    ev_io* watcher_client;
+
+    struct dap_server * server;
+
+    UT_hash_handle hh;
+
+    void * _internal;
+    void * _inheritor; // Internal data to specific client type, usualy states for state machine
+} dap_client_remote_t; // Node of bidirectional list of clients
+
+
+
+int dap_client_remote_init(); //  Init clients module
+void dap_client_remote_deinit(); // Deinit clients module
+
+dap_client_remote_t * dap_client_create(struct dap_server * sh, int s, ev_io* w_client); // Create new client and add it to the list
+dap_client_remote_t * dap_client_find(int sock, struct dap_server * sh); // Find client by socket
+
+bool dap_client_is_ready_to_read(dap_client_remote_t * sc);
+bool dap_client_is_ready_to_write(dap_client_remote_t * sc);
+void dap_client_ready_to_read(dap_client_remote_t * sc,bool is_ready);
+void dap_client_ready_to_write(dap_client_remote_t * sc,bool is_ready);
+
+size_t dap_client_write(dap_client_remote_t *sc, const void * data, size_t data_size);
+size_t dap_client_write_f(dap_client_remote_t *a_client, const char * a_format,...);
+size_t dap_client_read(dap_client_remote_t *sc, void * data, size_t data_size);
+
+void dap_client_remove(dap_client_remote_t *sc, struct dap_server * sh); // Removes the client from the list
+
+void dap_client_shrink_buf_in(dap_client_remote_t * cl, size_t shrink_size);
+
+#endif
diff --git a/core_server/client/sxmlc/sxmlc.c b/core_server/client/sxmlc/sxmlc.c
new file mode 100755
index 0000000..4852416
--- /dev/null
+++ b/core_server/client/sxmlc/sxmlc.c
@@ -0,0 +1,2291 @@
+/*
+	Copyright (c) 2010, Matthieu Labas
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without modification,
+	are permitted provided that the following conditions are met:
+
+	1. Redistributions of source code must retain the above copyright notice,
+	   this list of conditions and the following disclaimer.
+
+	2. Redistributions in binary form must reproduce the above copyright notice,
+	   this list of conditions and the following disclaimer in the documentation
+	   and/or other materials provided with the distribution.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+	ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+	WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+	IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+	INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+	WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+	OF SUCH DAMAGE.
+
+	The views and conclusions contained in the software and documentation are those of the
+	authors and should not be interpreted as representing official policies, either expressed
+	or implied, of the FreeBSD Project.
+*/
+#if defined(WIN32) || defined(WIN64)
+#pragma warning(disable : 4996)
+#else
+#ifndef strdup
+#define _GNU_SOURCE
+#endif
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "sxmlc.h"
+
+/*
+ Struct defining "special" tags such as "<? ?>" or "<![CDATA[ ]]/>".
+ These tags are considered having a start and an end with some data in between that will
+ be stored in the 'tag' member of an XMLNode.
+ The 'tag_type' member is a constant that is associated to such tag.
+ All 'len_*' members are basically the "sx_strlen()" of 'start' and 'end' members.
+ */
+typedef struct _Tag {
+	TagType tag_type;
+	SXML_CHAR* start;
+	int len_start;
+	SXML_CHAR* end;
+	int len_end;
+} _TAG;
+
+typedef struct _SpecialTag {
+	_TAG *tags;
+	int n_tags;
+} SPECIAL_TAG;
+
+/*
+ List of "special" tags handled by sxmlc.
+ NB the "<!DOCTYPE" tag has a special handling because its 'end' changes according
+ to its content ('>' or ']>').
+ */
+static _TAG _spec[] = {
+		{ TAG_INSTR, C2SX("<?"), 2, C2SX("?>"), 2 },
+		{ TAG_COMMENT, C2SX("<!--"), 4, C2SX("-->"), 3 },
+		{ TAG_CDATA, C2SX("<![CDATA["), 9, C2SX("]]>"), 3 }
+};
+static int NB_SPECIAL_TAGS = (int)(sizeof(_spec) / sizeof(_TAG)); /* Auto computation of number of special tags */
+
+/*
+ User-registered tags.
+ */
+static SPECIAL_TAG _user_tags = { NULL, 0 };
+
+int XML_register_user_tag(TagType tag_type, SXML_CHAR* start, SXML_CHAR* end)
+{
+	_TAG* p;
+	int i, n, le;
+
+	if (tag_type < TAG_USER)
+		return -1;
+
+	if (start == NULL || end == NULL || *start != C2SX('<'))
+		return -1;
+
+	le = sx_strlen(end);
+	if (end[le-1] != C2SX('>'))
+		return -1;
+
+	i = _user_tags.n_tags;
+	n = i + 1;
+	p = (_TAG*)__realloc(_user_tags.tags, n * sizeof(_TAG));
+	if (p == NULL)
+		return -1;
+
+	p[i].tag_type = tag_type;
+	p[i].start = start;
+	p[i].end = end;
+	p[i].len_start = sx_strlen(start);
+	p[i].len_end = le;
+	_user_tags.tags = p;
+	_user_tags.n_tags = n;
+
+	return i;
+}
+
+int XML_unregister_user_tag(int i_tag)
+{
+	_TAG* pt;
+
+	if (i_tag < 0 || i_tag >= _user_tags.n_tags)
+ 		return -1;
+
+	if (_user_tags.n_tags == 1)
+		pt = NULL;
+	else {
+		pt = (_TAG*)__malloc((_user_tags.n_tags - 1) * sizeof(_TAG));
+		if (pt == NULL)
+			return -1;
+	}
+ 
+	if (pt != NULL) {
+		memcpy(pt, _user_tags.tags, i_tag * sizeof(_TAG));
+		memcpy(&pt[i_tag], &_user_tags.tags[i_tag + 1], (_user_tags.n_tags - i_tag - 1) * sizeof(_TAG));
+	}
+	if (_user_tags.tags != NULL)
+		__free(_user_tags.tags);
+	_user_tags.tags = pt;
+	_user_tags.n_tags--;
+
+	return _user_tags.n_tags;
+}
+
+int XML_get_nb_registered_user_tags(void)
+{
+	return _user_tags.n_tags;
+}
+
+int XML_get_registered_user_tag(TagType tag_type)
+{
+	int i;
+
+	for (i = 0; i < _user_tags.n_tags; i++)
+		if (_user_tags.tags[i].tag_type == tag_type)
+			return i;
+
+	return -1;
+}
+
+/* --- XMLNode methods --- */
+
+/*
+ Add 'node' to given '*children_array' of '*len_array' elements.
+ '*len_array' is overwritten with the number of elements in '*children_array' after its reallocation.
+ Return the index of the newly added 'node' in '*children_array', or '-1' for memory error.
+ */
+static int _add_node(XMLNode*** children_array, int* len_array, XMLNode* node)
+{
+	XMLNode** pt = (XMLNode**)__realloc(*children_array, (*len_array+1) * sizeof(XMLNode*));
+	
+	if (pt == NULL)
+		return -1;
+	
+	pt[*len_array] = node;
+	*children_array = pt;
+	
+	return (*len_array)++;
+}
+
+int XMLNode_init(XMLNode* node)
+{
+	if (node == NULL)
+		return false;
+	
+	if (node->init_value == XML_INIT_DONE)
+		return true; /*(void)XMLNode_free(node);*/
+
+	node->tag = NULL;
+	node->text = NULL;
+	
+	node->attributes = NULL;
+	node->n_attributes = 0;
+	
+	node->father = NULL;
+	node->children = NULL;
+	node->n_children = 0;
+	
+	node->tag_type = TAG_NONE;
+	node->active = true;
+
+	node->init_value = XML_INIT_DONE;
+
+	return true;
+}
+
+XMLNode* XMLNode_allocN(int n)
+{
+	int i;
+	XMLNode* p;
+	
+	if (n <= 0)
+		return NULL;
+	
+	p = (XMLNode*)__calloc(n, sizeof(XMLNode));
+	if (p == NULL)
+		return NULL;
+
+	for (i = 0; i < n; i++)
+		(void)XMLNode_init(&p[i]);
+	
+	return p;
+}
+
+XMLNode* XMLNode_dup(const XMLNode* node, int copy_children)
+{
+	XMLNode* n;
+
+	if (node == NULL)
+		return NULL;
+
+	n = (XMLNode*)__calloc(1, sizeof(XMLNode));
+	if (n == NULL)
+		return NULL;
+
+	XMLNode_init(n);
+	if (!XMLNode_copy(n, node, copy_children)) {
+		XMLNode_free(n);
+
+		return NULL;
+	}
+
+	return n;
+}
+
+int XMLNode_free(XMLNode* node)
+{
+	if (node == NULL || node->init_value != XML_INIT_DONE)
+		return false;
+	
+	if (node->tag != NULL) {
+		__free(node->tag);
+		node->tag = NULL;
+	}
+
+	XMLNode_remove_text(node);
+	XMLNode_remove_all_attributes(node);
+	XMLNode_remove_children(node);
+	
+	node->tag_type = TAG_NONE;
+
+	return true;
+}
+
+int XMLNode_copy(XMLNode* dst, const XMLNode* src, int copy_children)
+{
+	int i;
+	
+	if (dst == NULL || (src != NULL && src->init_value != XML_INIT_DONE))
+		return false;
+	
+	(void)XMLNode_free(dst); /* 'dst' is freed first */
+	
+	/* NULL 'src' resets 'dst' */
+	if (src == NULL)
+		return true;
+	
+	/* Tag */
+	if (src->tag != NULL) {
+		dst->tag = sx_strdup(src->tag);
+		if (dst->tag == NULL) goto copy_err;
+	}
+
+	/* Text */
+	if (dst->text != NULL) {
+		dst->text = sx_strdup(src->text);
+		if (dst->text == NULL) goto copy_err;
+	}
+
+	/* Attributes */
+	if (src->n_attributes > 0) {
+		dst->attributes = (XMLAttribute*)__calloc(src->n_attributes, sizeof(XMLAttribute));
+		if (dst->attributes== NULL) goto copy_err;
+		dst->n_attributes = src->n_attributes;
+		for (i = 0; i < src->n_attributes; i++) {
+			dst->attributes[i].name = sx_strdup(src->attributes[i].name);
+			dst->attributes[i].value = sx_strdup(src->attributes[i].value);
+			if (dst->attributes[i].name == NULL || dst->attributes[i].value == NULL) goto copy_err;
+			dst->attributes[i].active = src->attributes[i].active;
+		}
+	}
+
+	dst->tag_type = src->tag_type;
+	dst->father = src->father;
+	dst->user = src->user;
+	dst->active = src->active;
+	
+	/* Copy children if required (and there are any) */
+	if (copy_children && src->n_children > 0) {
+		dst->children = (XMLNode**)__calloc(src->n_children, sizeof(XMLNode*));
+		if (dst->children == NULL) goto copy_err;
+		dst->n_children = src->n_children;
+		for (i = 0; i < src->n_children; i++) {
+			if (!XMLNode_copy(dst->children[i], src->children[i], true)) goto copy_err;
+		}
+	}
+	
+	return true;
+	
+copy_err:
+	(void)XMLNode_free(dst);
+	
+	return false;
+}
+
+int XMLNode_set_active(XMLNode* node, int active)
+{
+	if (node == NULL || node->init_value != XML_INIT_DONE)
+		return false;
+
+	node->active = active;
+
+	return true;
+}
+
+int XMLNode_set_tag(XMLNode* node, const SXML_CHAR* tag)
+{
+	SXML_CHAR* newtag;
+	if (node == NULL || tag == NULL || node->init_value != XML_INIT_DONE)
+		return false;
+	
+	newtag = sx_strdup(tag);
+	if (newtag == NULL)
+		return false;
+	if (node->tag != NULL) __free(node->tag);
+	node->tag = newtag;
+
+	return true;
+}
+
+int XMLNode_set_type(XMLNode* node, const TagType tag_type)
+{
+	if (node == NULL || node->init_value != XML_INIT_DONE)
+		return false;
+
+	switch (tag_type) {
+		case TAG_ERROR:
+		case TAG_END:
+		case TAG_PARTIAL:
+		case TAG_NONE:
+			return false;
+
+		default:
+			node->tag_type = tag_type;
+			return true;
+	}
+}
+
+int XMLNode_set_attribute(XMLNode* node, const SXML_CHAR* attr_name, const SXML_CHAR* attr_value)
+{
+	XMLAttribute* pt;
+	int i;
+	
+	if (node == NULL || attr_name == NULL || attr_name[0] == NULC || node->init_value != XML_INIT_DONE)
+		return -1;
+	
+	i = XMLNode_search_attribute(node, attr_name, 0);
+	if (i >= 0) { /* Attribute found: update it */
+		SXML_CHAR* value = NULL;
+		if (attr_value != NULL && (value = sx_strdup(attr_value)) == NULL)
+			return -1;
+		pt = node->attributes;
+		if (pt[i].value != NULL)
+			__free(pt[i].value);
+		pt[i].value = value;
+	} else { /* Attribute not found: add it */
+		SXML_CHAR* name = sx_strdup(attr_name);
+		SXML_CHAR* value = (attr_value == NULL ? NULL : sx_strdup(attr_value));
+		if (name == NULL || (value == NULL && attr_value != NULL)) {
+			if (value != NULL)
+				__free(value);
+			if (name != NULL)
+				__free(name);
+ 			return -1;
+		}
+		i = node->n_attributes;
+		pt = (XMLAttribute*)__realloc(node->attributes, (i+1) * sizeof(XMLAttribute));
+		if (pt == NULL) {
+			if (value != NULL)
+				__free(value);
+			__free(name);
+			return -1;
+		}
+
+		pt[i].name = name;
+		pt[i].value = value;
+		pt[i].active = true;
+		node->attributes = pt;
+		node->n_attributes = i + 1;
+	}
+
+	return node->n_attributes;
+}
+
+int XMLNode_get_attribute_with_default(XMLNode* node, const SXML_CHAR* attr_name, const SXML_CHAR** attr_value, const SXML_CHAR* default_attr_value)
+{
+	XMLAttribute* pt;
+	int i;
+	
+	if (node == NULL || attr_name == NULL || attr_name[0] == NULC || attr_value == NULL || node->init_value != XML_INIT_DONE)
+		return false;
+	
+	i = XMLNode_search_attribute(node, attr_name, 0);
+	if (i >= 0) {
+		pt = node->attributes;
+		if (pt[i].value != NULL) {
+			*attr_value = sx_strdup(pt[i].value);
+			if (*attr_value == NULL)
+				return false;
+		} else
+			*attr_value = NULL; /* NULL but returns 'true' as 'NULL' is the actual attribute value */
+	} else if (default_attr_value != NULL) {
+		*attr_value = sx_strdup(default_attr_value);
+		if (*attr_value == NULL)
+			return false;
+	} else
+		*attr_value = NULL;
+
+	return true;
+}
+
+int XMLNode_get_attribute_count(const XMLNode* node)
+{
+	int i, n;
+
+	if (node == NULL || node->init_value != XML_INIT_DONE)
+		return -1;
+
+	for (i = n = 0; i < node->n_attributes; i++)
+		if (node->attributes[i].active) n++;
+
+	return n;
+}
+
+int XMLNode_search_attribute(const XMLNode* node, const SXML_CHAR* attr_name, int i_search)
+{
+	int i;
+	
+	if (node == NULL || attr_name == NULL || attr_name[0] == NULC || i_search < 0 || i_search >= node->n_attributes)
+		return -1;
+	
+	for (i = i_search; i < node->n_attributes; i++)
+		if (node->attributes[i].active && !sx_strcmp(node->attributes[i].name, attr_name))
+			return i;
+	
+	return -1;
+}
+
+int XMLNode_remove_attribute(XMLNode* node, int i_attr)
+{
+	XMLAttribute* pt;
+	if (node == NULL || node->init_value != XML_INIT_DONE || i_attr < 0 || i_attr >= node->n_attributes)
+		return -1;
+	
+	/* Before modifying first see if we run out of memory */
+	if (node->n_attributes == 1)
+		pt = NULL;
+	else {
+		pt = (XMLAttribute*)__malloc((node->n_attributes - 1) * sizeof(XMLAttribute));
+		if (pt == NULL)
+			return -1;
+	}
+
+	/* Can't fail anymore, free item */
+	if (node->attributes[i_attr].name != NULL) __free(node->attributes[i_attr].name);
+	if (node->attributes[i_attr].value != NULL) __free(node->attributes[i_attr].value);
+	
+	if (pt != NULL) {
+		memcpy(pt, node->attributes, i_attr * sizeof(XMLAttribute));
+		memcpy(&pt[i_attr], &node->attributes[i_attr + 1], (node->n_attributes - i_attr - 1) * sizeof(XMLAttribute));
+	}
+	if (node->attributes != NULL)
+		__free(node->attributes);
+	node->attributes = pt;
+	node->n_attributes--;
+	
+	return node->n_attributes;
+}
+
+int XMLNode_remove_all_attributes(XMLNode* node)
+{
+	int i;
+
+	if (node == NULL || node->init_value != XML_INIT_DONE)
+		return false;
+
+	if (node->attributes != NULL) {
+		for (i = 0; i < node->n_attributes; i++) {
+			if (node->attributes[i].name != NULL)
+				__free(node->attributes[i].name);
+			if (node->attributes[i].value != NULL)
+				__free(node->attributes[i].value);
+		}
+		__free(node->attributes);
+		node->attributes = NULL;
+	}
+	node->n_attributes = 0;
+
+	return true;
+}
+
+int XMLNode_set_text(XMLNode* node, const SXML_CHAR* text)
+{
+	SXML_CHAR* p;
+	if (node == NULL || node->init_value != XML_INIT_DONE)
+		return false;
+
+	if (text == NULL) { /* We want to remove it => free node text */
+		if (node->text != NULL) {
+			__free(node->text);
+			node->text = NULL;
+		}
+
+		return true;
+	}
+
+	p = (SXML_CHAR*)__realloc(node->text, (sx_strlen(text) + 1)*sizeof(SXML_CHAR)); /* +1 for '\0' */
+	if (p == NULL)
+		return false;
+	node->text = p;
+
+	sx_strcpy(node->text, text);
+
+	return true;
+}
+
+int XMLNode_add_child(XMLNode* node, XMLNode* child)
+{
+	if (node == NULL || child == NULL || node->init_value != XML_INIT_DONE || child->init_value != XML_INIT_DONE)
+		return false;
+	
+	if (_add_node(&node->children, &node->n_children, child) >= 0) {
+		node->tag_type = TAG_FATHER;
+		child->father = node;
+		return true;
+	} else
+		return false;
+}
+
+int XMLNode_get_children_count(const XMLNode* node)
+{
+	int i, n;
+
+	if (node == NULL || node->init_value != XML_INIT_DONE)
+		return -1;
+
+	for (i = n = 0; i < node->n_children; i++)
+		if (node->children[i]->active) n++;
+	
+	return n;
+}
+
+XMLNode* XMLNode_get_child(const XMLNode* node, int i_child)
+{
+	int i;
+	
+	if (node == NULL || node->init_value != XML_INIT_DONE || i_child < 0 || i_child >= node->n_children)
+		return NULL;
+	
+	for (i = 0; i < node->n_children; i++) {
+		if (!node->children[i]->active)
+			i_child++;
+		else if (i == i_child)
+			return node->children[i];
+	}
+	
+	return NULL;
+}
+
+int XMLNode_remove_child(XMLNode* node, int i_child, int free_child)
+{
+	int i;
+	XMLNode** pt;
+
+	if (node == NULL || node->init_value != XML_INIT_DONE || i_child < 0 || i_child >= node->n_children)
+		return -1;
+	
+	/* Lookup 'i_child'th active child */
+	for (i = 0; i < node->n_children; i++) {
+		if (!node->children[i]->active)
+			i_child++;
+		else if (i == i_child)
+			break;
+	}
+	if (i >= node->n_children)
+		return -1; /* Children is not found */
+
+	/* Before modifying first see if we run out of memory */
+	if (node->n_children == 1)
+		pt = NULL;
+	else {
+		pt = (XMLNode**)__malloc((node->n_children - 1) * sizeof(XMLNode*));
+		if (pt == NULL)
+			return -1;
+	}
+
+	/* Can't fail anymore, free item */
+	(void)XMLNode_free(node->children[i_child]);
+	if (free_child)
+		__free(node->children[i_child]);
+	
+	if (pt != NULL) {
+		memcpy(pt, node->children, i_child * sizeof(XMLNode*));
+		memcpy(&pt[i_child], &node->children[i_child + 1], (node->n_children - i_child - 1) * sizeof(XMLNode*));
+	}
+	if (node->children != NULL)
+		__free(node->children);
+	node->children = pt;
+	node->n_children--;
+	if (node->n_children == 0)
+		node->tag_type = TAG_SELF;
+	
+	return node->n_children;
+}
+
+int XMLNode_remove_children(XMLNode* node)
+{
+	int i;
+
+	if (node == NULL || node->init_value != XML_INIT_DONE)
+		return false;
+
+	if (node->children != NULL) {
+		for (i = 0; i < node->n_children; i++)
+			if (node->children[i] != NULL) {
+				(void)XMLNode_free(node->children[i]);
+				__free(node->children[i]);
+			}
+		__free(node->children);
+		node->children = NULL;
+	}
+	node->n_children = 0;
+	
+	return true;
+}
+
+int XMLNode_equal(const XMLNode* node1, const XMLNode* node2)
+{
+	int i, j;
+
+	if (node1 == node2)
+		return true;
+
+	if (node1 == NULL || node2 == NULL || node1->init_value != XML_INIT_DONE || node2->init_value != XML_INIT_DONE)
+		return false;
+
+	if (sx_strcmp(node1->tag, node2->tag))
+		return false;
+
+	/* Test all attributes from 'node1' */
+	for (i = 0; i < node1->n_attributes; i++) {
+		if (!node1->attributes[i].active)
+			continue;
+		j = XMLNode_search_attribute(node2, node1->attributes[i].name, 0);
+		if (j < 0)
+			return false;
+		if (sx_strcmp(node1->attributes[i].value, node2->attributes[j].value))
+			return false;
+	}
+
+	/* Test other attributes from 'node2' that might not be in 'node1' */
+	for (i = 0; i < node2->n_attributes; i++) {
+		if (!node2->attributes[i].active)
+			continue;
+		j = XMLNode_search_attribute(node1, node2->attributes[i].name, 0);
+		if (j < 0)
+			return false;
+		if (sx_strcmp(node2->attributes[i].name, node1->attributes[j].name))
+			return false;
+	}
+
+	return true;
+}
+
+XMLNode* XMLNode_next_sibling(const XMLNode* node)
+{
+	int i;
+	XMLNode* father;
+
+	if (node == NULL || node->init_value != XML_INIT_DONE || node->father == NULL)
+		return NULL;
+
+	father = node->father;
+	for (i = 0; i < father->n_children && father->children[i] != node; i++) ;
+	i++; /* father->children[i] is now 'node' next sibling */
+
+	return i < father->n_children ? father->children[i] : NULL;
+}
+
+static XMLNode* _XMLNode_next(const XMLNode* node, int in_children)
+{
+	XMLNode* node2;
+
+	if (node == NULL || node->init_value != XML_INIT_DONE)
+		return NULL;
+
+	/* Check first child */
+	if (in_children && node->n_children > 0)
+		return node->children[0];
+
+	/* Check next sibling */
+	if ((node2 = XMLNode_next_sibling(node)) != NULL)
+		return node2;
+
+	/* Check next uncle */
+	return _XMLNode_next(node->father, false);
+}
+
+XMLNode* XMLNode_next(const XMLNode* node)
+{
+	return _XMLNode_next(node, true);
+}
+
+/* --- XMLDoc methods --- */
+
+int XMLDoc_init(XMLDoc* doc)
+{
+	if (doc == NULL)
+		return false;
+
+	doc->filename[0] = NULC;
+#ifdef SXMLC_UNICODE
+	memset(&doc->bom, 0, sizeof(doc->bom));
+#endif
+	doc->nodes = NULL;
+	doc->n_nodes = 0;
+	doc->i_root = -1;
+	doc->init_value = XML_INIT_DONE;
+
+	return true;
+}
+
+int XMLDoc_free(XMLDoc* doc)
+{
+	int i;
+	
+	if (doc == NULL || doc->init_value != XML_INIT_DONE)
+		return false;
+
+	for (i = 0; i < doc->n_nodes; i++) {
+		(void)XMLNode_free(doc->nodes[i]);
+		__free(doc->nodes[i]);
+	}
+	__free(doc->nodes);
+	doc->nodes = NULL;
+	doc->n_nodes = 0;
+	doc->i_root = -1;
+
+	return true;
+}
+
+int XMLDoc_set_root(XMLDoc* doc, int i_root)
+{
+	if (doc == NULL || doc->init_value != XML_INIT_DONE || i_root < 0 || i_root >= doc->n_nodes)
+		return false;
+	
+	doc->i_root = i_root;
+	
+	return true;
+}
+
+int XMLDoc_add_node(XMLDoc* doc, XMLNode* node)
+{
+	if (doc == NULL || node == NULL || doc->init_value != XML_INIT_DONE)
+		return -1;
+	
+	if (_add_node(&doc->nodes, &doc->n_nodes, node) < 0)
+		return -1;
+
+	if (node->tag_type == TAG_FATHER)
+		doc->i_root = doc->n_nodes - 1; /* Main root node is the last father node */
+
+	return doc->n_nodes;
+}
+
+int XMLDoc_remove_node(XMLDoc* doc, int i_node, int free_node)
+{
+	XMLNode** pt;
+	if (doc == NULL || doc->init_value != XML_INIT_DONE || i_node < 0 || i_node > doc->n_nodes)
+		return false;
+
+	/* Before modifying first see if we run out of memory */
+	if (doc->n_nodes == 1)
+		pt = NULL;
+	else {
+		pt = (XMLNode**)__malloc((doc->n_nodes - 1) * sizeof(XMLNode*));
+		if (pt == NULL)
+			return false;
+	}
+
+	/* Can't fail anymore, free item */
+	(void)XMLNode_free(doc->nodes[i_node]);
+	if (free_node) __free(doc->nodes[i_node]);
+	
+	if (pt != NULL) {
+		memcpy(pt, &doc->nodes[i_node], i_node * sizeof(XMLNode*));
+		memcpy(&pt[i_node], &doc->nodes[i_node + 1], (doc->n_nodes - i_node - 1) * sizeof(XMLNode*));
+	}
+
+	if (doc->nodes != NULL)
+		__free(doc->nodes);
+	doc->nodes = pt;
+	doc->n_nodes--;
+
+	return true;
+}
+
+/*
+ Helper functions to print formatting before a new tag.
+ Returns the new number of characters in the line.
+ */
+static int _count_new_char_line(const SXML_CHAR* str, int nb_char_tab, int cur_sz_line)
+{
+	for (; *str; str++) {
+		if (*str == C2SX('\n'))
+			cur_sz_line = 0;
+		else if (*str == C2SX('\t'))
+			cur_sz_line += nb_char_tab;
+		else
+			cur_sz_line++;
+	}
+	
+	return cur_sz_line;
+}
+static int _print_formatting(const XMLNode* node, FILE* f, const SXML_CHAR* tag_sep, const SXML_CHAR* child_sep, int nb_char_tab, int cur_sz_line)
+{
+	if (tag_sep != NULL) {
+		sx_fprintf(f, tag_sep);
+		cur_sz_line = _count_new_char_line(tag_sep, nb_char_tab, cur_sz_line);
+	}
+	if (child_sep != NULL) {
+		for (node = node->father; node != NULL; node = node->father) {
+			sx_fprintf(f, child_sep);
+			cur_sz_line = _count_new_char_line(child_sep, nb_char_tab, cur_sz_line);
+		}
+	}
+	
+	return cur_sz_line;
+}
+
+static int _XMLNode_print_header(const XMLNode* node, FILE* f, const SXML_CHAR* tag_sep, const SXML_CHAR* child_sep, const SXML_CHAR* attr_sep, int sz_line, int cur_sz_line, int nb_char_tab)
+{
+	int i;
+	SXML_CHAR* p;
+
+	if (node == NULL || f == NULL || !node->active || node->tag == NULL || node->tag[0] == NULC)
+		return -1;
+	
+	/* Special handling of DOCTYPE */
+	if (node->tag_type == TAG_DOCTYPE) {
+		/* Search for an unescaped '[' in the DOCTYPE definition, in which case the end delimiter should be ']>' instead of '>' */
+		for (p = sx_strchr(node->tag, C2SX('[')); p != NULL && *(p-1) == C2SX('\\'); p = sx_strchr(p+1, C2SX('['))) ;
+		cur_sz_line += sx_fprintf(f, C2SX("<!DOCTYPE%s%s>"), node->tag, p != NULL ? C2SX("]") : C2SX(""));
+		return cur_sz_line;
+	}
+
+	/* Check for special tags first */
+	for (i = 0; i < NB_SPECIAL_TAGS; i++) {
+		if (node->tag_type == _spec[i].tag_type) {
+			sx_fprintf(f, C2SX("%s%s%s"), _spec[i].start, node->tag, _spec[i].end);
+			cur_sz_line += sx_strlen(_spec[i].start) + sx_strlen(node->tag) + sx_strlen(_spec[i].end);
+			return cur_sz_line;
+		}
+	}
+
+	/* Check for user tags */
+	for (i = 0; i < _user_tags.n_tags; i++) {
+		if (node->tag_type == _user_tags.tags[i].tag_type) {
+			sx_fprintf(f, C2SX("%s%s%s"), _user_tags.tags[i].start, node->tag, _user_tags.tags[i].end);
+			cur_sz_line += sx_strlen(_user_tags.tags[i].start) + sx_strlen(node->tag) + sx_strlen(_user_tags.tags[i].end);
+			return cur_sz_line;
+		}
+	}
+	
+	/* Print tag name */
+	cur_sz_line += sx_fprintf(f, C2SX("<%s"), node->tag);
+
+	/* Print attributes */
+	for (i = 0; i < node->n_attributes; i++) {
+		if (!node->attributes[i].active)
+			continue;
+		cur_sz_line += sx_strlen(node->attributes[i].name) + sx_strlen(node->attributes[i].value) + 3;
+		if (sz_line > 0 && cur_sz_line > sz_line) {
+			cur_sz_line = _print_formatting(node, f, tag_sep, child_sep, nb_char_tab, cur_sz_line);
+			/* Add extra separator, as if new line was a child of the previous one */
+			if (child_sep != NULL) {
+				sx_fprintf(f, child_sep);
+				cur_sz_line = _count_new_char_line(child_sep, nb_char_tab, cur_sz_line);
+			}
+		}
+		/* Attribute name */
+		if (attr_sep != NULL) {
+			sx_fprintf(f, attr_sep);
+			cur_sz_line = _count_new_char_line(attr_sep, nb_char_tab, cur_sz_line);
+			sx_fprintf(f, C2SX("%s="), node->attributes[i].name);
+		} else
+			sx_fprintf(f, C2SX(" %s="), node->attributes[i].name);
+		
+		/* Attribute value */
+		(void)sx_fputc(XML_DEFAULT_QUOTE, f);
+		cur_sz_line += fprintHTML(f, node->attributes[i].value) + 2;
+		(void)sx_fputc(XML_DEFAULT_QUOTE, f);
+	}
+	
+	/* End the tag if there are no children and no text */
+	if (node->n_children == 0 && (node->text == NULL || node->text[0] == NULC)) {
+		cur_sz_line += sx_fprintf(f, C2SX("/>"));
+	} else {
+		(void)sx_fputc(C2SX('>'), f);
+		cur_sz_line++;
+	}
+
+	return cur_sz_line;
+}
+
+int XMLNode_print_header(const XMLNode* node, FILE* f, int sz_line, int nb_char_tab)
+{
+	return _XMLNode_print_header(node, f, NULL, NULL, NULL, sz_line, 0, nb_char_tab) < 0 ? false : true;
+}
+
+static int _XMLNode_print(const XMLNode* node, FILE* f, const SXML_CHAR* tag_sep, const SXML_CHAR* child_sep, const SXML_CHAR* attr_sep, int keep_text_spaces, int sz_line, int cur_sz_line, int nb_char_tab, int depth)
+{
+	int i;
+	SXML_CHAR* p;
+	
+	if (node != NULL && node->tag_type==TAG_TEXT) { /* Text has to be printed: check if it is only spaces */
+		if (!keep_text_spaces) {
+			for (p = node->text; *p != NULC && sx_isspace(*p); p++) ; /* 'p' points to first non-space character, or to '\0' if only spaces */
+		} else
+			p = node->text; /* '*p' won't be '\0' */
+		if (*p != NULC)
+			cur_sz_line += fprintHTML(f, node->text);
+		return cur_sz_line;
+	}
+
+	if (node == NULL || f == NULL || !node->active || node->tag == NULL || node->tag[0] == NULC)
+		return -1;
+	
+	if (nb_char_tab <= 0)
+		nb_char_tab = 1;
+	
+	/* Print formatting */
+	if (depth < 0) /* UGLY HACK: 'depth' forced negative on very first line so we don't print an extra 'tag_sep' (usually "\n" when pretty-printing) */
+		depth = 0;
+	else
+		cur_sz_line = _print_formatting(node, f, tag_sep, child_sep, nb_char_tab, cur_sz_line);
+	
+	_XMLNode_print_header(node, f, tag_sep, child_sep, attr_sep, sz_line, cur_sz_line, nb_char_tab);
+
+	if (node->text != NULL && node->text[0] != NULC) {
+		/* Text has to be printed: check if it is only spaces */
+		if (!keep_text_spaces) {
+			for (p = node->text; *p != NULC && sx_isspace(*p); p++) ; /* 'p' points to first non-space character, or to '\0' if only spaces */
+		} else
+			p = node->text; /* '*p' won't be '\0' */
+		if (*p != NULC) cur_sz_line += fprintHTML(f, node->text);
+	} else if (node->n_children <= 0) /* Everything has already been printed */
+		return cur_sz_line;
+	
+	/* Recursively print children */
+	for (i = 0; i < node->n_children; i++)
+		(void)_XMLNode_print(node->children[i], f, tag_sep, child_sep, attr_sep, keep_text_spaces, sz_line, cur_sz_line, nb_char_tab, depth+1);
+	
+	/* Print tag end after children */
+		/* Print formatting */
+	if (node->n_children > 0)
+		cur_sz_line = _print_formatting(node, f, tag_sep, child_sep, nb_char_tab, cur_sz_line);
+	cur_sz_line += sx_fprintf(f, C2SX("</%s>"), node->tag);
+
+	return cur_sz_line;
+}
+
+int XMLNode_print_attr_sep(const XMLNode* node, FILE* f, const SXML_CHAR* tag_sep, const SXML_CHAR* child_sep, const SXML_CHAR* attr_sep, int keep_text_spaces, int sz_line, int nb_char_tab)
+{
+	return _XMLNode_print(node, f, tag_sep, child_sep, attr_sep, keep_text_spaces, sz_line, 0, nb_char_tab, 0);
+}
+
+int XMLDoc_print_attr_sep(const XMLDoc* doc, FILE* f, const SXML_CHAR* tag_sep, const SXML_CHAR* child_sep, const SXML_CHAR* attr_sep, int keep_text_spaces, int sz_line, int nb_char_tab)
+{
+	int i, depth, cur_sz_line;
+	
+	if (doc == NULL || f == NULL || doc->init_value != XML_INIT_DONE)
+		return false;
+	
+#ifdef SXMLC_UNICODE
+	/* Write BOM if it exist */
+	if (doc->sz_bom > 0) fwrite(doc->bom, sizeof(unsigned char), doc->sz_bom, f);
+#endif
+
+	depth = -1; /* UGLY HACK: 'depth' forced negative on very first line so we don't print an extra 'tag_sep' (usually "\n") */
+	for (i = 0, cur_sz_line = 0; i < doc->n_nodes; i++) {
+		cur_sz_line = _XMLNode_print(doc->nodes[i], f, tag_sep, child_sep, attr_sep, keep_text_spaces, sz_line, cur_sz_line, nb_char_tab, depth);
+		depth = 0;
+	}
+	/* TODO: Find something more graceful than 'depth=-1', even though everyone knows I probably never will ;) */
+
+	return true;
+}
+
+/* --- */
+
+int XML_parse_attribute_to(const SXML_CHAR* str, int to, XMLAttribute* xmlattr)
+{
+	const SXML_CHAR *p;
+	int i, n0, n1, remQ = 0;
+	int ret = 1;
+	SXML_CHAR quote = '\0';
+	
+	if (str == NULL || xmlattr == NULL)
+		return 0;
+
+	if (to < 0)
+		to = sx_strlen(str) - 1;
+	
+	/* Search for the '=' */
+	/* 'n0' is where the attribute name stops, 'n1' is where the attribute value starts */
+	for (n0 = 0; n0 != to && str[n0] != C2SX('=') && !sx_isspace(str[n0]); n0++) ; /* Search for '=' or a space */
+	for (n1 = n0; n1 != to && sx_isspace(str[n1]); n1++) ; /* Search for something not a space */
+	if (str[n1] != C2SX('='))
+		return 0; /* '=' not found: malformed string */
+	for (n1++; n1 != to && sx_isspace(str[n1]); n1++) ; /* Search for something not a space */
+	if (isquote(str[n1])) { /* Remove quotes */
+		quote = str[n1];
+		remQ = 1;
+	}
+	
+	xmlattr->name = (SXML_CHAR*)__malloc((n0+1)*sizeof(SXML_CHAR));
+	xmlattr->value = (SXML_CHAR*)__malloc((to+1 - n1 - remQ + 1) * sizeof(SXML_CHAR));
+	xmlattr->active = true;
+	if (xmlattr->name != NULL && xmlattr->value != NULL) {
+		/* Copy name */
+		sx_strncpy(xmlattr->name, str, n0);
+		xmlattr->name[n0] = NULC;
+		/* (void)str_unescape(xmlattr->name); do not unescape the name */
+		/* Copy value (p starts after the quote (if any) and stops at the end of 'str'
+		  (skipping the quote if any, hence the '*(p+remQ)') */
+		for (i = 0, p = str + n1 + remQ; i + n1 + remQ < to && *(p+remQ) != NULC; i++, p++)
+			xmlattr->value[i] = *p;
+		xmlattr->value[i] = NULC;
+		(void)html2str(xmlattr->value, NULL); /* Convert HTML escape sequences, do not str_unescape(xmlattr->value) */
+		if (remQ && *p != quote)
+			ret = 2; /* Quote at the beginning but not at the end: probable presence of '>' inside attribute value, so we need to read more data! */
+	} else
+		ret = 0;
+	
+	if (ret == 0) {
+		if (xmlattr->name != NULL) {
+			__free(xmlattr->name);
+			xmlattr->name = NULL;
+		}
+		if (xmlattr->value != NULL) {
+			__free(xmlattr->value);
+			xmlattr->value = NULL;
+		}
+	}
+	
+	return ret;
+}
+
+static TagType _parse_special_tag(const SXML_CHAR* str, int len, _TAG* tag, XMLNode* node)
+{
+	if (sx_strncmp(str, tag->start, tag->len_start))
+		return TAG_NONE;
+
+	if (sx_strncmp(str + len - tag->len_end, tag->end, tag->len_end)) /* There probably is a '>' inside the tag */
+		return TAG_PARTIAL;
+
+	node->tag = (SXML_CHAR*)__malloc((len - tag->len_start - tag->len_end + 1)*sizeof(SXML_CHAR));
+	if (node->tag == NULL)
+		return TAG_NONE;
+	sx_strncpy(node->tag, str + tag->len_start, len - tag->len_start - tag->len_end);
+	node->tag[len - tag->len_start - tag->len_end] = NULC;
+	node->tag_type = tag->tag_type;
+
+	return node->tag_type;
+}
+
+/*
+ Reads a string that is supposed to be an xml tag like '<tag (attribName="attribValue")* [/]>' or '</tag>'.
+ Fills the 'xmlnode' structure with the tag name and its attributes.
+ Returns 'TAG_ERROR' if an error occurred (malformed 'str' or memory). 'TAG_*' when string is recognized.
+ */
+TagType XML_parse_1string(const SXML_CHAR* str, XMLNode* xmlnode)
+{
+	SXML_CHAR *p;
+	XMLAttribute* pt;
+	int n, nn, len, rc, tag_end = 0;
+	
+	if (str == NULL || xmlnode == NULL)
+		return TAG_ERROR;
+	len = sx_strlen(str);
+	
+	/* Check for malformed string */
+	if (str[0] != C2SX('<') || str[len-1] != C2SX('>'))
+		return TAG_ERROR;
+
+	for (nn = 0; nn < NB_SPECIAL_TAGS; nn++) {
+		n = (int)_parse_special_tag(str, len, &_spec[nn], xmlnode);
+		switch (n) {
+			case TAG_NONE:	break;				/* Nothing found => do nothing */
+			default:		return (TagType)n;	/* Tag found => return it */
+		}
+	}
+
+	/* "<!DOCTYPE" requires a special handling because it can end with "]>" instead of ">" if a '[' is found inside */
+	if (str[1] == C2SX('!')) {
+		/* DOCTYPE */
+		if (!sx_strncmp(str, C2SX("<!DOCTYPE"), 9)) { /* 9 = sizeof("<!DOCTYPE") */
+			for (n = 9; str[n] && str[n] != C2SX('['); n++) ; /* Look for a '[' inside the DOCTYPE, which would mean that we should be looking for a "]>" tag end */
+			nn = 0;
+			if (str[n]) { /* '[' was found */
+				if (sx_strncmp(str+len-2, C2SX("]>"), 2)) /* There probably is a '>' inside the DOCTYPE */
+					return TAG_PARTIAL;
+				nn = 1;
+			}
+			xmlnode->tag = (SXML_CHAR*)__malloc((len - 9 - nn)*sizeof(SXML_CHAR)); /* 'len' - "<!DOCTYPE" and ">" + '\0' */
+			if (xmlnode->tag == NULL)
+				return TAG_ERROR;
+			sx_strncpy(xmlnode->tag, &str[9], len - 10 - nn);
+			xmlnode->tag[len - 10 - nn] = NULC;
+			xmlnode->tag_type = TAG_DOCTYPE;
+
+			return TAG_DOCTYPE;
+		}
+	}
+	
+	/* Test user tags */
+	for (nn = 0; nn < _user_tags.n_tags; nn++) {
+		n = _parse_special_tag(str, len, &_user_tags.tags[nn], xmlnode);
+		switch (n) {
+			case TAG_ERROR:	return TAG_NONE;	/* Error => exit */
+			case TAG_NONE:	break;				/* Nothing found => do nothing */
+			default:		return (TagType)n;	/* Tag found => return it */
+		}
+	}
+
+	if (str[1] == C2SX('/'))
+		tag_end = 1;
+	
+	/* tag starts at index 1 (or 2 if tag end) and ends at the first space or '/>' */
+	for (n = 1 + tag_end; str[n] != NULC && str[n] != C2SX('>') && str[n] != C2SX('/') && !sx_isspace(str[n]); n++) ;
+	xmlnode->tag = (SXML_CHAR*)__malloc((n - tag_end)*sizeof(SXML_CHAR));
+	if (xmlnode->tag == NULL)
+		return TAG_ERROR;
+	sx_strncpy(xmlnode->tag, &str[1 + tag_end], n - 1 - tag_end);
+	xmlnode->tag[n - 1 - tag_end] = NULC;
+	if (tag_end) {
+		xmlnode->tag_type = TAG_END;
+		return TAG_END;
+	}
+	
+	/* Here, 'n' is the position of the first space after tag name */
+	while (n < len) {
+		/* Skips spaces */
+		while (sx_isspace(str[n])) n++;
+		
+		/* Check for XML end ('>' or '/>') */
+		if (str[n] == C2SX('>')) { /* Tag with children */
+			int type = (str[n-1] == '/' ? TAG_SELF : TAG_FATHER); // TODO: Find something better to cope with <tag attr=v/>
+			xmlnode->tag_type = type;
+			return type;
+		}
+		if (!sx_strcmp(str+n, C2SX("/>"))) { /* Tag without children */
+			xmlnode->tag_type = TAG_SELF;
+			return TAG_SELF;
+		}
+		
+		/* New attribute found */
+		p = sx_strchr(str+n, C2SX('='));
+		if (p == NULL) goto parse_err;
+		pt = (XMLAttribute*)__realloc(xmlnode->attributes, (xmlnode->n_attributes + 1) * sizeof(XMLAttribute));
+		if (pt == NULL) goto parse_err;
+		
+		pt[xmlnode->n_attributes].name = NULL;
+		pt[xmlnode->n_attributes].value = NULL;
+		pt[xmlnode->n_attributes].active = false;
+		xmlnode->n_attributes++;
+		xmlnode->attributes = pt;
+		while (*p != NULC && sx_isspace(*++p)) ; /* Skip spaces */
+		if (isquote(*p)) { /* Attribute value starts with a quote, look for next one, ignoring protected ones with '\' */
+			for (nn = p-str+1; str[nn] && str[nn] != *p; nn++) { // CHECK UNICODE "nn = p-str+1"
+				/* if (str[nn] == C2SX('\\')) nn++; [bugs:#7]: '\' is valid in values */
+			}
+		} else { /* Attribute value stops at first space or end of XML string */
+			for (nn = p-str+1; str[nn] != NULC && !sx_isspace(str[nn]) && str[nn] != C2SX('/') && str[nn] != C2SX('>'); nn++) ; /* Go to the end of the attribute value */ // CHECK UNICODE
+		}
+		
+		/* Here 'str[nn]' is the character after value */
+		/* the attribute definition ('attrName="attrVal"') is between 'str[n]' and 'str[nn]' */
+		rc = XML_parse_attribute_to(&str[n], nn - n, &xmlnode->attributes[xmlnode->n_attributes - 1]);
+		if (!rc) goto parse_err;
+		if (rc == 2) { /* Probable presence of '>' inside attribute value, which is legal XML. Remove attribute to re-parse it later */
+			XMLNode_remove_attribute(xmlnode, xmlnode->n_attributes - 1);
+			return TAG_PARTIAL;
+		}
+		
+		n = nn + 1;
+	}
+	
+	sx_fprintf(stderr, C2SX("\nWE SHOULD NOT BE HERE!\n[%s]\n\n"), str);
+	
+parse_err:
+	(void)XMLNode_free(xmlnode);
+
+	return TAG_ERROR;
+}
+
+static int _parse_data_SAX(void* in, const DataSourceType in_type, const SAX_Callbacks* sax, SAX_Data* sd)
+{
+	SXML_CHAR *line = NULL, *txt_end, *p;
+	XMLNode node;
+	int ret, exit, sz, n0, ncr;
+	TagType tag_type;
+	int (*meos)(void* ds) = (in_type == DATA_SOURCE_BUFFER ? (int(*)(void*))_beob : (int(*)(void*))sx_feof);
+
+	if (sax->start_doc != NULL && !sax->start_doc(sd))
+		return true;
+	if (sax->all_event != NULL && !sax->all_event(XML_EVENT_START_DOC, NULL, (SXML_CHAR*)sd->name, 0, sd))
+		return true;
+
+	ret = true;
+	exit = false;
+	sd->line_num = 1; /* Line counter, starts at 1 */
+	sz = 0; /* 'line' buffer size */
+	node.init_value = 0;
+	(void)XMLNode_init(&node);
+	while ((n0 = read_line_alloc(in, in_type, &line, &sz, 0, NULC, C2SX('>'), true, C2SX('\n'), &ncr)) != 0) {
+		(void)XMLNode_free(&node);
+		for (p = line; *p != NULC && sx_isspace(*p); p++) ; /* Checks if text is only spaces */
+		if (*p == NULC)
+			break;
+		sd->line_num += ncr;
+
+		/* Get text for 'father' (i.e. what is before '<') */
+		while ((txt_end = sx_strchr(line, C2SX('<'))) == NULL) { /* '<' was not found, indicating a probable '>' inside text (should have been escaped with '&gt;' but we'll handle that ;) */
+			int n1 = read_line_alloc(in, in_type, &line, &sz, n0, 0, C2SX('>'), true, C2SX('\n'), &ncr); /* Go on reading the file from current position until next '>' */
+			sd->line_num += ncr;
+			if (n1 <= n0) {
+				ret = false;
+				if (sax->on_error == NULL && sax->all_event == NULL)
+					sx_fprintf(stderr, C2SX("%s:%d: MEMORY ERROR.\n"), sd->name, sd->line_num);
+				else {
+					if (sax->on_error != NULL && !sax->on_error(PARSE_ERR_MEMORY, sd->line_num, sd))
+						break;
+					if (sax->all_event != NULL && !sax->all_event(XML_EVENT_ERROR, NULL, (SXML_CHAR*)sd->name, PARSE_ERR_MEMORY, sd))
+						break;
+				}
+				break; /* 'txt_end' is still NULL here so we'll display the syntax error below */
+			}
+			n0 = n1;
+		}
+		if (txt_end == NULL) { /* Missing tag start */
+			ret = false;
+			if (sax->on_error == NULL && sax->all_event == NULL)
+				sx_fprintf(stderr, C2SX("%s:%d: ERROR: Unexpected end character '>', without matching '<'!\n"), sd->name, sd->line_num);
+			else {
+				if (sax->on_error != NULL && !sax->on_error(PARSE_ERR_UNEXPECTED_TAG_END, sd->line_num, sd))
+					break;
+				if (sax->all_event != NULL && !sax->all_event(XML_EVENT_ERROR, NULL, (SXML_CHAR*)sd->name, PARSE_ERR_UNEXPECTED_TAG_END, sd))
+					break;
+			}
+			break;
+		}
+		/* First part of 'line' (before '<') is to be added to 'father->text' */
+		*txt_end = NULC; /* Have 'line' be the text for 'father' */
+		if (*line != NULC && (sax->new_text != NULL || sax->all_event != NULL)) {
+			if (sax->new_text != NULL && (exit = !sax->new_text(line, sd))) /* no str_unescape(line) */
+				break;
+			if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_TEXT, NULL, line, sd->line_num, sd)))
+				break;
+		}
+		*txt_end = '<'; /* Restores tag start */
+
+		switch (tag_type = XML_parse_1string(txt_end, &node)) {
+			case TAG_ERROR: /* Memory error */
+				ret = false;
+				if (sax->on_error == NULL && sax->all_event == NULL)
+					sx_fprintf(stderr, C2SX("%s:%d: MEMORY ERROR.\n"), sd->name, sd->line_num);
+				else {
+					if (sax->on_error != NULL && (exit = !sax->on_error(PARSE_ERR_MEMORY, sd->line_num, sd)))
+						break;
+					if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_ERROR, NULL, (SXML_CHAR*)sd->name, PARSE_ERR_MEMORY, sd)))
+						break;
+				}
+				break;
+		
+			case TAG_NONE: /* Syntax error */
+				ret = false;
+				p = sx_strchr(txt_end, C2SX('\n'));
+				if (p != NULL)
+					*p = NULC;
+				if (sax->on_error == NULL && sax->all_event == NULL) {
+					sx_fprintf(stderr, C2SX("%s:%d: SYNTAX ERROR (%s%s).\n"), sd->name, sd->line_num, txt_end, p == NULL ? C2SX("") : C2SX("..."));
+					if (p != NULL)
+						*p = C2SX('\n');
+				} else {
+					if (sax->on_error != NULL && (exit = !sax->on_error(PARSE_ERR_SYNTAX, sd->line_num, sd)))
+						break;
+					if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_ERROR, NULL, (SXML_CHAR*)sd->name, PARSE_ERR_SYNTAX, sd)))
+						break;
+				}
+				break;
+
+			case TAG_END:
+				if (sax->end_node != NULL || sax->all_event != NULL) {
+					if (sax->end_node != NULL && (exit = !sax->end_node(&node, sd)))
+						break;
+					if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_END_NODE, &node, NULL, sd->line_num, sd)))
+						break;
+				}
+				break;
+
+			default: /* Add 'node' to 'father' children */
+				/* If the line looks like a comment (or CDATA) but is not properly finished, loop until we find the end. */
+				while (tag_type == TAG_PARTIAL) {
+					int n1 = read_line_alloc(in, in_type, &line, &sz, n0, NULC, C2SX('>'), true, C2SX('\n'), &ncr); /* Go on reading the file from current position until next '>' */
+					sd->line_num += ncr;
+					if (n1 <= n0) {
+						ret = false;
+						if (sax->on_error == NULL && sax->all_event == NULL)
+							sx_fprintf(stderr, C2SX("%s:%d: SYNTAX ERROR.\n"), sd->name, sd->line_num);
+						else {
+							if (sax->on_error != NULL && (exit = !sax->on_error(meos(in) ? PARSE_ERR_EOF : PARSE_ERR_MEMORY, sd->line_num, sd)))
+								break;
+							if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_ERROR, NULL, (SXML_CHAR*)sd->name, meos(in) ? PARSE_ERR_EOF : PARSE_ERR_MEMORY, sd)))
+								break;
+						}
+						break;
+					}
+					n0 = n1;
+					txt_end = sx_strchr(line, C2SX('<')); /* In case 'line' has been moved by the '__realloc' in 'read_line_alloc' */
+					tag_type = XML_parse_1string(txt_end, &node);
+					if (tag_type == TAG_ERROR) {
+						ret = false;
+						if (sax->on_error == NULL && sax->all_event == NULL)
+							sx_fprintf(stderr, C2SX("%s:%d: PARSE ERROR.\n"), sd->name, sd->line_num);
+						else {
+							if (sax->on_error != NULL && (exit = !sax->on_error(meos(in) ? PARSE_ERR_EOF : PARSE_ERR_SYNTAX, sd->line_num, sd)))
+								break;
+							if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_ERROR, NULL, (SXML_CHAR*)sd->name, meos(in) ? PARSE_ERR_EOF : PARSE_ERR_SYNTAX, sd)))
+								break;
+						}
+						break;
+					}
+				}
+				if (ret == false)
+					break;
+				if (sax->start_node != NULL && (exit = !sax->start_node(&node, sd)))
+					break;
+				if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_START_NODE, &node, NULL, sd->line_num, sd)))
+					break;
+				if (node.tag_type != TAG_FATHER && (sax->end_node != NULL || sax->all_event != NULL)) {
+					if (sax->end_node != NULL && (exit = !sax->end_node(&node, sd)))
+						break;
+					if (sax->all_event != NULL && (exit = !sax->all_event(XML_EVENT_END_NODE, &node, NULL, sd->line_num, sd)))
+						break;
+				}
+			break;
+		}
+		if (exit == true || ret == false || meos(in))
+			break;
+	}
+	__free(line);
+	(void)XMLNode_free(&node);
+
+	if (sax->end_doc != NULL && !sax->end_doc(sd))
+		return ret;
+	if (sax->all_event != NULL)
+		(void)sax->all_event(XML_EVENT_END_DOC, NULL, (SXML_CHAR*)sd->name, sd->line_num, sd);
+
+	return ret;
+}
+
+int SAX_Callbacks_init(SAX_Callbacks* sax)
+{
+	if (sax == NULL)
+		return false;
+
+	sax->start_doc = NULL;
+	sax->start_node = NULL;
+	sax->end_node = NULL;
+	sax->new_text = NULL;
+	sax->on_error = NULL;
+	sax->end_doc = NULL;
+	sax->all_event = NULL;
+
+	return true;
+}
+
+int DOMXMLDoc_doc_start(SAX_Data* sd)
+{
+	DOM_through_SAX* dom = (DOM_through_SAX*)sd->user;
+
+	dom->current = NULL;
+	dom->error = PARSE_ERR_NONE;
+	dom->line_error = 0;
+
+	return true;
+}
+
+int DOMXMLDoc_node_start(const XMLNode* node, SAX_Data* sd)
+{
+	DOM_through_SAX* dom = (DOM_through_SAX*)sd->user;
+	XMLNode* new_node;
+	int i;
+
+	if ((new_node = XMLNode_dup(node, true)) == NULL) goto node_start_err; /* No real need to put 'true' for 'XMLNode_dup', but cleaner */
+	
+	if (dom->current == NULL) {
+		if ((i = _add_node(&dom->doc->nodes, &dom->doc->n_nodes, new_node)) < 0) goto node_start_err;
+
+		if (dom->doc->i_root < 0 && (node->tag_type == TAG_FATHER || node->tag_type == TAG_SELF))
+			dom->doc->i_root = i;
+	} else {
+		if (_add_node(&dom->current->children, &dom->current->n_children, new_node) < 0) goto node_start_err;
+	}
+
+	new_node->father = dom->current;
+	dom->current = new_node;
+
+	return true;
+
+node_start_err:
+	dom->error = PARSE_ERR_MEMORY;
+	dom->line_error = sd->line_num;
+	(void)XMLNode_free(new_node);
+	__free(new_node);
+
+	return false;
+}
+
+int DOMXMLDoc_node_end(const XMLNode* node, SAX_Data* sd)
+{
+	DOM_through_SAX* dom = (DOM_through_SAX*)sd->user;
+
+	if (dom->current == NULL || sx_strcmp(dom->current->tag, node->tag)) {
+		sx_fprintf(stderr, C2SX("%s:%d: ERROR - End tag </%s> was unexpected"), sd->name, sd->line_num, node->tag);
+		if (dom->current != NULL)
+			sx_fprintf(stderr, C2SX(" (</%s> was expected)\n"), dom->current->tag);
+		else
+			sx_fprintf(stderr, C2SX(" (no node to end)\n"));
+
+		dom->error = PARSE_ERR_UNEXPECTED_NODE_END;
+		dom->line_error = sd->line_num;
+
+		return false;
+	}
+
+	dom->current = dom->current->father;
+
+	return true;
+}
+
+int DOMXMLDoc_node_text(SXML_CHAR* text, SAX_Data* sd)
+{
+	SXML_CHAR* p = text;
+	DOM_through_SAX* dom = (DOM_through_SAX*)sd->user;
+
+	/* Keep text, even if it is only spaces */
+#if 0
+	while(*p != NULC && sx_isspace(*p++)) ;
+	if (*p == NULC) return true; /* Only spaces */
+#endif
+
+	/* If there is no current node to add text to, raise an error, except if text is only spaces, in which case it is probably just formatting */
+	if (dom->current == NULL) {
+		while(*p != NULC && sx_isspace(*p++)) ;
+		if (*p == NULC) /* Only spaces => probably pretty-printing */
+			return true;
+		dom->error = PARSE_ERR_TEXT_OUTSIDE_NODE;
+		dom->line_error = sd->line_num;
+		return false; /* There is some "real" text => raise an error */
+	}
+
+	if (dom->text_as_nodes) {
+		XMLNode* new_node = XMLNode_allocN(1);
+		if (new_node == NULL || (new_node->text = sx_strdup(text)) == NULL
+			|| _add_node(&dom->current->children, &dom->current->n_children, new_node) < 0) {
+			dom->error = PARSE_ERR_MEMORY;
+			dom->line_error = sd->line_num;
+			(void)XMLNode_free(new_node);
+			__free(new_node);
+			return false;
+		}
+		new_node->tag_type = TAG_TEXT;
+		new_node->father = dom->current;
+		//dom->current->tag_type = TAG_FATHER; // OS: should parent field be forced to be TAG_FATHER? now it has at least one TAG_TEXT child. I decided not to enforce this to enforce backward-compatibility related to tag_types
+		return true;
+	} else { /* Old behaviour: concatenate text to the previous one */
+		/* 'p' will point at the new text */
+		if (dom->current->text == NULL) {
+			p = sx_strdup(text);
+		} else {
+			p = (SXML_CHAR*)__realloc(dom->current->text, (sx_strlen(dom->current->text) + sx_strlen(text) + 1)*sizeof(SXML_CHAR));
+			if (p != NULL)
+				sx_strcat(p, text);
+		}
+		if (p == NULL) {
+			dom->error = PARSE_ERR_MEMORY;
+			dom->line_error = sd->line_num;
+			return false;
+		}
+		
+		dom->current->text = p;
+	}
+
+	return true;
+}
+
+int DOMXMLDoc_parse_error(ParseError error_num, int line_number, SAX_Data* sd)
+{
+	DOM_through_SAX* dom = (DOM_through_SAX*)sd->user;
+
+	dom->error = error_num;
+	dom->line_error = line_number;
+
+	/* Complete error message will be displayed in 'DOMXMLDoc_doc_end' callback */
+
+	return false; /* Stop on error */
+}
+
+int DOMXMLDoc_doc_end(SAX_Data* sd)
+{
+	DOM_through_SAX* dom = (DOM_through_SAX*)sd->user;
+
+	if (dom->error != PARSE_ERR_NONE) {
+		SXML_CHAR* msg;
+
+		switch (dom->error) {
+			case PARSE_ERR_MEMORY:				msg = C2SX("MEMORY"); break;
+			case PARSE_ERR_UNEXPECTED_TAG_END:	msg = C2SX("UNEXPECTED_TAG_END"); break;
+			case PARSE_ERR_SYNTAX:				msg = C2SX("SYNTAX"); break;
+			case PARSE_ERR_EOF:					msg = C2SX("UNEXPECTED_END_OF_FILE"); break;
+			case PARSE_ERR_TEXT_OUTSIDE_NODE:	msg = C2SX("TEXT_OUTSIDE_NODE"); break;
+			case PARSE_ERR_UNEXPECTED_NODE_END:	msg = C2SX("UNEXPECTED_NODE_END"); break;
+			default:							msg = C2SX("UNKNOWN"); break;
+		}
+		sx_fprintf(stderr, C2SX("%s:%d: An error was found (%s), loading aborted...\n"), sd->name, dom->line_error, msg);
+		dom->current = NULL;
+		(void)XMLDoc_free(dom->doc);
+		dom->doc = NULL;
+	}
+
+	return true;
+}
+
+int SAX_Callbacks_init_DOM(SAX_Callbacks* sax)
+{
+	if (sax == NULL)
+		return false;
+
+	sax->start_doc = DOMXMLDoc_doc_start;
+	sax->start_node = DOMXMLDoc_node_start;
+	sax->end_node = DOMXMLDoc_node_end;
+	sax->new_text = DOMXMLDoc_node_text;
+	sax->on_error = DOMXMLDoc_parse_error;
+	sax->end_doc = DOMXMLDoc_doc_end;
+	sax->all_event = NULL;
+
+	return true;
+}
+
+int XMLDoc_parse_file_SAX(const SXML_CHAR* filename, const SAX_Callbacks* sax, void* user)
+{
+	FILE* f;
+	int ret;
+	SAX_Data sd;
+	SXML_CHAR* fmode = 
+#ifndef SXMLC_UNICODE
+	C2SX("rt");
+#else
+	C2SX("rb"); /* In Unicode, open the file as binary so that further 'fgetwc' read all bytes */
+	BOM_TYPE bom;
+#endif
+
+
+	if (sax == NULL || filename == NULL || filename[0] == NULC)
+		return false;
+
+	f = sx_fopen(filename, fmode);
+	if (f == NULL)
+		return false;
+	/* Microsoft' 'ftell' returns invalid position for Unicode text files
+	   (see http://connect.microsoft.com/VisualStudio/feedback/details/369265/ftell-ftell-nolock-incorrectly-handling-unicode-text-translation)
+	   However, we're opening the file as binary in Unicode so we don't fall into that case...
+	*/
+	#if defined(SXMLC_UNICODE) && (defined(WIN32) || defined(WIN64))
+	//setvbuf(f, NULL, _IONBF, 0);
+	#endif
+
+	sd.name = (SXML_CHAR*)filename;
+	sd.user = user;
+#ifdef SXMLC_UNICODE
+	bom = freadBOM(f, NULL, NULL); /* Skip BOM, if any */
+	/* In Unicode, re-open the file in text-mode if there is no BOM (or UTF-8) as we assume that
+	   the file is "plain" text (i.e. 1 byte = 1 character). If opened in binary mode, 'fgetwc'
+	   would read 2 bytes for 1 character, which would not work on "plain" files. */
+	if (bom == BOM_NONE || bom == BOM_UTF_8) {
+		sx_fclose(f);
+		f = sx_fopen(filename, C2SX("rt"));
+		if (f == NULL)
+			return false;
+		if (bom == BOM_UTF_8)
+			freadBOM(f, NULL, NULL); /* Skip the UTF-8 BOM that was found */
+	}
+#endif
+	ret = _parse_data_SAX((void*)f, DATA_SOURCE_FILE, sax, &sd);
+	(void)sx_fclose(f);
+
+	return ret;
+}
+
+int XMLDoc_parse_buffer_SAX(const SXML_CHAR* buffer, const SXML_CHAR* name, const SAX_Callbacks* sax, void* user)
+{
+	DataSourceBuffer dsb = { buffer, 0 };
+	SAX_Data sd;
+
+	if (sax == NULL || buffer == NULL)
+		return false;
+
+	sd.name = name;
+	sd.user = user;
+	return _parse_data_SAX((void*)&dsb, DATA_SOURCE_BUFFER, sax, &sd);
+}
+
+int XMLDoc_parse_file_DOM_text_as_nodes(const SXML_CHAR* filename, XMLDoc* doc, int text_as_nodes)
+{
+	DOM_through_SAX dom;
+	SAX_Callbacks sax;
+
+	if (doc == NULL || filename == NULL || filename[0] == NULC || doc->init_value != XML_INIT_DONE)
+		return false;
+
+	sx_strncpy(doc->filename, filename, SXMLC_MAX_PATH - 1);
+	doc->filename[SXMLC_MAX_PATH - 1] = NULC;
+
+	/* Read potential BOM on file, only when unicode is defined */
+#ifdef SXMLC_UNICODE
+	{
+		/* In Unicode, open the file as binary so that further 'fgetwc' read all bytes */
+		FILE* f = sx_fopen(filename, C2SX("rb"));
+		if (f != NULL) {
+			#if defined(SXMLC_UNICODE) && (defined(WIN32) || defined(WIN64))
+			//setvbuf(f, NULL, _IONBF, 0);
+			#endif
+			doc->bom_type = freadBOM(f, doc->bom, &doc->sz_bom);
+			sx_fclose(f);
+		}
+	}
+#endif
+
+	dom.doc = doc;
+	dom.current = NULL;
+	dom.text_as_nodes = text_as_nodes;
+	SAX_Callbacks_init_DOM(&sax);
+
+	if (!XMLDoc_parse_file_SAX(filename, &sax, &dom)) {
+		(void)XMLDoc_free(doc);
+		dom.doc = NULL;
+		return false;
+	}
+
+	return true;
+}
+
+int XMLDoc_parse_buffer_DOM_text_as_nodes(const SXML_CHAR* buffer, const SXML_CHAR* name, XMLDoc* doc, int text_as_nodes)
+{
+	DOM_through_SAX dom;
+	SAX_Callbacks sax;
+
+	if (doc == NULL || buffer == NULL || doc->init_value != XML_INIT_DONE)
+		return false;
+
+	dom.doc = doc;
+	dom.current = NULL;
+	dom.text_as_nodes = text_as_nodes;
+	SAX_Callbacks_init_DOM(&sax);
+
+	return XMLDoc_parse_buffer_SAX(buffer, name, &sax, &dom) ? true : XMLDoc_free(doc);
+}
+
+
+
+/* --- Utility functions (ex sxmlutils.c) --- */
+
+#ifdef DBG_MEM
+static int nb_alloc = 0, nb_free = 0;
+
+void* __malloc(size_t sz)
+{
+	void* p = malloc(sz);
+	if (p != NULL)
+		nb_alloc++;
+	printf("0x%x: MALLOC (%d) - NA %d - NF %d = %d\n", p, sz, nb_alloc, nb_free, nb_alloc - nb_free);
+	return p;
+}
+
+void* __calloc(size_t count, size_t sz)
+{
+	void* p = calloc(count, sz);
+	if (p != NULL)
+		nb_alloc++;
+	printf("0x%x: CALLOC (%d, %d) - NA %d - NF %d = %d\n", p, count, sz, nb_alloc, nb_free, nb_alloc - nb_free);
+	return p;
+}
+
+void* __realloc(void* mem, size_t sz)
+{
+	void* p = realloc(mem, sz);
+	if (mem == NULL && p != NULL)
+		nb_alloc++;
+	else if (mem != NULL && sz == 0)
+		nb_free++;
+	printf("0x%x: REALLOC 0x%x (%d)", p, mem, sz);
+	if (mem == NULL)
+		printf(" - NA %d - NF %d = %d", nb_alloc, nb_free, nb_alloc - nb_free);
+	printf("\n");
+	return p;
+}
+
+void __free(void* mem)
+{
+	nb_free++;
+	printf("0x%x: FREE - NA %d - NF %d = %d\n", mem, nb_alloc, nb_free, nb_alloc - nb_free);
+	free(mem);
+}
+
+char* __sx_strdup(const char* s)
+{
+/* Mimic the behavior of sx_strdup(), as we can't use it directly here: DBG_MEM is defined
+   and sx_strdup is this function! (bug #5) */
+#ifdef SXMLC_UNICODE
+	char* p = wcsdup(s);
+#else
+	char* p = strdup(s);
+#endif
+	if (p != NULL)
+		nb_alloc++;
+	printf("0x%x: STRDUP (%d) - NA %d - NF %d = %d\n", p, sx_strlen(s), nb_alloc, nb_free, nb_alloc - nb_free);
+	return p;
+}
+#endif
+
+/* Dictionary of special characters and their HTML equivalent */
+static struct _html_special_dict {
+	SXML_CHAR chr;		/* Original character */
+	SXML_CHAR* html;	/* Equivalent HTML string */
+	int html_len;	/* 'sx_strlen(html)' */
+} HTML_SPECIAL_DICT[] = {
+	{ C2SX('<'), C2SX("&lt;"), 4 },
+	{ C2SX('>'), C2SX("&gt;"), 4 },
+	{ C2SX('"'), C2SX("&quot;"), 6 },
+	{ C2SX('\''), C2SX("&apos;"), 6 },
+	{ C2SX('&'), C2SX("&amp;"), 5 },
+	{ NULC, NULL, 0 }, /* Terminator */
+};
+
+int _bgetc(DataSourceBuffer* ds)
+{
+	if (ds == NULL || ds->buf[ds->cur_pos] == NULC)
+		return EOF;
+	
+	return (int)(ds->buf[ds->cur_pos++]);
+}
+
+int _beob(DataSourceBuffer* ds)
+{
+
+	if (ds == NULL || ds->buf[ds->cur_pos] == NULC)
+		return true;
+
+	return false;
+}
+
+int read_line_alloc(void* in, DataSourceType in_type, SXML_CHAR** line, int* sz_line, int i0, SXML_CHAR from, SXML_CHAR to, int keep_fromto, SXML_CHAR interest, int* interest_count)
+{
+	int init_sz = 0;
+	SXML_CHAR ch, *pt;
+	int c;
+	int n, ret;
+	int (*mgetc)(void* ds) = (in_type == DATA_SOURCE_BUFFER ? (int(*)(void*))_bgetc : (int(*)(void*))sx_fgetc);
+	int (*meos)(void* ds) = (in_type == DATA_SOURCE_BUFFER ? (int(*)(void*))_beob : (int(*)(void*))sx_feof);
+	
+	if (in == NULL || line == NULL)
+		return 0;
+	
+	if (to == NULC)
+		to = C2SX('\n');
+	/* Search for character 'from' */
+	if (interest_count != NULL)
+		*interest_count = 0;
+	while (true) {
+		/* Reaching EOF before 'to' char is not an error but should trigger 'line' alloc and init to '' */
+		c = mgetc(in);
+		ch = (SXML_CHAR)c;
+		if (c == EOF)
+			break;
+		if (interest_count != NULL && ch == interest)
+			(*interest_count)++;
+		/* If 'from' is '\0', we stop here */
+		if (ch == from || from == NULC)
+			break;
+	}
+	
+	if (sz_line == NULL)
+		sz_line = &init_sz;
+	
+	if (*line == NULL || *sz_line == 0) {
+		if (*sz_line == 0) *sz_line = MEM_INCR_RLA;
+		*line = (SXML_CHAR*)__malloc(*sz_line*sizeof(SXML_CHAR));
+		if (*line == NULL)
+			return 0;
+	}
+	if (i0 < 0)
+		i0 = 0;
+	if (i0 > *sz_line)
+		return 0;
+	
+	n = i0;
+	if (c == CEOF) { /* EOF reached before 'to' char => return the empty string */
+		(*line)[n] = NULC;
+		return meos(in) ? n : 0; /* Error if not EOF */
+	}
+	if (ch != from || keep_fromto)
+		(*line)[n++] = ch;
+	(*line)[n] = NULC;
+	ret = 0;
+	while (true) {
+		if ((c = mgetc(in)) == CEOF) { /* EOF or error */
+			(*line)[n] = NULC;
+			ret = meos(in) ? n : 0;
+			break;
+		}
+		ch = (SXML_CHAR)c;
+		if (interest_count != NULL && ch == interest)
+			(*interest_count)++;
+		(*line)[n] = ch;
+		if (ch != to || (keep_fromto && to != NULC && ch == to)) /* If we reached the 'to' character and we keep it, we still need to add the extra '\0' */
+			n++;
+		if (n >= *sz_line) { /* Too many characters for our line => realloc some more */
+			*sz_line += MEM_INCR_RLA;
+			pt = (SXML_CHAR*)__realloc(*line, *sz_line*sizeof(SXML_CHAR));
+			if (pt == NULL) {
+				ret = 0;
+				break;
+			} else
+				*line = pt;
+		}
+		(*line)[n] = NULC; /* If we reached the 'to' character and we want to strip it, 'n' hasn't changed and 'line[n]' (which is 'to') will be replaced by '\0' */
+		if (ch == to) {
+			ret = n;
+			break;
+		}
+	}
+	
+#if 0 /* Automatic buffer resize is deactivated */
+	/* Resize line to the exact size */
+	pt = (SXML_CHAR*)__realloc(*line, (n+1)*sizeof(SXML_CHAR));
+	if (pt != NULL)
+		*line = pt;
+#endif
+	
+	return ret;
+}
+
+/* --- */
+
+SXML_CHAR* strcat_alloc(SXML_CHAR** src1, const SXML_CHAR* src2)
+{
+	SXML_CHAR* cat;
+	int n;
+
+	/* Do not concatenate '*src1' with itself */
+	if (src1 == NULL || *src1 == src2)
+		return NULL;
+
+	/* Concatenate a NULL or empty string */
+	if (src2 == NULL || *src2 == NULC)
+		return *src1;
+
+	n = (*src1 == NULL ? 0 : sx_strlen(*src1)) + sx_strlen(src2) + 1;
+	cat = (SXML_CHAR*)__realloc(*src1, n*sizeof(SXML_CHAR));
+	if (cat == NULL)
+		return NULL;
+	if (*src1 == NULL)
+		*cat = NULC;
+	*src1 = cat;
+	sx_strcat(*src1, src2);
+
+	return *src1;
+}
+
+SXML_CHAR* strip_spaces(SXML_CHAR* str, SXML_CHAR repl_sq)
+{
+	SXML_CHAR* p;
+	int i, len;
+	
+	/* 'p' to the first non-space */
+	for (p = str; *p != NULC && sx_isspace(*p); p++) ; /* No need to search for 'protect' as it is not a space */
+	len = sx_strlen(str);
+	for (i = len-1; sx_isspace(str[i]); i--) ;
+	if (str[i] == C2SX('\\')) /* If last non-space is the protection, keep the last space */
+		i++;
+	str[i+1] = NULC; /* New end of string to last non-space */
+	
+	if (repl_sq == NULC) {
+		if (p == str && i == len)
+			return str; /* Nothing to do */
+		for (i = 0; (str[i] = *p) != NULC; i++, p++) ; /* Copy 'p' to 'str' */
+		return str;
+	}
+	
+	/* Squeeze all spaces with 'repl_sq' */
+	i = 0;
+	while (*p != NULC) {
+		if (sx_isspace(*p)) {
+			str[i++] = repl_sq;
+			while (sx_isspace(*++p)) ; /* Skips all next spaces */
+		} else {
+			if (*p == C2SX('\\'))
+				p++;
+			str[i++] = *p++;
+		}
+	}
+	str[i] = NULC;
+	
+	return str;
+}
+
+SXML_CHAR* str_unescape(SXML_CHAR* str)
+{
+	int i, j;
+
+	if (str == NULL)
+		return NULL;
+
+	for (i = j = 0; str[j]; j++) {
+		if (str[j] == C2SX('\\'))
+			j++;
+		str[i++] = str[j];
+	}
+
+	return str;
+}
+
+int split_left_right(SXML_CHAR* str, SXML_CHAR sep, int* l0, int* l1, int* i_sep, int* r0, int* r1, int ignore_spaces, int ignore_quotes)
+{
+	int n0, n1, is;
+	SXML_CHAR quote = '\0';
+
+	if (str == NULL)
+		return false;
+
+	if (i_sep != NULL)
+		*i_sep = -1;
+
+	if (!ignore_spaces) /* No sense of ignore quotes if spaces are to be kept */
+		ignore_quotes = false;
+
+	/* Parse left part */
+
+	if (ignore_spaces) {
+		for (n0 = 0; str[n0] != NULC && sx_isspace(str[n0]); n0++) ; /* Skip head spaces, n0 points to first non-space */
+		if (ignore_quotes && isquote(str[n0])) { /* If quote is found, look for next one */
+			quote = str[n0++]; /* Quote can be '\'' or '"' */
+			for (n1 = n0; str[n1] != NULC && str[n1] != quote; n1++) {
+				if (str[n1] == C2SX('\\') && str[++n1] == NULC)
+					break; /* Escape character (can be the last) */
+			}
+			for (is = n1 + 1; str[is] != NULC && sx_isspace(str[is]); is++) ; /* '--' not to take quote into account */
+		} else {
+			for (n1 = n0; str[n1] != NULC && str[n1] != sep && !sx_isspace(str[n1]); n1++) ; /* Search for separator or a space */
+			for (is = n1; str[is] != NULC && sx_isspace(str[is]); is++) ;
+		}
+	} else {
+		n0 = 0;
+		for (n1 = 0; str[n1] != NULC && str[n1] != sep; n1++) ; /* Search for separator only */
+		if (str[n1] != sep) /* Separator not found: malformed string */
+			return false;
+		is = n1;
+	}
+
+	/* Here 'n0' is the start of left member, 'n1' is the character after the end of left member */
+
+	if (l0 != NULL)
+		*l0 = n0;
+	if (l1 != NULL)
+		*l1 = n1 - 1;
+	if (i_sep != NULL)
+		*i_sep = is;
+	if (str[is] == NULC || str[is+1] == NULC) { /* No separator => empty right member */
+		if (r0 != NULL)
+			*r0 = is;
+		if (r1 != NULL)
+			*r1 = is-1;
+		if (i_sep != NULL)
+			*i_sep = (str[is] == NULC ? -1 : is);
+		return true;
+	}
+
+	/* Parse right part */
+
+	n0 = is + 1;
+	if (ignore_spaces) {
+		for (; str[n0] != NULC && sx_isspace(str[n0]); n0++) ;
+		if (ignore_quotes && isquote(str[n0]))
+			quote = str[n0];
+	}
+
+	for (n1 = ++n0; str[n1]; n1++) {
+		if (ignore_quotes && str[n1] == quote) /* Quote was reached */
+			break;
+		if (str[n1] == C2SX('\\') && str[++n1] == NULC) /* Escape character (can be the last) */
+			break;
+	}
+	if (ignore_quotes && str[n1--] != quote) /* Quote is not the same than earlier, '--' is not to take it into account */
+		return false;
+	if (!ignore_spaces)
+		while (str[++n1]) ; /* Jump down the end of the string */
+
+	if (r0 != NULL)
+		*r0 = n0;
+	if (r1 != NULL)
+		*r1 = n1;
+
+	return true;
+}
+
+BOM_TYPE freadBOM(FILE* f, unsigned char* bom, int* sz_bom)
+{
+	unsigned char c1, c2;
+	long pos;
+
+	if (f == NULL)
+		return BOM_NONE;
+
+	/* Save position and try to read and skip BOM if found. If not, go back to save position. */
+	pos = ftell(f);
+	if (pos < 0)
+		return BOM_NONE;
+	if (fread(&c1, sizeof(char), 1, f) != 1 || fread(&c2, sizeof(char), 1, f) != 1) {
+		fseek(f, pos, SEEK_SET);
+		return BOM_NONE;
+	}
+	if (bom != NULL) {
+		bom[0] = c1;
+		bom[1] = c2;
+		bom[2] = '\0';
+		if (sz_bom != NULL)
+			*sz_bom = 2;
+	}
+	switch ((unsigned short)(c1 << 8) | c2) {
+		case (unsigned short)0xfeff:
+			return BOM_UTF_16BE;
+
+		case (unsigned short)0xfffe:
+			pos = ftell(f); /* Save current position to get it back if BOM is not UTF-32LE */
+			if (pos < 0)
+				return BOM_UTF_16LE;
+			if (fread(&c1, sizeof(char), 1, f) != 1 || fread(&c2, sizeof(char), 1, f) != 1) {
+				fseek(f, pos, SEEK_SET);
+				return BOM_UTF_16LE;
+			}
+			if (c1 == 0x00 && c2 == 0x00) {
+				if (bom != NULL)
+					bom[2] = bom[3] = bom[4] = '\0';
+				if (sz_bom != NULL)
+					*sz_bom = 4;
+				return BOM_UTF_32LE;
+			}
+			fseek(f, pos, SEEK_SET); /* fseek(f, -2, SEEK_CUR) is not garanteed on Windows (and actually fail in Unicode...) */
+			return BOM_UTF_16LE;
+
+		case (unsigned short)0x0000:
+			if (fread(&c1, sizeof(char), 1, f) == 1 && fread(&c2, sizeof(char), 1, f) == 1
+					&& c1 == 0xfe && c2 == 0xff) {
+				bom[2] = c1;
+				bom[3] = c2;
+				bom[4] = '\0';
+				if (sz_bom != NULL)
+					*sz_bom = 4;
+				return BOM_UTF_32BE;
+			}
+			fseek(f, pos, SEEK_SET);
+			return BOM_NONE;
+
+		case (unsigned short)0xefbb: /* UTF-8? */
+			if (fread(&c1, sizeof(char), 1, f) != 1 || c1 != 0xbf) { /* Not UTF-8 */
+				fseek(f, pos, SEEK_SET);
+				if (bom != NULL)
+					bom[0] = '\0';
+				if (sz_bom != NULL)
+					*sz_bom = 0;
+				return BOM_NONE;
+			}
+			if (bom != NULL) {
+				bom[2] = c1;
+				bom[3] = '\0';
+			}
+			if (sz_bom != NULL)
+				*sz_bom = 3;
+			return BOM_UTF_8;
+
+		default: /* No BOM, go back */
+			fseek(f, pos, SEEK_SET);
+			if (bom != NULL)
+				bom[0] = '\0';
+			if (sz_bom != NULL)
+				*sz_bom = 0;
+			return BOM_NONE;
+	}
+}
+
+/* --- */
+
+SXML_CHAR* html2str(SXML_CHAR* html, SXML_CHAR* str)
+{
+	SXML_CHAR *ps, *pd;
+	int i;
+
+	if (html == NULL) return NULL;
+
+	if (str == NULL) str = html;
+	
+	/* Look for '&' and matches it to any of the recognized HTML pattern. */
+	/* If found, replaces the '&' by the corresponding char. */
+	/* 'p2' is the char to analyze, 'p1' is where to insert it */
+	for (pd = str, ps = html; *ps; ps++, pd++) {
+		if (*ps != C2SX('&')) {
+			if (pd != ps)
+				*pd = *ps;
+			continue;
+		}
+		
+		for (i = 0; HTML_SPECIAL_DICT[i].chr; i++) {
+			if (sx_strncmp(ps, HTML_SPECIAL_DICT[i].html, HTML_SPECIAL_DICT[i].html_len))
+				continue;
+			
+			*pd = HTML_SPECIAL_DICT[i].chr;
+			ps += HTML_SPECIAL_DICT[i].html_len-1;
+			break;
+		}
+		/* If no string was found, simply copy the character */
+		if (HTML_SPECIAL_DICT[i].chr == NULC && pd != ps)
+			*pd = *ps;
+	}
+	*pd = NULC;
+	
+	return str;
+}
+
+/* TODO: Allocate 'html'? */
+SXML_CHAR* str2html(SXML_CHAR* str, SXML_CHAR* html)
+{
+	SXML_CHAR *ps, *pd;
+	int i;
+
+	if (str == NULL)
+		return NULL;
+
+	if (html == str) /* Not handled (yet) */
+		return NULL;
+
+	if (html == NULL) { /* Allocate 'html' to the correct size */
+		html = __malloc(strlen_html(str) * sizeof(SXML_CHAR));
+		if (html == NULL)
+			return NULL;
+	}
+
+	for (ps = str, pd = html; *ps; ps++, pd++) {
+		for (i = 0; HTML_SPECIAL_DICT[i].chr; i++) {
+			if (*ps == HTML_SPECIAL_DICT[i].chr) {
+				sx_strcpy(pd, HTML_SPECIAL_DICT[i].html);
+				pd += HTML_SPECIAL_DICT[i].html_len - 1;
+				break;
+			}
+		}
+		if (HTML_SPECIAL_DICT[i].chr == NULC && pd != ps)
+			*pd = *ps;
+	}
+	*pd = NULC;
+
+	return html;
+}
+
+int strlen_html(SXML_CHAR* str)
+{
+	int i, j, n;
+	
+	if (str == NULL)
+		return 0;
+
+	n = 0;
+	for (i = 0; str[i] != NULC; i++) {
+		for (j = 0; HTML_SPECIAL_DICT[j].chr; j++) {
+			if (str[i] == HTML_SPECIAL_DICT[j].chr) {
+				n += HTML_SPECIAL_DICT[j].html_len;
+				break;
+			}
+		}
+		if (HTML_SPECIAL_DICT[j].chr == NULC)
+			n++;
+	}
+
+	return n;
+}
+
+int fprintHTML(FILE* f, SXML_CHAR* str)
+{
+	SXML_CHAR* p;
+	int i, n;
+	
+	for (p = str, n = 0; *p != NULC; p++) {
+		for (i = 0; HTML_SPECIAL_DICT[i].chr; i++) {
+			if (*p != HTML_SPECIAL_DICT[i].chr)
+				continue;
+			sx_fprintf(f, HTML_SPECIAL_DICT[i].html);
+			n += HTML_SPECIAL_DICT[i].html_len;
+			break;
+		}
+		if (HTML_SPECIAL_DICT[i].chr == NULC) {
+			(void)sx_fputc(*p, f);
+			n++;
+		}
+	}
+	
+	return n;
+}
+
+int regstrcmp(SXML_CHAR* str, SXML_CHAR* pattern)
+{
+	SXML_CHAR *p, *s;
+
+	if (str == NULL && pattern == NULL)
+		return true;
+
+	if (str == NULL || pattern == NULL)
+		return false;
+
+	p = pattern;
+	s = str;
+	while (true) {
+		switch (*p) {
+			/* Any character matches, go to next one */
+			case C2SX('?'):
+				p++;
+				s++;
+				break;
+
+			/* Go to next character in pattern and wait until it is found in 'str' */
+			case C2SX('*'):
+				for (; *p != NULC; p++) { /* Squeeze '**?*??**' to '*' */
+					if (*p != C2SX('*') && *p != C2SX('?'))
+						break;
+				}
+				for (; *s != NULC; s++) {
+					if (*s == *p)
+						break;
+				}
+				break;
+
+			/* NULL character on pattern has to be matched by 'str' */
+			case 0:
+				return *s ? false : true;
+
+			default:
+				if (*p == C2SX('\\')) /* Escape character */
+					p++;
+				if (*p++ != *s++) /* Characters do not match */
+					return false;
+				break;
+		}
+	}
+
+	return false;
+}
diff --git a/core_server/client/sxmlc/sxmlc.h b/core_server/client/sxmlc/sxmlc.h
new file mode 100755
index 0000000..91aef71
--- /dev/null
+++ b/core_server/client/sxmlc/sxmlc.h
@@ -0,0 +1,829 @@
+/*
+	Copyright (c) 2010, Matthieu Labas
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without modification,
+	are permitted provided that the following conditions are met:
+
+	1. Redistributions of source code must retain the above copyright notice,
+	   this list of conditions and the following disclaimer.
+
+	2. Redistributions in binary form must reproduce the above copyright notice,
+	   this list of conditions and the following disclaimer in the documentation
+	   and/or other materials provided with the distribution.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+	ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+	WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+	IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+	INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+	WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+	OF SUCH DAMAGE.
+
+	The views and conclusions contained in the software and documentation are those of the
+	authors and should not be interpreted as representing official policies, either expressed
+	or implied, of the FreeBSD Project.
+*/
+#ifndef _SXML_H_
+#define _SXML_H_
+
+#define SXMLC_VERSION "4.2.7"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+
+#ifdef SXMLC_UNICODE
+	typedef wchar_t SXML_CHAR;
+	#define C2SX(c) L ## c
+	#define CEOF WEOF
+	#define sx_strcmp wcscmp
+	#define sx_strncmp wcsncmp
+	#define sx_strlen wcslen
+	#define sx_strdup wcsdup
+	#define sx_strchr wcschr
+	#define sx_strrchr wcsrchr
+	#define sx_strcpy wcscpy
+	#define sx_strncpy wcsncpy
+	#define sx_strcat wcscat
+	#define sx_printf wprintf
+	#define sx_fprintf fwprintf
+	#define sx_sprintf swprintf
+	#define sx_fgetc fgetwc
+	#define sx_fputc fputwc
+	#define sx_isspace iswspace
+	#if defined(WIN32) || defined(WIN64)
+		#define sx_fopen _wfopen
+	#else
+		#define sx_fopen fopen
+	#endif
+	#define sx_fclose fclose
+	#define sx_feof feof
+#else
+	typedef char SXML_CHAR;
+	#define C2SX(c) c
+	#define CEOF EOF
+	#define sx_strcmp strcmp
+	#define sx_strncmp strncmp
+	#define sx_strlen strlen
+	#define sx_strdup __sx_strdup
+	#define sx_strchr strchr
+	#define sx_strrchr strrchr
+	#define sx_strcpy strcpy
+	#define sx_strncpy strncpy
+	#define sx_strcat strcat
+	#define sx_printf printf
+	#define sx_fprintf fprintf
+	#define sx_sprintf sprintf
+	#define sx_fgetc fgetc
+	#define sx_fputc fputc
+	#define sx_isspace(ch) isspace((int)ch)
+	#define sx_fopen fopen
+	#define sx_fclose fclose
+	#define sx_feof feof
+#endif
+
+#ifdef DBG_MEM
+	void* __malloc(size_t sz);
+	void* __calloc(size_t count, size_t sz);
+	void* __realloc(void* mem, size_t sz);
+	void __free(void* mem);
+	char* __sx_strdup(const char* s);
+#else
+	#define __malloc malloc
+	#define __calloc calloc
+	#define __realloc realloc
+	#define __free free
+	#define __sx_strdup strdup
+#endif
+
+#ifndef MEM_INCR_RLA
+#define MEM_INCR_RLA (256*sizeof(SXML_CHAR)) /* Initial buffer size and increment for memory reallocations */
+#endif
+
+#ifndef false
+#define false 0
+#endif
+
+#ifndef true
+#define true 1
+#endif
+
+#define NULC ((SXML_CHAR)C2SX('\0'))
+#define isquote(c) (((c) == C2SX('"')) || ((c) == C2SX('\'')))
+
+/*
+ Buffer data source used by 'read_line_alloc' when required.
+ 'buf' should be 0-terminated.
+ */
+typedef struct _DataSourceBuffer {
+	const SXML_CHAR* buf;
+	int cur_pos;
+} DataSourceBuffer;
+
+typedef FILE* DataSourceFile;
+
+typedef enum _DataSourceType {
+	DATA_SOURCE_FILE = 0,
+	DATA_SOURCE_BUFFER,
+	DATA_SOURCE_MAX
+} DataSourceType;
+
+#ifndef false
+#define false 0
+#endif
+
+#ifndef true
+#define true 1
+#endif
+
+/* Node types */
+typedef enum _TagType {
+	TAG_ERROR = -1,
+	TAG_NONE = 0,
+	TAG_PARTIAL,	/* Node containing a legal '>' that stopped file reading */
+	TAG_FATHER,		/* <tag> - Next nodes will be children of this one. */
+	TAG_SELF,		/* <tag/> - Standalone node. */
+	TAG_INSTR,		/* <?prolog?> - Processing instructions, or prolog node. */
+	TAG_COMMENT,	/* <!--comment--> */
+	TAG_CDATA,		/* <![CDATA[ ]]> - CDATA node */
+	TAG_DOCTYPE,	/* <!DOCTYPE [ ]> - DOCTYPE node */
+	TAG_END,		/* </tag> - End of father node. */
+	TAG_TEXT,		/* text node*/
+
+	TAG_USER = 100	/* User-defined tag start */
+} TagType;
+
+/* TODO: Performance improvement with some fixed-sized strings ??? (e.g. XMLAttribute.name[64], XMLNode.tag[64]) */
+
+typedef struct _XMLAttribute {
+	SXML_CHAR* name;
+	SXML_CHAR* value;
+	int active;
+} XMLAttribute;
+
+/* Constant to know whether a struct has been initialized (XMLNode or XMLDoc) */
+#define XML_INIT_DONE 0x19770522 /* Happy Birthday ;) */
+
+/*
+ An XML node.
+ */
+typedef struct _XMLNode {
+	SXML_CHAR* tag;				/* Tag name */
+	SXML_CHAR* text;			/* Text inside the node */
+	XMLAttribute* attributes;
+	int n_attributes;
+	
+	struct _XMLNode* father;	/* NULL if root */
+	struct _XMLNode** children;
+	int n_children;
+	
+	TagType tag_type;	/* Node type ('TAG_FATHER', 'TAG_SELF' or 'TAG_END') */
+	int active;		/* 'true' to tell that node is active and should be displayed by 'XMLDoc_print' */
+
+	void* user;	/* Pointer for user data associated to the node */
+
+	/* Keep 'init_value' as the last member */
+	int init_value;	/* Initialized to 'XML_INIT_DONE' to indicate that node has been initialized properly */
+} XMLNode;
+
+/*
+ An XML document.
+ */
+#ifndef SXMLC_MAX_PATH
+#define SXMLC_MAX_PATH 256
+#endif
+typedef struct _XMLDoc {
+	SXML_CHAR filename[SXMLC_MAX_PATH];
+#ifdef SXMLC_UNICODE
+	BOM_TYPE bom_type;
+	unsigned char bom[5];	/* First characters read that might be a BOM when unicode is used */
+	int sz_bom;				/* Number of bytes in BOM */
+#endif
+	XMLNode** nodes;		/* Nodes of the document, including prolog, comments and root nodes */
+	int n_nodes;			/* Number of nodes in 'nodes' */
+	int i_root;				/* Index of first root node in 'nodes', -1 if document is empty */
+
+	/* Keep 'init_value' as the last member */
+	int init_value;	/* Initialized to 'XML_INIT_DONE' to indicate that document has been initialized properly */
+} XMLDoc;
+
+/*
+ Register an XML tag, giving its 'start' and 'end' string, which should include '<' and '>'.
+ The 'tag_type' is user-given and has to be less than or equal to 'TAG_USER'. It will be
+ returned as the 'tag_type' member of the XMLNode struct. Note that no test is performed
+ to check for an already-existing tag_type.
+ Return tag index in user tags table when successful, or '-1' if the 'tag_type' is invalid or
+ the new tag could not be registered (e.g. when 'start' does not start with '<' or 'end' does not end with '>').
+ */
+int XML_register_user_tag(TagType tag_type, SXML_CHAR* start, SXML_CHAR* end);
+
+/*
+ Remove a registered user tag.
+ Return the new number of registered user tags or '-1' if 'i_tag' is invalid.
+ */
+int XML_unregister_user_tag(int i_tag);
+
+/*
+ Return the number of registered tags.
+ */
+int XML_get_nb_registered_user_tags(void);
+
+/*
+ Return the index of first occurrence of 'tag_type' in registered user tags, or '-1' if not found.
+ */
+int XML_get_registered_user_tag(TagType tag_type);
+
+
+typedef enum _ParseError {
+	PARSE_ERR_NONE = 0,
+	PARSE_ERR_MEMORY = -1,
+	PARSE_ERR_UNEXPECTED_TAG_END = -2,
+	PARSE_ERR_SYNTAX = -3,
+	PARSE_ERR_EOF = -4,
+	PARSE_ERR_TEXT_OUTSIDE_NODE = -5, /* During DOM loading */
+	PARSE_ERR_UNEXPECTED_NODE_END = -6 /* During DOM loading */
+} ParseError;
+
+/*
+ Events that can happen when loading an XML document.
+ These will be passed to the 'all_event' callback of the SAX parser.
+ */
+typedef enum _XMLEvent {
+	XML_EVENT_START_DOC,
+	XML_EVENT_START_NODE,
+	XML_EVENT_END_NODE,
+	XML_EVENT_TEXT,
+	XML_EVENT_ERROR,
+	XML_EVENT_END_DOC
+} XMLEvent;
+
+/*
+ Structure given as an argument for SAX callbacks to retrieve information about
+ parsing status
+ */
+typedef struct _SAX_Data {
+	const SXML_CHAR* name;
+	int line_num;
+	void* user;
+} SAX_Data;
+
+/*
+ User callbacks used for SAX parsing. Return values of these callbacks should be 0 to stop parsing.
+ Members can be set to NULL to disable handling of some events.
+ All parameters are pointers to structures that will no longer be available after callback returns.
+ It is recommended that the callback uses the information and stores it in its own data structure.
+ WARNING! SAX PARSING DOES NOT CHECK FOR XML INTEGRITY! e.g. a tag end without a matching tag start
+ will not be detected by the parser and should be detected by the callbacks instead.
+ */
+typedef struct _SAX_Callbacks {
+	/*
+	 Callback called when parsing starts, before parsing the first node.
+	 */
+	int (*start_doc)(SAX_Data* sd);
+
+	/*
+	 Callback called when a new node starts (e.g. '<tag>' or '<tag/>').
+	 If any, attributes can be read from 'node->attributes'.
+	 N.B. '<tag/>' will trigger an immediate call to the 'end_node' callback
+	 after the 'start_node' callback.
+	 */
+	int (*start_node)(const XMLNode* node, SAX_Data* sd);
+
+	/*
+	 Callback called when a node ends (e.g. '</tag>' or '<tag/>').
+	 */
+	int (*end_node)(const XMLNode* node, SAX_Data* sd);
+
+	/*
+	 Callback called when text has been found in the last node.
+	 */
+	int (*new_text)(SXML_CHAR* text, SAX_Data* sd);
+
+	/*
+	 Callback called when parsing is finished.
+	 No other callbacks will be called after it.
+	 */
+	int (*end_doc)(SAX_Data* sd);
+
+	/*
+	 Callback called when an error occurs during parsing.
+	 'error_num' is the error number and 'line_number' is the line number in the stream
+	 being read (file or buffer).
+	 */
+	int (*on_error)(ParseError error_num, int line_number, SAX_Data* sd);
+
+	/*
+	 Callback called when text has been found in the last node.
+	 'event' is the type of event for which the callback was called:
+	 	 XML_EVENT_START_DOC:
+	 	 	 'node' is NULL.
+	 	 	 'text' is the file name if a file is being parsed, NULL if a buffer is being parsed.
+	 	 	 'n' is 0.
+	 	 XML_EVENT_START_NODE:
+	 	 	 'node' is the node starting, with tag and all attributes initialized.
+	 	 	 'text' is NULL.
+	 	 	 'n' is the number of lines parsed.
+	 	 XML_EVENT_END_NODE:
+	 	 	 'node' is the node ending, with tag, attributes and text initialized.
+	 	 	 'text' is NULL.
+	 	 	 'n' is the number of lines parsed.
+	 	 XML_EVENT_TEXT:
+	 	 	 'node' is NULL.
+	 	 	 'text' is the text to be added to last node started and not finished.
+	 	 	 'n' is the number of lines parsed.
+	 	 XML_EVENT_ERROR:
+	 	 	 Everything is NULL.
+	 	 	 'n' is one of the 'PARSE_ERR_*'.
+	 	 XML_EVENT_END_DOC:
+	 	 	 'node' is NULL.
+	 	 	 'text' is the file name if a file is being parsed, NULL if a buffer is being parsed.
+	 	 	 'n' is the number of lines parsed.
+	 */
+	int (*all_event)(XMLEvent event, const XMLNode* node, SXML_CHAR* text, const int n, SAX_Data* sd);
+} SAX_Callbacks;
+
+/*
+ Helper function to initialize all 'sax' members to NULL.
+ Return 'false' is 'sax' is NULL.
+ */
+int SAX_Callbacks_init(SAX_Callbacks* sax);
+
+/*
+ Set of SAX callbacks used by 'XMLDoc_parse_file_DOM'.
+ These are made available to be able to load an XML document using DOM implementation
+ with user-defined code at some point (e.g. counting nodes, running search, ...).
+ In this case, the 'XMLDoc_parse_file_SAX' has to be called instead of the 'XMLDoc_parse_file_DOM',
+ providing either these callbacks directly, or a functions calling these callbacks.
+ To do that, you should initialize the 'doc' member of the 'DOM_through_SAX' struct and call the
+ 'XMLDoc_parse_file_SAX' giving this struct as a the 'user' data pointer.
+ */
+
+typedef struct _DOM_through_SAX {
+	XMLDoc* doc;		/* Document to fill up */
+	XMLNode* current;	/* For internal use (current father node) */
+	ParseError error;	/* For internal use (parse status) */
+	int line_error;		/* For internal use (line number when error occurred) */
+	int text_as_nodes;	/* For internal use (store text inside nodes as sequential TAG_TEXT nodes) */
+} DOM_through_SAX;
+
+int DOMXMLDoc_doc_start(SAX_Data* dom);
+int DOMXMLDoc_node_start(const XMLNode* node, SAX_Data* dom);
+int DOMXMLDoc_node_text(SXML_CHAR* text, SAX_Data* dom);
+int DOMXMLDoc_node_end(const XMLNode* node, SAX_Data* dom);
+int DOMXMLDoc_parse_error(ParseError error_num, int line_number, SAX_Data* sd);
+int DOMXMLDoc_doc_end(SAX_Data* dom);
+
+/*
+ Initialize 'sax' with the "official" DOM callbacks.
+ */
+int SAX_Callbacks_init_DOM(SAX_Callbacks* sax);
+
+/* --- XMLNode methods --- */
+
+/*
+ Fills 'xmlattr' with 'xmlattr->name' to 'attrName' and 'xmlattr->value' to 'attr Value'.
+ 'str' is supposed to be like 'attrName[ ]=[ ]["]attr Value["]'.
+ Return 0 if not enough memory or bad parameters (NULL 'str' or 'xmlattr').
+        2 if last quote is missing in the attribute value.
+        1 if 'xmlattr' was filled correctly.
+ */
+int XML_parse_attribute_to(const SXML_CHAR* str, int to, XMLAttribute* xmlattr);
+
+#define XML_parse_attribute(str, xmlattr) XML_parse_attribute_to(str, -1, xmlattr)
+
+/*
+ Reads a string that is supposed to be an xml tag like '<tag (attribName="attribValue")* [/]>' or '</tag>'.
+ Fills the 'xmlnode' structure with the tag name and its attributes.
+ Returns 0 if an error occurred (malformed 'str' or memory). 'TAG_*' when string is recognized.
+ */
+TagType XML_parse_1string(const SXML_CHAR* str, XMLNode* xmlnode);
+
+/*
+ Allocate and initialize XML nodes.
+ 'n' is the number of contiguous elements to allocate (to create and array).
+ Return 'NULL' if not enough memory, or the pointer to the elements otherwise.
+ */
+XMLNode* XMLNode_allocN(int n);
+
+/*
+ Shortcut to allocate one node only.
+ */
+#define XMLNode_alloc() XMLNode_allocN(1)
+
+/*
+ Initialize an already-allocated XMLNode.
+ */
+int XMLNode_init(XMLNode* node);
+
+/*
+ Free a node and all its children.
+ */
+int XMLNode_free(XMLNode* node);
+
+/*
+ Free XMLNode 'dst' and copy 'src' to 'dst', along with its children if specified.
+ If 'src' is NULL, 'dst' is freed and initialized.
+ */
+int XMLNode_copy(XMLNode* dst, const XMLNode* src, int copy_children);
+
+/*
+ Allocate a node and copy 'node' into it.
+ If 'copy_children' is 'true', all children of 'node' will be copied to the new node.
+ Return 'NULL' if not enough memory, or a pointer to the new node otherwise.
+ */
+XMLNode* XMLNode_dup(const XMLNode* node, int copy_children);
+
+/*
+ Set the active/inactive state of 'node'.
+ Set 'active' to 'true' to activate 'node' and all its children, and enable its use
+ in other functions (e.g. 'XMLDoc_print', 'XMLNode_search_child').
+ */
+int XMLNode_set_active(XMLNode* node, int active);
+
+/*
+ Set 'node' tag.
+ Return 'false' for memory error, 'true' otherwise.
+ */
+int XMLNode_set_tag(XMLNode* node, const SXML_CHAR* tag);
+
+/*
+ Set the node type among one of the valid ones (TAG_FATHER, TAG_SELF, TAG_INSTR,
+ TAG_COMMENT, TAG_CDATA, TAG_DOCTYPE) or any user-registered tag.
+ Return 'false' when the node or the 'tag_type' is invalid.
+ */
+int XMLNode_set_type(XMLNode* node, const TagType tag_type);
+
+/*
+ Add an attribute to 'node' or update an existing one.
+ The attribute has a 'name' and a 'value'.
+ Return the new number of attributes, or -1 for memory problem.
+ */
+int XMLNode_set_attribute(XMLNode* node, const SXML_CHAR* attr_name, const SXML_CHAR* attr_value);
+
+/*
+ Retrieve an attribute value, based on its name, allocating 'attr_value'.
+ If the attribute name does not exist, set 'attr_value' to the given default value.
+ Return 'false' when the node is invalid, 'attr_name' is NULL or empty, or 'attr_value' is NULL.
+ */
+int XMLNode_get_attribute_with_default(XMLNode* node, const SXML_CHAR* attr_name, const SXML_CHAR** attr_value, const SXML_CHAR* default_attr_value);
+
+/*
+ Helper macro that retrieve an attribute value, or an empty string if the attribute does
+ not exist.
+ */
+#define XMLNode_get_attribute(node, attr_name, attr_value) XMLNode_get_attribute_with_default(node, attr_name, attr_value, C2SX(""))
+
+/*
+ Return the number of active attributes of 'node', or '-1' if 'node' is invalid.
+*/
+int XMLNode_get_attribute_count(const XMLNode* node);
+
+/*
+ Search for the active attribute 'attr_name' in 'node', starting from index 'isearch'
+ and returns its index, or -1 if not found or error.
+ */
+int XMLNode_search_attribute(const XMLNode* node, const SXML_CHAR* attr_name, int isearch);
+
+/*
+ Remove attribute index 'i_attr'.
+ Return the new number of attributes or -1 on invalid arguments.
+ */
+int XMLNode_remove_attribute(XMLNode* node, int i_attr);
+
+/*
+ Remove all attributes from 'node'.
+ */
+int XMLNode_remove_all_attributes(XMLNode* node);
+
+/*
+ Set node text.
+ Return 'true' when successful, 'false' on error.
+ */
+int XMLNode_set_text(XMLNode* node, const SXML_CHAR* text);
+
+/*
+ Helper macro to remove text from 'node'.
+ */
+#define XMLNode_remove_text(node) XMLNode_set_text(node, NULL);
+
+/*
+ Add a child to a node.
+ Return 'false' for memory problem, 'true' otherwise.
+ */
+int XMLNode_add_child(XMLNode* node, XMLNode* child);
+
+/*
+ Return the number of active children nodes of 'node', or '-1' if 'node' is invalid.
+ */
+int XMLNode_get_children_count(const XMLNode* node);
+
+/*
+ Return a reference to the 'i_child'th active node.
+ */
+XMLNode* XMLNode_get_child(const XMLNode* node, int i_child);
+
+/*
+ Remove the 'i_child'th active child of 'node'.
+ If 'free_child' is 'true', free the child node itself. This parameter is usually 'true'
+ but should be 'false' when child nodes are pointers to local or global variables instead of
+ user-allocated memory.
+ Return the new number of children or -1 on invalid arguments.
+ */
+int XMLNode_remove_child(XMLNode* node, int i_child, int free_child);
+
+/*
+ Remove all children from 'node'.
+ */
+int XMLNode_remove_children(XMLNode* node);
+
+/*
+ Return 'true' if 'node1' is the same as 'node2' (i.e. same tag, same active attributes).
+ */
+int XMLNode_equal(const XMLNode* node1, const XMLNode* node2);
+
+/*
+ Return the next sibling of node 'node', or NULL if 'node' is invalid or the last child
+ or if its father could not be determined (i.e. 'node' is a root node).
+ */
+XMLNode* XMLNode_next_sibling(const XMLNode* node);
+
+/*
+ Return the next node in XML order i.e. first child or next sibling, or NULL
+ if 'node' is invalid or the end of its root node is reached.
+ */
+XMLNode* XMLNode_next(const XMLNode* node);
+
+
+/* --- XMLDoc methods --- */
+
+
+/*
+ Initializes an already-allocated XML document.
+ */
+int XMLDoc_init(XMLDoc* doc);
+
+/*
+ Free an XML document.
+ Return 'false' if 'doc' was not initialized.
+ */
+int XMLDoc_free(XMLDoc* doc);
+
+/*
+ Set the new 'doc' root node among all existing nodes in 'doc'.
+ Return 'false' if bad arguments, 'true' otherwise.
+ */
+int XMLDoc_set_root(XMLDoc* doc, int i_root);
+
+/*
+ Add a node to the document, specifying the type.
+ If its type is TAG_FATHER, it also sets the document root node if previously undefined.
+ Return the node index, or -1 if bad arguments or memory error.
+ */
+int XMLDoc_add_node(XMLDoc* doc, XMLNode* node);
+
+/*
+ Remove a node from 'doc' root nodes, base on its index.
+ If 'free_node' is 'true', free the node itself. This parameter is usually 'true'
+ but should be 'false' when the node is a pointer to local or global variable instead of
+ user-allocated memory.
+ Return 'true' if node was removed or 'false' if 'doc' or 'i_node' is invalid.
+ */
+int XMLDoc_remove_node(XMLDoc* doc, int i_node, int free_node);
+
+/*
+ Shortcut macro to retrieve root node from a document.
+ Equivalent to
+ doc->nodes[doc->i_root]
+ */
+#define XMLDoc_root(doc) ((doc)->nodes[(doc)->i_root])
+
+/*
+ Shortcut macro to add a node to 'doc' root node.
+ Equivalent to
+ XMLDoc_add_child_root(XMLDoc* doc, XMLNode* child);
+ */
+#define XMLDoc_add_child_root(doc, child) XMLNode_add_child((doc)->nodes[(doc)->i_root], (child))
+
+/*
+ Default quote to use to print attribute value.
+ User can redefine it with its own character by adding a #define XML_DEFAULT_QUOTE before including
+ this file.
+ */
+#ifndef XML_DEFAULT_QUOTE
+#define XML_DEFAULT_QUOTE C2SX('"')
+#endif
+
+/*
+ Print the node and its children to a file (that can be stdout).
+ - 'tag_sep' is the string to use to separate nodes from each other (usually "\n").
+ - 'child_sep' is the additional string to put for each child level (usually "\t").
+ - 'keep_text_spaces' indicates that text should not be printed if it is composed of
+   spaces, tabs or new lines only (e.g. when XML document spans on several lines due to
+   pretty-printing).
+ - 'sz_line' is the maximum number of characters that can be put on a single line. The
+   node remainder will be output to extra lines.
+ - 'nb_char_tab' is how many characters should be counted for a tab when counting characters
+   in the line. It usually is 8 or 4, but at least 1.
+ - 'depth' is an internal parameter that is used to determine recursively how deep we are in
+   the tree. It should be initialized to 0 at first call.
+ Return 'false' on invalid arguments (NULL 'node' or 'f'), 'true' otherwise.
+ */
+int XMLNode_print_attr_sep(const XMLNode* node, FILE* f, const SXML_CHAR* tag_sep, const SXML_CHAR* child_sep, const SXML_CHAR* attr_sep, int keep_text_spaces, int sz_line, int nb_char_tab);
+
+/* For backward compatibility */
+#define XMLNode_print(node, f, tag_sep, child_sep, keep_text_spaces, sz_line, nb_char_tab) XMLNode_print_attr_sep(node, f, tag_sep, child_sep, C2SX(" "), keep_text_spaces, sz_line, nb_char_tab)
+
+/*
+ Print the node "header": <tagname attribname="attibval" ...[/]>, spanning it on several lines if needed.
+ Return 'false' on invalid arguments (NULL 'node' or 'f'), 'true' otherwise.
+ */
+int XMLNode_print_header(const XMLNode* node, FILE* f, int sz_line, int nb_char_tab);
+
+/*
+ Prints the XML document using 'XMLNode_print' on all document root nodes.
+ */
+int XMLDoc_print_attr_sep(const XMLDoc* doc, FILE* f, const SXML_CHAR* tag_sep, const SXML_CHAR* child_sep, const SXML_CHAR* attr_sep, int keep_text_spaces, int sz_line, int nb_char_tab);
+
+/* For backward compatibility */
+#define XMLDoc_print(doc, f, tag_sep, child_sep, keep_text_spaces, sz_line, nb_char_tab) XMLDoc_print_attr_sep(doc, f, tag_sep, child_sep, C2SX(" "), keep_text_spaces, sz_line, nb_char_tab)
+
+/*
+ Create a new XML document from a given 'filename' and load it to 'doc'.
+ 'text_as_nodes' should be non-zero to put text into separate TAG_TEXT nodes.
+ Return 'false' in case of error (memory or unavailable filename, malformed document), 'true' otherwise.
+ */
+int XMLDoc_parse_file_DOM_text_as_nodes(const SXML_CHAR* filename, XMLDoc* doc, int text_as_nodes);
+
+/* For backward compatibility */
+#define XMLDoc_parse_file_DOM(filename, doc) XMLDoc_parse_file_DOM_text_as_nodes(filename, doc, 0)
+
+/*
+ Create a new XML document from a memory buffer 'buffer' that can be given a name 'name', and load
+ it into 'doc'.
+ 'text_as_nodes' should be non-zero to put text into separate TAG_TEXT nodes.
+ Return 'false' in case of error (memory or unavailable filename, malformed document), 'true' otherwise.
+ */
+int XMLDoc_parse_buffer_DOM_text_as_nodes(const SXML_CHAR* buffer, const SXML_CHAR* name, XMLDoc* doc, int text_as_nodes);
+
+/* For backward compatibility */
+#define XMLDoc_parse_buffer_DOM(buffer, name, doc) XMLDoc_parse_buffer_DOM_text_as_nodes(buffer, name, doc, 0)
+
+/*
+ Parse an XML document from a given 'filename', calling SAX callbacks given in the 'sax' structure.
+ 'user' is a user-given pointer that will be given back to all callbacks.
+ Return 'false' in case of error (memory or unavailable filename, malformed document), 'true' otherwise.
+ */
+int XMLDoc_parse_file_SAX(const SXML_CHAR* filename, const SAX_Callbacks* sax, void* user);
+
+/*
+ Parse an XML document from a memory buffer 'buffer' that can be given a name 'name',
+ calling SAX callbacks given in the 'sax' structure.
+ 'user' is a user-given pointer that will be given back to all callbacks.
+ Return 'false' in case of error (memory or unavailable filename, malformed document), 'true' otherwise.
+ */
+int XMLDoc_parse_buffer_SAX(const SXML_CHAR* buffer, const SXML_CHAR* name, const SAX_Callbacks* sax, void* user);
+
+/*
+ Parse an XML file using the DOM implementation.
+ */
+#define XMLDoc_parse_file XMLDOC_parse_file_DOM
+
+
+
+/* --- Utility functions --- */
+
+/*
+ Functions to get next byte from buffer data source and know if the end has been reached.
+ Return as 'fgetc' and 'feof' would for 'FILE*'.
+ */
+int _bgetc(DataSourceBuffer* ds);
+int _beob(DataSourceBuffer* ds);
+/*
+ Reads a line from data source 'in', eventually (re-)allocating a given buffer 'line'.
+ Characters read will be stored in 'line' starting at 'i0' (this allows multiple calls to
+ 'read_line_alloc' on the same 'line' buffer without overwriting it at each call).
+ 'in_type' specifies the type of data source to be read: 'in' is 'FILE*' if 'in_type'
+ 'sz_line' is the size of the buffer 'line' if previously allocated. 'line' can point
+ to NULL, in which case it will be allocated '*sz_line' bytes. After the function returns,
+ '*sz_line' is the actual buffer size. This allows multiple calls to this function using the
+ same buffer (without re-allocating/freeing).
+ If 'sz_line' is non NULL and non 0, it means that '*line' is a VALID pointer to a location
+ of '*sz_line' SXML_CHAR (not bytes! Multiply by sizeof(SXML_CHAR) to get number of bytes).
+ Searches for character 'from' until character 'to'. If 'from' is 0, starts from
+ current position. If 'to' is 0, it is replaced by '\n'.
+ If 'keep_fromto' is 0, removes characters 'from' and 'to' from the line.
+ If 'interest_count' is not NULL, will receive the count of 'interest' characters while searching
+ for 'to' (e.g. use 'interest'='\n' to count lines in file).
+ Returns the number of characters in the line or 0 if an error occurred.
+ 'read_line_alloc' uses constant 'MEM_INCR_RLA' to reallocate memory when needed. It is possible
+ to override this definition to use another value.
+ */
+int read_line_alloc(void* in, DataSourceType in_type, SXML_CHAR** line, int* sz_line, int i0, SXML_CHAR from, SXML_CHAR to, int keep_fromto, SXML_CHAR interest, int* interest_count);
+
+/*
+ Concatenates the string pointed at by 'src1' with 'src2' into '*src1' and
+ return it ('*src1').
+ Return NULL when out of memory.
+ */
+SXML_CHAR* strcat_alloc(SXML_CHAR** src1, const SXML_CHAR* src2);
+
+/*
+ Strip spaces at the beginning and end of 'str', modifying 'str'.
+ If 'repl_sq' is not '\0', squeezes spaces to an single character ('repl_sq').
+ If not '\0', 'protect' is used to protect spaces from being deleted (usually a backslash).
+ Returns the string or NULL if 'protect' is a space (which would not make sense).
+ */
+SXML_CHAR* strip_spaces(SXML_CHAR* str, SXML_CHAR repl_sq);
+
+/*
+ Remove '\' characters from 'str', modifying it.
+ Return 'str'.
+ */
+SXML_CHAR* str_unescape(SXML_CHAR* str);
+
+/*
+ Split 'str' into a left and right part around a separator 'sep'.
+ The left part is located between indexes 'l0' and 'l1' while the right part is
+ between 'r0' and 'r1' and the separator position is at 'i_sep' (whenever these are
+ not NULL).
+ If 'ignore_spaces' is 'true', computed indexes will not take into account potential
+ spaces around the separator as well as before left part and after right part.
+ if 'ignore_quotes' is 'true', " or ' will not be taken into account when parsing left
+ and right members.
+ Whenever the right member is empty (e.g. "attrib" or "attrib="), '*r0' is initialized
+ to 'str' size and '*r1' to '*r0-1' (crossed).
+ If the separator was not found (i.e. left member only), '*i_sep' is '-1'.
+ Return 'false' when 'str' is malformed, 'true' when splitting was successful.
+ */
+int split_left_right(SXML_CHAR* str, SXML_CHAR sep, int* l0, int* l1, int* i_sep, int* r0, int* r1, int ignore_spaces, int ignore_quotes);
+
+typedef enum _BOM_TYPE {
+	BOM_NONE = 0x00,
+	BOM_UTF_8 = 0xefbbbf,
+	BOM_UTF_16BE = 0xfeff,
+	BOM_UTF_16LE = 0xfffe,
+	BOM_UTF_32BE = 0x0000feff,
+	BOM_UTF_32LE = 0xfffe0000
+} BOM_TYPE;
+/*
+ Detect a potential BOM at the current file position and read it into 'bom' (if not NULL,
+ 'bom' should be at least 5 bytes). It also moves the 'f' beyond the BOM so it's possible to
+ skip it by calling 'freadBOM(f, NULL, NULL)'. If no BOM is found, it leaves 'f' file pointer
+ is reset to its original location.
+ If not null, 'sz_bom' is filled with how many bytes are stored in 'bom'.
+ Return the BOM type or BOM_NONE if none found (empty 'bom' in this case).
+ */
+BOM_TYPE freadBOM(FILE* f, unsigned char* bom, int* sz_bom);
+
+/*
+ Replace occurrences of special HTML characters escape sequences (e.g. '&amp;') found in 'html'
+ by its character equivalent (e.g. '&') into 'str'.
+ If 'html' and 'str' are the same pointer replacement is made in 'str' itself, overwriting it.
+ If 'str' is NULL, replacement is made into 'html', overwriting it.
+ Returns 'str' (or 'html' if 'str' was NULL).
+ */
+SXML_CHAR* html2str(SXML_CHAR* html, SXML_CHAR* str);
+
+/*
+ Replace occurrences of special characters (e.g. '&') found in 'str' into their XML escaped
+ equivalent (e.g. '&amp;') into 'xml'.
+ 'xml' is supposed allocated to the correct size (e.g. using 'malloc(strlen_html(str)+30)') and
+ different from 'str' (unlike 'html2str'), as string will expand. If it is NULL, 'str' will be
+ analyzed and a string will be allocated to the exact size, before being returned. In that case,
+ it is the responsibility of the caller to free() the result!
+ Return 'xml' or NULL if 'str' or 'xml' are NULL, or when 'xml' is 'str'.
+*/
+SXML_CHAR* str2html(SXML_CHAR* str, SXML_CHAR* xml);
+
+/*
+ Return the length of 'str' as if all its special character were replaced by their HTML
+ equivalent.
+ Return 0 if 'str' is NULL.
+ */
+int strlen_html(SXML_CHAR* str);
+
+/*
+ Print 'str' to 'f', transforming special characters into their HTML equivalent.
+ Returns the number of output characters.
+ */
+int fprintHTML(FILE* f, SXML_CHAR* str);
+
+/*
+ Checks whether 'str' corresponds to 'pattern'.
+ 'pattern' can use wildcads such as '*' (any potentially empty string) or
+ '?' (any character) and use '\' as an escape character.
+ Returns 'true' when 'str' matches 'pattern', 'false' otherwise.
+ */
+int regstrcmp(SXML_CHAR* str, SXML_CHAR* pattern);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/core_server/client/sxmlc/sxmlsearch.c b/core_server/client/sxmlc/sxmlsearch.c
new file mode 100755
index 0000000..c03b12a
--- /dev/null
+++ b/core_server/client/sxmlc/sxmlsearch.c
@@ -0,0 +1,639 @@
+/*
+	Copyright (c) 2010, Matthieu Labas
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without modification,
+	are permitted provided that the following conditions are met:
+
+	1. Redistributions of source code must retain the above copyright notice,
+	   this list of conditions and the following disclaimer.
+
+	2. Redistributions in binary form must reproduce the above copyright notice,
+	   this list of conditions and the following disclaimer in the documentation
+	   and/or other materials provided with the distribution.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+	ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+	WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+	IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+	INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+	WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+	OF SUCH DAMAGE.
+
+	The views and conclusions contained in the software and documentation are those of the
+	authors and should not be interpreted as representing official policies, either expressed
+	or implied, of the FreeBSD Project.
+*/
+#if defined(WIN32) || defined(WIN64)
+#pragma warning(disable : 4996)
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include "sxmlc.h"
+#include "sxmlsearch.h"
+
+#define INVALID_XMLNODE_POINTER ((XMLNode*)-1)
+
+/* The function used to compare a string to a pattern */
+static REGEXPR_COMPARE regstrcmp_search = regstrcmp;
+
+REGEXPR_COMPARE XMLSearch_set_regexpr_compare(REGEXPR_COMPARE fct)
+{
+	REGEXPR_COMPARE previous = regstrcmp_search;
+
+	regstrcmp_search = fct;
+
+	return previous;
+}
+
+int XMLSearch_init(XMLSearch* search)
+{
+	if (search == NULL)
+		return false;
+
+	if (search->init_value == XML_INIT_DONE)
+		XMLSearch_free(search, true);
+
+	search->tag = NULL;
+	search->text = NULL;
+	search->attributes = NULL;
+	search->n_attributes = 0;
+	search->next = NULL;
+	search->prev = NULL;
+	search->stop_at = INVALID_XMLNODE_POINTER; /* Because 'NULL' can be a valid value */
+	search->init_value = XML_INIT_DONE;
+	
+	return true;
+}
+
+int XMLSearch_free(XMLSearch* search, int free_next)
+{
+	int i;
+
+	if (search == NULL || search->init_value != XML_INIT_DONE)
+		return false;
+
+	if (search->tag != NULL) {
+		__free(search->tag);
+		search->tag = NULL;
+	}
+
+	if (search->attributes != NULL) {
+		for (i = 0; i < search->n_attributes; i++) {
+			if (search->attributes[i].name != NULL)
+				__free(search->attributes[i].name);
+			if (search->attributes[i].value != NULL)
+				__free(search->attributes[i].value);
+		}
+		__free(search->attributes);
+		search->n_attributes = 0;
+		search->attributes = NULL;
+	}
+
+	if (free_next && search->next != NULL) {
+		(void)XMLSearch_free(search->next, true);
+		__free(search->next);
+		search->next = NULL;
+	}
+	search->init_value = 0; /* Something not XML_INIT_DONE, otherwise we'll go into 'XMLSearch_free' again */
+	(void)XMLSearch_init(search);
+
+	return true;
+}
+
+int XMLSearch_search_set_tag(XMLSearch* search, const SXML_CHAR* tag)
+{
+	if (search == NULL)
+		return false;
+
+	if (tag == NULL) {
+		if (search->tag != NULL) {
+			__free(search->tag);
+			search->tag = NULL;
+		}
+		return true;
+	}
+
+	search->tag = sx_strdup(tag);
+	return (search->tag != NULL);
+}
+
+int XMLSearch_search_set_text(XMLSearch* search, const SXML_CHAR* text)
+{
+	if (search == NULL)
+		return false;
+
+	if (text == NULL) {
+		if (search->text != NULL) {
+			__free(search->text);
+			search->text = NULL;
+		}
+		return true;
+	}
+
+	search->text = sx_strdup(text);
+	return (search->text != NULL);
+}
+
+int XMLSearch_search_add_attribute(XMLSearch* search, const SXML_CHAR* attr_name, const SXML_CHAR* attr_value, int value_equal)
+{
+	int i;
+	XMLAttribute* pt;
+	SXML_CHAR* name;
+	SXML_CHAR* value;
+
+	if (search == NULL)
+		return -1;
+
+	if (attr_name == NULL || attr_name[0] == NULC)
+		return -1;
+
+	name = sx_strdup(attr_name);
+	value = (attr_value == NULL ? NULL : sx_strdup(attr_value));
+	if (name == NULL || (attr_value && value == NULL)) {
+		if (value != NULL)
+			__free(value);
+		if (name != NULL)
+			__free(name);
+	}
+
+	i = search->n_attributes;
+	pt = (XMLAttribute*)__realloc(search->attributes, (i + 1) * sizeof(XMLAttribute));
+	if (pt == NULL) {
+		if (value)
+			__free(value);
+		__free(name);
+		return -1;
+	}
+
+	pt[i].name = name;
+	pt[i].value = value;
+	pt[i].active = value_equal;
+
+	search->n_attributes = i+1;
+	search->attributes = pt;
+
+	return i;
+}
+
+int XMLSearch_search_get_attribute_index(const XMLSearch* search, const SXML_CHAR* attr_name)
+{
+	int i;
+
+	if (search == NULL || attr_name == NULL || attr_name[0] == NULC)
+		return -1;
+
+	for (i = 0; i < search->n_attributes; i++) {
+		if (!sx_strcmp(search->attributes[i].name, attr_name))
+			return i;
+	}
+
+	return -1;
+}
+
+int XMLSearch_search_remove_attribute(XMLSearch* search, int i_attr)
+{
+	XMLAttribute* pt;
+
+	if (search == NULL || i_attr < 0 || i_attr >= search->n_attributes)
+		return -1;
+
+	/* Free attribute fields first */
+	if (search->n_attributes == 1)
+		pt = NULL;
+	else {
+		pt = (XMLAttribute*)__malloc((search->n_attributes - 1) * sizeof(XMLAttribute));
+		if (pt == NULL)
+			return -1;
+	}
+	if (search->attributes[i_attr].name != NULL)
+		__free(search->attributes[i_attr].name);
+	if (search->attributes[i_attr].value != NULL)
+		__free(search->attributes[i_attr].value);
+
+	if (pt != NULL) {
+		memcpy(pt, search->attributes, i_attr * sizeof(XMLAttribute));
+		memcpy(&pt[i_attr], &search->attributes[i_attr + 1], (search->n_attributes - i_attr - 1) * sizeof(XMLAttribute));
+	}
+	if (search->attributes)
+		__free(search->attributes);
+	search->attributes = pt;
+	search->n_attributes--;
+
+	return search->n_attributes;
+}
+
+int XMLSearch_search_set_children_search(XMLSearch* search, XMLSearch* children_search)
+{
+	if (search == NULL)
+		return false;
+
+	if (search->next != NULL)
+		XMLSearch_free(search->next, true);
+
+	search->next = children_search;
+	children_search->prev = search;
+
+	return true;
+}
+
+SXML_CHAR* XMLSearch_get_XPath_string(const XMLSearch* search, SXML_CHAR** xpath, SXML_CHAR quote)
+{
+	const XMLSearch* s;
+	SXML_CHAR squote[] = C2SX("'");
+	int i, fill;
+
+	if (xpath == NULL)
+		return NULL;
+
+	/* NULL 'search' is an empty string */
+	if (search == NULL) {
+		*xpath = sx_strdup(C2SX(""));
+		if (*xpath == NULL)
+			return NULL;
+
+		return *xpath;
+	}
+
+	squote[0] = (quote == NULC ? XML_DEFAULT_QUOTE : quote);
+
+	for (s = search; s != NULL; s = s->next) {
+		if (s != search && strcat_alloc(xpath, C2SX("/")) == NULL) goto err; /* No "/" prefix for the first criteria */
+		if (strcat_alloc(xpath, s->tag == NULL || s->tag[0] == NULC ? C2SX("*"): s->tag) == NULL) goto err;
+
+		if (s->n_attributes > 0 || (s->text != NULL && s->text[0] != NULC))
+			if (strcat_alloc(xpath, C2SX("[")) == NULL) goto err;
+
+		fill = false; /* '[' has not been filled with text yet, no ", " separator should be added */
+		if (s->text != NULL && s->text[0] != NULC) {
+			if (strcat_alloc(xpath, C2SX(".=")) == NULL) goto err;
+			if (strcat_alloc(xpath, squote) == NULL) goto err;
+			if (strcat_alloc(xpath, s->text) == NULL) goto err;
+			if (strcat_alloc(xpath, squote) == NULL) goto err;
+			fill = true;
+		}
+
+		for (i = 0; i < s->n_attributes; i++) {
+			if (fill) {
+				if (strcat_alloc(xpath, C2SX(", ")) == NULL) goto err;
+			} else
+				fill = true; /* filling is being performed */
+			if (strcat_alloc(xpath, C2SX("@")) == NULL) goto err;
+			if (strcat_alloc(xpath, s->attributes[i].name) == NULL) goto err;
+			if (s->attributes[i].value == NULL) continue;
+
+			if (strcat_alloc(xpath, s->attributes[i].active ? C2SX("=") : C2SX("!=")) == NULL) goto err;
+			if (strcat_alloc(xpath, squote) == NULL) goto err;
+			if (strcat_alloc(xpath, s->attributes[i].value) == NULL) goto err;
+			if (strcat_alloc(xpath, squote) == NULL) goto err;
+		}
+		if ((s->text != NULL && s->text[0] != NULC) || s->n_attributes > 0) {
+			if (strcat_alloc(xpath, C2SX("]")) == NULL) goto err;
+		}
+	}
+
+	return *xpath;
+
+err:
+	__free(*xpath);
+	*xpath = NULL;
+
+	return NULL;
+}
+
+/*
+ Extract search information from 'xpath', where 'xpath' represents a single node
+ (i.e. no '/' inside, except escaped ones), stripped from lead and tail '/'.
+ tag[.=text, @attrib="value"] with potential spaces around '=' and ','.
+ Return 'false' if parsing failed, 'true' for success.
+ This is an internal function so we assume that arguments are valid (non-NULL).
+ */
+static int _init_search_from_1XPath(SXML_CHAR* xpath, XMLSearch* search)
+{
+	SXML_CHAR *p, *q;
+	SXML_CHAR c, c1, cc;
+	int l0, l1, is, r0, r1;
+	int ret;
+
+	XMLSearch_init(search);
+
+	/* Look for tag name */
+	for (p = xpath; *p != NULC && *p != C2SX('['); p++) ;
+	c = *p; /* Either '[' or '\0' */
+	*p = NULC;
+	ret = XMLSearch_search_set_tag(search, xpath);
+	*p = c;
+	if (!ret)
+		return false;
+
+	if (*p == NULC)
+		return true;
+
+	/* Here, '*p' is '[', we have to parse either text or attribute names/values until ']' */
+	for (p++; *p && *p != C2SX(']'); p++) {
+		for (q = p; *q && *q != C2SX(',') && *q != C2SX(']'); q++) ; /* Look for potential ',' separator to null it */
+		cc = *q;
+		if (*q == C2SX(',') || *q == C2SX(']'))
+			*q = NULC;
+		ret = true;
+		switch (*p) {
+			case C2SX('.'): /* '.[ ]=[ ]["']...["']' to search for text */
+				if (!split_left_right(p, C2SX('='), &l0, &l1, &is, &r0, &r1, true, true))
+					return false;
+				c = p[r1+1];
+				p[r1+1] = NULC;
+				ret = XMLSearch_search_set_text(search, &p[r0]);
+				p[r1+1] = c;
+				p += r1+1;
+				break;
+
+			/* Attribute name, possibly '@attrib[[ ]=[ ]"value"]' */
+			case C2SX('@'):
+				if (!split_left_right(++p, '=', &l0, &l1, &is, &r0, &r1, true, true))
+					return false;
+				c = p[l1+1];
+				c1 = p[r1+1];
+				p[l1+1] = NULC;
+				p[r1+1] = NULC;
+				ret = (XMLSearch_search_add_attribute(search, &p[l0], (is < 0 ? NULL : &p[r0]), true) < 0 ? false : true); /* 'is' < 0 when there is no '=' (i.e. check for attribute presence only */
+				p[l1+1] = c;
+				p[r1+1] = c1;
+				p += r1-1; /* Jump to next value */
+				break;
+
+			default: /* Not implemented */
+				break;
+		}
+		*q = cc; /* Restore ',' separator if any */
+		if (!ret)
+			return false;
+	}
+
+	return true;
+}
+
+int XMLSearch_init_from_XPath(const SXML_CHAR* xpath, XMLSearch* search)
+{
+	XMLSearch *search1, *search2;
+	SXML_CHAR *p, *tag, *tag0;
+	SXML_CHAR c;
+
+	if (!XMLSearch_init(search))
+		return false;
+
+	/* NULL or empty xpath is an empty (initialized only) search */
+	if (xpath == NULL || *xpath == NULC)
+		return true;
+
+	search1 = NULL;		/* Search struct to add the xpath portion to */
+	search2 = search;	/* Search struct to be filled from xpath portion */
+
+	tag = tag0 = sx_strdup(xpath); /* Create a copy of 'xpath' to be able to patch it (or segfault if 'xpath' is const, cnacu6o Sergey@sourceforge!) */
+	while (*tag != NULC) {
+		if (search2 != search) { /* Allocate a new search when the original one (i.e. 'search') has already been filled */
+			search2 = (XMLSearch*)__calloc(1, sizeof(XMLSearch));
+			if (search2 == NULL) {
+				__free(tag0);
+				(void)XMLSearch_free(search, true);
+				return false;
+			}
+		}
+		/* Skip all first '/' */
+		for (; *tag != NULC && *tag == C2SX('/'); tag++) ;
+		if (*tag == NULC) {
+			__free(tag0);
+			return false;
+		}
+
+		/* Look for the end of tag name: after '/' (to get another tag) or end of string */
+		for (p = &tag[1]; *p != NULC && *p != C2SX('/'); p++) {
+			if (*p == C2SX('\\') && *++p == NULC)
+				break; /* Escape character, '\' could be the last character... */
+		}
+		c = *p; /* Backup character before nulling it */
+		*p = NULC;
+		if (!_init_search_from_1XPath(tag, search2)) {
+			__free(tag0);
+			(void)XMLSearch_free(search, true);
+			return false;
+		}
+		*p = c;
+
+		/* 'search2' is the newly parsed tag, 'search1' is the previous tag (or NULL if 'search2' is the first tag to parse (i.e. 'search2' == 'search') */
+
+		if (search1 != NULL) search1->next = search2;
+		if (search2 != search) search2->prev = search1;
+		search1 = search2;
+		search2 = NULL; /* Will force allocation during next loop */
+		tag = p;
+	}
+
+	__free(tag0);
+	return true;
+}
+
+static int _attribute_matches(XMLAttribute* to_test, XMLAttribute* pattern)
+{
+	if (to_test == NULL && pattern == NULL)
+		return true;
+
+	if (to_test == NULL || pattern == NULL)
+		return false;
+	
+	/* No test on name => match */
+	if (pattern->name == NULL || pattern->name[0] == NULC)
+		return true;
+
+	/* Test on name fails => no match */
+	if (!regstrcmp_search(to_test->name, pattern->name))
+		return false;
+
+	/* No test on value => match */
+	if (pattern->value == NULL)
+		return true;
+
+	/* Test on value according to pattern "equal" attribute */
+	return regstrcmp_search(to_test->value, pattern->value) == pattern->active ? true : false;
+}
+
+int XMLSearch_node_matches(const XMLNode* node, const XMLSearch* search)
+{
+	int i, j;
+
+	if (node == NULL)
+		return false;
+
+	if (search == NULL)
+		return true;
+
+	/* No comments, prolog, or such type of nodes are tested */
+	if (node->tag_type != TAG_FATHER && node->tag_type != TAG_SELF)
+		return false;
+
+	/* Check tag */
+	if (search->tag != NULL && !regstrcmp_search(node->tag, search->tag))
+		return false;
+
+	/* Check text */
+	if (search->text != NULL && !regstrcmp_search(node->text, search->text))
+		return false;
+
+	/* Check attributes */
+	if (search->attributes != NULL) {
+		for (i = 0; i < search->n_attributes; i++) {
+			for (j = 0; j < node->n_attributes; j++) {
+				if (!node->attributes[j].active)
+					continue;
+				if (_attribute_matches(&node->attributes[j], &search->attributes[i]))
+					break;
+			}
+			if (j >= node->n_attributes) /* All attributes where scanned without a successful match */
+				return false;
+		}
+	}
+
+	/* 'node' matches 'search'. If there is a father search, its father must match it */
+	if (search->prev != NULL)
+		return XMLSearch_node_matches(node->father, search->prev);
+
+	/* TODO: Should a node match if search has no more 'prev' search and node father is still below the initial search ?
+	 Depends if XPath started with "//" (=> yes) or "/" (=> no).
+	 if (search->prev == NULL && node->father != search->from) return false; ? */
+		
+	return true;
+}
+
+XMLNode* XMLSearch_next(const XMLNode* from, XMLSearch* search)
+{
+	XMLNode* node;
+
+	if (search == NULL || from == NULL)
+		return NULL;
+
+	/* Go down the last child search as fathers will be tested recursively by the 'XMLSearch_node_matches' function */
+	for (; search->next != NULL; search = search->next) ;
+
+	/* Initialize the 'stop_at' node on first search, to remember where to stop as there will be multiple calls */
+	/* 'stop_at' can be NULL when 'from' is a root node, that is why it should be initialized with something else than NULL */
+	if (search->stop_at == INVALID_XMLNODE_POINTER)
+		search->stop_at = XMLNode_next_sibling(from);
+
+	for (node = XMLNode_next(from); node != search->stop_at; node = XMLNode_next(node)) { /* && node != NULL */
+		if (!XMLSearch_node_matches(node, search))
+			continue;
+
+		/* 'node' is a matching node */
+
+		/* No search to perform on 'node' children => 'node' is returned */
+		if (search->next == NULL)
+			return node;
+
+		/* Run the search on 'node' children */
+		return XMLSearch_next(node, search->next);
+	}
+
+	return NULL;
+}
+
+static SXML_CHAR* _get_XPath(const XMLNode* node, SXML_CHAR** xpath)
+{
+	int i, n, brackets, sz_xpath;
+	SXML_CHAR* p;
+
+	brackets = 0;
+	sz_xpath = sx_strlen(node->tag);
+	if (node->text != NULL) {
+		sz_xpath += strlen_html(node->text) + 4; /* 4 = '.=""' */
+		brackets = 2; /* Text has to be displayed => add '[]' */
+	}
+	for (i = 0; i < node->n_attributes; i++) {
+		if (!node->attributes[i].active)
+			continue;
+		brackets = 2; /* At least one attribute has to be displayed => add '[]' */
+		sz_xpath += strlen_html(node->attributes[i].name) + strlen_html(node->attributes[i].value) + 6; /* 6 = ', @=""' */
+	}
+	sz_xpath += brackets + 1;
+	*xpath = (SXML_CHAR*)__malloc(sz_xpath*sizeof(SXML_CHAR));
+
+	if (*xpath == NULL)
+		return NULL;
+
+	sx_strcpy(*xpath, node->tag);
+	if (node->text != NULL) {
+		sx_strcat(*xpath, C2SX("[.=\""));
+		(void)str2html(node->text, &(*xpath[sx_strlen(*xpath)]));
+		sx_strcat(*xpath, C2SX("\""));
+		n = 1; /* Indicates '[' has been put */
+	} else
+		n = 0;
+
+	for (i = 0; i < node->n_attributes; i++) {
+		if (!node->attributes[i].active)
+			continue;
+
+		if (n == 0) {
+			sx_strcat(*xpath, C2SX("["));
+			n = 1;
+		} else
+			sx_strcat(*xpath, C2SX(", "));
+		p = &(*xpath)[sx_strlen(*xpath)];
+
+		/* Standard and Unicode versions of 'sprintf' do not have the same signature! :( */
+		sx_sprintf(p,
+#ifdef SXMLC_UNICODE
+			sz_xpath,
+#endif
+			C2SX("@%s=%c"), node->attributes[i].name, XML_DEFAULT_QUOTE);
+
+		(void)str2html(node->attributes[i].value, p);
+		sx_strcat(*xpath, C2SX("\""));
+	}
+	if (n > 0)
+		sx_strcat(*xpath, C2SX("]"));
+
+	return *xpath;
+}
+
+SXML_CHAR* XMLNode_get_XPath(XMLNode* node, SXML_CHAR** xpath, int incl_parents)
+{
+	SXML_CHAR* xp = NULL;
+	SXML_CHAR* xparent;
+	XMLNode* parent;
+
+	if (node == NULL || node->init_value != XML_INIT_DONE || xpath == NULL)
+		return NULL;
+
+	if (!incl_parents) {
+		if (_get_XPath(node, &xp) == NULL) {
+			*xpath = NULL;
+			return NULL;
+		}
+		return *xpath = xp;
+	}
+
+	/* Go up to root node */
+	parent = node;
+	do {
+		xparent = NULL;
+		if (_get_XPath(parent, &xparent) == NULL) goto xp_err;
+		if (xp != NULL) {
+			if (strcat_alloc(&xparent, C2SX("/")) == NULL) goto xp_err;
+			if (strcat_alloc(&xparent, xp) == NULL) goto xp_err;
+		}
+		xp = xparent;
+		parent = parent->father;
+	} while (parent != NULL);
+	if ((*xpath = sx_strdup(C2SX("/"))) == NULL || strcat_alloc(xpath, xp) == NULL) goto xp_err;
+
+	return *xpath;
+
+xp_err:
+	if (xp != NULL) __free(xp);
+	*xpath = NULL;
+
+	return NULL;
+}
diff --git a/core_server/client/sxmlc/sxmlsearch.h b/core_server/client/sxmlc/sxmlsearch.h
new file mode 100755
index 0000000..f1160aa
--- /dev/null
+++ b/core_server/client/sxmlc/sxmlsearch.h
@@ -0,0 +1,232 @@
+/*
+	Copyright (c) 2010, Matthieu Labas
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without modification,
+	are permitted provided that the following conditions are met:
+
+	1. Redistributions of source code must retain the above copyright notice,
+	   this list of conditions and the following disclaimer.
+
+	2. Redistributions in binary form must reproduce the above copyright notice,
+	   this list of conditions and the following disclaimer in the documentation
+	   and/or other materials provided with the distribution.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+	ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+	WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+	IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+	INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+	WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+	OF SUCH DAMAGE.
+
+	The views and conclusions contained in the software and documentation are those of the
+	authors and should not be interpreted as representing official policies, either expressed
+	or implied, of the FreeBSD Project.
+*/
+#ifndef _SXMLCSEARCH_H_
+#define _SXMLCSEARCH_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "sxmlc.h"
+
+/*
+ XML search parameters. Can be initialized from an XPath string.
+ A pointer to such structure is given to search functions which can modify
+ its content (the 'from' structure).
+ */
+typedef struct _XMLSearch {
+	/*
+	 Search for nodes which tag match this 'tag' field.
+	 If NULL or an empty string, all nodes will be matching.
+	 */
+	SXML_CHAR* tag;
+
+	/*
+	 Search for nodes which attributes match all the ones described.
+	 If NULL, all nodes will be matching.
+	 The 'attribute->name' should not be NULL. If corresponding 'attribute->value'
+	 is NULL or an empty-string, search will return the first node with an attribute
+	 'attribute->name', no matter what is its value.
+	 If 'attribute->value' is not NULL, a matching node should have an attribute
+	 'attribute->name' with the corresponding value 'attribute->value'.
+	 When 'attribute->value' is not NULL, the 'attribute->active' should be 'true'
+	 to specify that values should be equal, or 'false' to specify that values should
+	 be different.
+	 */
+	XMLAttribute* attributes;
+	int n_attributes;
+
+	/*
+	 Search for nodes which text match this 'text' field.
+	 If NULL or an empty string, all nodes will be matching.
+	 */
+	SXML_CHAR* text;
+
+	/*
+	 Next search to perform on children of a node matching current struct.
+	 Used to search for nodes children of specific nodes (used in XPath queries).
+	 */
+	struct _XMLSearch* next;
+	struct _XMLSearch* prev;
+
+	/*
+	 Internal use only. Must be initialized to 'INVALID_XMLNODE_POINTER' prior to first search.
+	 */
+	XMLNode* stop_at;
+
+	/* Keep 'init_value' as the last member */
+	int init_value;	/* Initialized to 'XML_INIT_DONE' to indicate that document has been initialized properly */
+} XMLSearch;
+
+typedef int (*REGEXPR_COMPARE)(SXML_CHAR* str, SXML_CHAR* pattern);
+
+/*
+ Set a new comparison function to evaluate whether a string matches a given pattern.
+ The default one is the "regstrcmp" which handles limited regular expressions.
+ 'fct' prototype is 'int fct(SXML_CHAR* str, SXML_CHAR* pattern)' where 'str' is the string to
+ evaluate the match for and 'pattern' the pattern. It should return 'true' (=1) when
+ 'str' matches 'pattern' and 'false' (=0) when it does not.
+ Return the previous function used for matching.
+ */
+REGEXPR_COMPARE XMLSearch_set_regexpr_compare(REGEXPR_COMPARE fct);
+
+/*
+ Initialize 'search' struct to an empty search.
+ No memory freeing is performed.
+ Return 'false' when 'search' is NULL.
+ */
+int XMLSearch_init(XMLSearch* search);
+
+/*
+ Free all 'search' members except for the 'search->next' member that should be freed
+ by its creator, unless 'free_next' is 'true'.
+ It is recommended that 'free_next' is positioned to 'true' only when the creator did not
+ handle the whole memory allocation chain, e.g. when using 'XMLSearch_init_from_XPath'
+ that allocates all search structs.
+ Return 'false' when 'search' is NULL.
+ */
+int XMLSearch_free(XMLSearch* search, int free_next);
+
+/*
+ Set the search based on tag.
+ 'tag' should be NULL or empty to search for any node (e.g. search based on attributes
+ only). In this case, the previous tag is freed.
+ Return 'true' upon successful completion, 'false' for memory error.
+ */
+int XMLSearch_search_set_tag(XMLSearch* search, const SXML_CHAR* tag);
+
+/*
+ Add an attribute search criteria.
+ 'attr_name' is mandatory. 'attr_value' should be NULL to test for attribute presence only
+ (no test on value). An empty string for 'attr_value' is not an equivalent to 'NULL'!
+ 'value_equal' should be specified to test for attribute value equality (='true') or
+ difference (='false).
+ Return the index of the new attribute, or '-1' for memory error.
+ */
+int XMLSearch_search_add_attribute(XMLSearch* search, const SXML_CHAR* attr_name, const SXML_CHAR* attr_value, int value_equal);
+
+/*
+ Search for attribute 'attr_name' in Search attribute list and return its index
+ or '-1' if not found.
+ */
+int XMLSearch_search_get_attribute_index(const XMLSearch* search, const SXML_CHAR* attr_name);
+
+/*
+ Removes the search attribute given by its index 'i_attr'.
+ Return the number of attributes left.
+ */
+int XMLSearch_search_remove_attribute(XMLSearch* search, int i_attr);
+
+/*
+ Set the search based on text content.
+ 'text' should be NULL or empty to search for any node (e.g. search based on attributes
+ only). In this case, the previous text is freed.
+ Return 'true' upon successful completion, 'false' for memory error.
+ */
+int XMLSearch_search_set_text(XMLSearch* search, const SXML_CHAR* text);
+
+/*
+ Set an additional search on children nodes of a previously matching node.
+ Search struct are chained to finally return the node matching the last search struct,
+ which father node matches the previous search struct, and so on.
+ This allows describing more complex search queries like XPath
+ "//FatherTag[@attrib=val]/ChildTag/".
+ In this case, a first search struct would have 'search->tag = "FatherTag"' and
+ 'search->attributes[0] = { "attrib", "val" }' and a second search struct with
+ 'search->tag = "ChildTag"'.
+ If 'children_search' is NULL, next search is removed. Freeing previous search is to be
+ performed by its owner.
+ In any case, if 'search' next search is not NULL, it is freed.
+ Return 'true' when association has been made, 'false' when an error occurred.
+ */
+int XMLSearch_search_set_children_search(XMLSearch* search, XMLSearch* children_search);
+
+/*
+ Compute an XPath-equivalent string of the 'search' criteria.
+ 'xpath' is a pointer to a string that will be allocated by the function and should
+ be freed after use.
+ 'quote' is the quote character to be used (e.g. '"' or '\''). If '\0', XML_DEFAULT_QUOTE will be used.
+ A NULL 'search' will return an empty string.
+ Return 'false' for a memory problem, 'true' otherwise.
+ */
+SXML_CHAR* XMLSearch_get_XPath_string(const XMLSearch* search, SXML_CHAR** xpath, SXML_CHAR quote);
+
+/*
+ Initialize a 'search' struct from an XPath-like query. "XPath-like" means that
+ it does not fully comply to XPath standard.
+ 'xpath' should be like "tag[.=text, @attrib="value", @attrib!='value']/tag...".
+ Warning: the XPath query on node text like 'father[child="text"]' should be
+ re-written 'father/child[.="text"]' instead (which should be XPath-compliant as well).
+ Return 'true' when 'search' was correctly initialized, 'false' in case of memory
+ problem or malformed 'xpath'.
+ */
+int XMLSearch_init_from_XPath(const SXML_CHAR* xpath, XMLSearch* search);
+
+/*
+ Check whether a 'node' matches 'search' criteria.
+ 'node->tag_type' should be 'TAG_FATHER' or 'TAG_SELF' only.
+ If 'search->prev' is not nULL (i.e. has a father search), 'node->father' is also
+ tested, recursively (i.e. grand-father and so on).
+ Return 'false' when 'node' does not match or for invalid arguments, 'true'
+ if 'node' is a match.
+ */
+int XMLSearch_node_matches(const XMLNode* node, const XMLSearch* search);
+
+/*
+ Search next matching node, according to search parameters given by 'search'.
+ Search starts from node 'from' by scanning all its children, and going up to siblings,
+ uncles and so on.
+ Searching for the next matching node is performed by running the search again on the last
+ matching node. So 'search' has to be initialized by 'XMLSearch_init' prior to the first call,
+ to memorize the initial 'from' node and know where to stop search.
+ 'from' ITSELF IS NOT CHECKED! Direct call to 'XMLSearch_node_matches(from, search);' should
+ be made if necessary.
+ If the document has several root nodes, a complete search in the document should be performed
+ by manually calling 'XMLSearch_next' on each root node in a for loop.
+ Note that 'search' should be the initial search struct (i.e. 'search->prev' should be NULL). This
+ cannot be checked/corrected by the function itself as it is partly recursive.
+ Return the next matching node according to 'search' criteria, or NULL when no more nodes match
+ or when an error occurred.
+ */
+XMLNode* XMLSearch_next(const XMLNode* from, XMLSearch* search);
+
+/*
+ Get 'node' XPath-like equivalent: 'tag[.="text", @attribute="value", ...]', potentially
+ including father nodes XPathes.
+ The computed XPath is stored in a dynamically-allocated string.
+ Return the XPath, or NULL if 'node' is invalid or on memory error.
+ */
+SXML_CHAR* XMLNode_get_XPath(XMLNode* node, SXML_CHAR** xpath, int incl_parents);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/core_server/dap_server.h b/core_server/dap_server.h
index c1dd326..b0932a7 100644
--- a/core_server/dap_server.h
+++ b/core_server/dap_server.h
@@ -27,7 +27,7 @@
 #include <pthread.h>
 #include "uthash.h"
 
-#include "../../libdap/client/dap_client_remote.h"
+#include "client/dap_client_remote.h"
 
 typedef enum dap_server_type {DAP_SERVER_TCP} dap_server_type_t;
 
diff --git a/enc_server/CMakeLists.txt b/enc_server/CMakeLists.txt
index e14abc1..d389080 100644
--- a/enc_server/CMakeLists.txt
+++ b/enc_server/CMakeLists.txt
@@ -24,4 +24,4 @@ set(${PROJECT_NAME}_DEFINITIONS CACHE INTERNAL "${PROJECT_NAME}: Definitions" FO
 
 set(${PROJECT_NAME}_INCLUDE_DIRS ${PROJECT_SOURCE_DIR} CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE)
 
-target_link_libraries(${PROJECT_NAME} dap_core dap_crypto dap_client dap_http dap_core_server dap_http_server)
+target_link_libraries(${PROJECT_NAME} dap_core dap_crypto dap_core_server dap_http_server)
diff --git a/enc_server/dap_enc_http.c b/enc_server/dap_enc_http.c
index d4bc496..c1c2d5d 100644
--- a/enc_server/dap_enc_http.c
+++ b/enc_server/dap_enc_http.c
@@ -26,7 +26,7 @@
 #include "dap_common.h"
 
 #include "dap_http.h"
-#include "dap_http_client.h"
+#include "../http_server/http_client/dap_http_client.h"
 #include "dap_http_simple.h"
 
 #include "dap_enc.h"
diff --git a/enc_server/dap_enc_ks.c b/enc_server/dap_enc_ks.c
index b04efcc..ef5f5b7 100644
--- a/enc_server/dap_enc_ks.c
+++ b/enc_server/dap_enc_ks.c
@@ -21,8 +21,8 @@
 #include "uthash.h"
 #include "dap_common.h"
 
-#include "dap_http_client.h"
-#include "dap_http_header.h"
+#include "../http_server/http_client/dap_http_client.h"
+#include "../http_server/http_client/dap_http_header.h"
 
 #include "dap_enc.h"
 #include "dap_enc_ks.h"
diff --git a/http_server/CMakeLists.txt b/http_server/CMakeLists.txt
index c0c3bb3..edf92e9 100644
--- a/http_server/CMakeLists.txt
+++ b/http_server/CMakeLists.txt
@@ -1,14 +1,23 @@
 cmake_minimum_required(VERSION 2.8)
 project (dap_http_server C)
   
-set(HTTP_SERVER_SRCS dap_http.c dap_http_folder.c  dap_http_simple.c dap_http_simple.h )
-
 include_directories("${INCLUDE_DIRECTORIES} ${dap_core_INCLUDE_DIRS}")
 include_directories("${INCLUDE_DIRECTORIES} ${dap_http_INCLUDE_DIRS}")
 include_directories("${INCLUDE_DIRECTORIES} ${dap_crypto_INCLUDE_DIRS}")
 include_directories("${INCLUDE_DIRECTORIES} ${dap_enc_server_INCLUDE_DIRS}")
 include_directories("${INCLUDE_DIRECTORIES} ${dap_client_INCLUDE_DIRS}")
 include_directories("${INCLUDE_DIRECTORIES} ${dap_core_server_INCLUDE_DIRS}")
+include_directories("${INCLUDE_DIRECTORIES} ${dap_core_server_INCLUDE_DIRS}/client")
+include_directories(http_client)
+
+set(HTTP_SERVER_SRCS
+    dap_http.c
+    dap_http_folder.c
+    dap_http_simple.c
+    dap_http_simple.h
+    http_client/dap_http_client.c
+    http_client/dap_http_client_simple.c
+    http_client/dap_http_header.c)
 
 add_definitions ("${dap_core_DEFINITIONS}")
 add_definitions ("${dap_http_DEFINITIONS}")
diff --git a/http_server/dap_http.c b/http_server/dap_http.c
index bc0aba8..025081f 100644
--- a/http_server/dap_http.c
+++ b/http_server/dap_http.c
@@ -37,7 +37,7 @@
 #include <netdb.h>
 
 #include "dap_common.h"
-#include "dap_client.h"
+#include "../core_server/client/dap_client.h"
 #include "dap_server.h"
 
 #include "dap_http.h"
diff --git a/http_server/dap_http.h b/http_server/dap_http.h
index d883a29..ab55a27 100644
--- a/http_server/dap_http.h
+++ b/http_server/dap_http.h
@@ -20,10 +20,10 @@
 
 #ifndef _SERVER_HTTP_H_
 #define _SERVER_HTTP_H_
-#include "../../libdap-server/core_server/dap_server.h"
-#include "dap_client.h"
-#include "../../libdap/http/dap_http_header.h"
-#include "../../libdap/http/dap_http_client.h"
+#include "dap_server.h"
+#include "../core_server/client/dap_client.h"
+#include "http_client/dap_http_header.h"
+#include "http_client/dap_http_client.h"
 #include "uthash.h"
 
 struct dap_http;
diff --git a/http_server/dap_http_simple.c b/http_server/dap_http_simple.c
index 32b519c..8da5597 100644
--- a/http_server/dap_http_simple.c
+++ b/http_server/dap_http_simple.c
@@ -29,8 +29,8 @@
 #include "dap_http_client.h"
 #include "dap_http_simple.h"
 #include "dap_enc_key.h"
-#include "dap_enc_ks.h"
-#include "dap_enc_http.h"
+#include "../enc_server/dap_enc_ks.h"
+#include "../enc_server/dap_enc_http.h"
 #include <ev.h>
 #include <sys/queue.h>
 
diff --git a/http_server/http_client/CMakeLists.txt b/http_server/http_client/CMakeLists.txt
new file mode 100644
index 0000000..7457a54
--- /dev/null
+++ b/http_server/http_client/CMakeLists.txt
@@ -0,0 +1,18 @@
+cmake_minimum_required(VERSION 2.8)
+project (dap_http)
+  
+set(HTTP_SRCS dap_http_client.c dap_http_client_simple.c dap_http_header.c)
+ 
+add_library(${PROJECT_NAME} STATIC ${HTTP_SRCS})
+target_link_libraries(${PROJECT_NAME}  curl)
+
+include_directories("${dap_core_INCLUDE_DIRS}")
+#include_directories("${dap_client_INCLUDE_DIRS}")
+include_directories("${dap_http_server_INCLUDE_DIRS}")
+add_definitions ("${dap_core_DEFINITIONS}")
+add_definitions ("${dap_client_DEFINITIONS}")
+add_definitions ("${dap_http_server_DEFINITIONS}")
+
+set(${PROJECT_NAME}_DEFINITIONS CACHE INTERNAL "${PROJECT_NAME}: Definitions" FORCE)
+
+set(${PROJECT_NAME}_INCLUDE_DIRS ${PROJECT_SOURCE_DIR} CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE)
diff --git a/http_server/http_client/dap_http_client.c b/http_server/http_client/dap_http_client.c
new file mode 100644
index 0000000..15264c6
--- /dev/null
+++ b/http_server/http_client/dap_http_client.c
@@ -0,0 +1,446 @@
+/*
+ Copyright (c) 2017-2018 (c) Project "DeM Labs Inc" https://github.com/demlabsinc
+  All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+    DAP (Deus Applicaions Prototypes) 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.
+
+    DAP 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 any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <libgen.h>
+#include "dap_common.h"
+#include "dap_client_remote.h"
+
+#ifdef DAP_SERVER
+#include "../http_server/dap_http.h"
+#endif
+
+#include "dap_http_header.h"
+#include "dap_http_client.h"
+
+#define LOG_TAG "http_client"
+
+#define BUF_SIZE 2048
+
+void dap_http_client_out_header_generate(dap_http_client_t *cl_ht);
+
+/**
+ * @brief dap_http_client_init Init HTTP client module
+ * @return  Zero if ok others if not
+ */
+int dap_http_client_init()
+{
+    log_it(L_NOTICE,"Initialized HTTP client module");
+    return 0;
+}
+
+/**
+ * @brief dap_http_client_deinit Deinit HTTP client module
+ */
+void dap_http_client_deinit()
+{
+    log_it(L_INFO,"HTTP client module deinit");
+}
+
+/**
+ * @brief dap_http_client_new Creates HTTP client's internal structure
+ * @param cl HTTP Client instance
+ * @param arg Additional argument (usualy not used)
+ */
+void dap_http_client_new(dap_client_remote_t * cl,void * arg)
+{
+    (void) arg;
+    cl->_inheritor = DAP_NEW_Z(dap_http_client_t);
+    dap_http_client_t * cl_ht=DAP_HTTP_CLIENT(cl);
+    cl_ht->client=cl;
+#ifdef DAP_SERVER
+    cl_ht->http= DAP_HTTP(cl->server);
+#endif
+    cl_ht->state_read=DAP_HTTP_CLIENT_STATE_START;
+    cl_ht->state_write=DAP_HTTP_CLIENT_STATE_NONE;
+
+}
+
+/**
+ * @brief dap_http_client_delete
+ * @param cl HTTP Client instance
+ * @param arg Additional argument (usualy not used)
+ */
+void dap_http_client_delete(dap_client_remote_t * cl,void * arg)
+{
+    dap_http_client_t * cl_ht=DAP_HTTP_CLIENT(cl);
+    while(cl_ht->in_headers)
+        dap_http_header_remove(&cl_ht->in_headers,cl_ht->in_headers);
+
+    while(cl_ht->out_headers)
+        dap_http_header_remove(&cl_ht->out_headers,cl_ht->out_headers);
+
+#ifdef DAP_SERVER
+    if(cl_ht->proc)
+        if(cl_ht->proc->delete_callback)
+            cl_ht->proc->delete_callback(cl_ht,NULL);
+#endif
+
+    if(cl_ht->_inheritor)
+        free(cl_ht->_inheritor);
+    (void) arg;
+}
+
+
+/**
+ * @brief detect_end_of_line Detect end of line, return position of its end (with \n symbols)
+ * @param buf Input buffer
+ * @param max_size Maximum size of this buffer minus 1 (for terminating zero)
+ * @return position of the end of line
+ */
+int detect_end_of_line(const char * buf, size_t max_size)
+{
+    size_t i;
+    for(i=0;i<max_size; i++){
+        if(buf[i]=='\n'){
+            return i;
+        }
+    }
+    return -1;
+}
+
+bool dap_http_request_line_parse(dap_http_client_t * cl_ht, char * buf, size_t buf_length)
+{
+    size_t pos;
+    size_t pos_kw_begin=0;
+    enum parse_state{PS_START=0, PS_ACTION=1, PS_URL=2, PS_TYPE=3, PS_VER_MAJOR=4, PS_VER_MINOR=5}  p_st=PS_ACTION;
+    for(pos=0;pos<buf_length; pos++){
+        if(buf[pos]=='\n'){
+            break;
+        }else if(( buf[pos]==' ')||( buf[pos]=='\t')){
+            switch(p_st){
+                case PS_ACTION:{
+                    size_t c_size= ((pos-pos_kw_begin+1)>sizeof(cl_ht->action) )?
+                                (sizeof(cl_ht->action)-1) :
+                                (pos-pos_kw_begin) ;
+                    memcpy(cl_ht->action, buf+pos_kw_begin,c_size );
+                    cl_ht->action[c_size]='\0';
+                    //log_it(L_DEBUGUG, "Input: action '%s' pos=%lu pos_kw_begin=%lu", cl_ht->action,pos,pos_kw_begin);
+                    p_st=PS_URL;
+                    pos_kw_begin=pos+1;
+                }break;
+                case PS_URL:{
+                    size_t c_size= ((pos-pos_kw_begin+1)>sizeof(cl_ht->action) )?
+                                (sizeof(cl_ht->url_path)-1) :
+                                (pos-pos_kw_begin) ;
+                    memcpy(cl_ht->url_path, buf+pos_kw_begin,c_size );
+                    cl_ht->url_path[c_size]='\0';
+                    //log_it(L_DEBUGUG, "Input: url '%s' pos=%lu pos_kw_begin=%lu", cl_ht->url_path,pos,pos_kw_begin);
+                    p_st=PS_TYPE;
+                    pos_kw_begin=pos+1;
+                }break;
+                default:
+                    break;
+            }
+        }else{
+            switch(p_st){
+                case PS_START:{
+                    p_st=PS_ACTION;
+                    pos_kw_begin=pos;
+                };break;
+                default:break;
+            }
+        }
+    }
+    return cl_ht->url_path[0]&&cl_ht->action[0];
+}
+
+/**
+ * @brief dap_http_client_read
+ * @param cl HTTP Client instance
+ * @param arg Additional argument (usualy not used)
+ */
+void dap_http_client_read(dap_client_remote_t * cl,void * arg)
+{
+
+    (void) arg;
+    dap_http_client_t * cl_ht=DAP_HTTP_CLIENT(cl);
+    char buf_line[4096];
+//    log_it(L_DEBUGUG,"HTTP client in state read %d taked bytes in input %lu",cl_ht->state_read,cl->buf_in_size);
+cnt:switch(cl_ht->state_read){
+        case DAP_HTTP_CLIENT_STATE_START:{ // Beginning of the session. We try to detect
+            int eol = detect_end_of_line(cl->buf_in,cl->buf_in_size);
+            if(eol<0){
+                return;
+            }else if((eol+3)<sizeof(buf_line) ){
+                memcpy(buf_line,cl->buf_in,eol+1);
+                dap_client_shrink_buf_in(cl,eol+1);
+                buf_line[eol+2]='\0';
+                if( dap_http_request_line_parse(cl_ht,buf_line,eol+1) ){
+                    char * query_string;
+
+                    if( query_string = strchr(cl_ht->url_path,'?'))
+                    {
+                        size_t len_after=strlen(query_string+1);
+                        if(len_after){
+                            if(len_after>(sizeof(cl_ht->in_query_string)-1))
+                                len_after=sizeof(cl_ht->in_query_string)-1;
+
+                            if(strstr(query_string, "HTTP/1.1"))
+                                strncpy(cl_ht->in_query_string,query_string+1,len_after - 11);
+                            else
+                                strncpy(cl_ht->in_query_string,query_string+1,len_after);
+
+                            if(cl_ht->in_query_string[strlen(cl_ht->in_query_string) - 1] == ' ')
+                                cl_ht->in_query_string[strlen(cl_ht->in_query_string) - 1] = '\0';
+                            query_string[0]='\0';
+                        }
+                    }
+                    //log_it(NOTICE, "Input: %s request for %s document (query string '%s')",cl_ht->action,cl_ht->url_path, cl_ht->in_query_string? cl_ht->in_query_string: "");
+                    char *b_name;
+                    char * url_cpy1, *url_cpy2;
+                    url_cpy1=strdup(cl_ht->url_path);
+                    url_cpy2=strdup(cl_ht->url_path);
+
+
+                    b_name=basename(url_cpy2);
+
+                    strncpy(cl_ht->url_path,b_name,sizeof(cl_ht->url_path));
+#ifdef DAP_SERVER
+                    char * d_name;
+                    d_name=dirname(url_cpy1);
+                    dap_http_url_proc_t * url_proc;
+
+                    HASH_FIND_STR(cl_ht->http->url_proc, d_name , url_proc);  // Find URL processor
+
+                    cl_ht->proc=url_proc;
+                    if(url_proc) {
+                        cl_ht->state_read=DAP_HTTP_CLIENT_STATE_HEADERS;
+                    }
+                    else{
+                        log_it(L_WARNING, "Input: unprocessed URL request %s is rejected", d_name);
+                        cl_ht->state_read=DAP_HTTP_CLIENT_STATE_NONE;
+                        dap_client_ready_to_read(cl_ht->client,true);
+                        dap_client_ready_to_write(cl_ht->client,true);
+                        cl_ht->reply_status_code=505;
+                        strcpy(cl_ht->reply_reason_phrase,"Error");
+                        cl_ht->state_write=DAP_HTTP_CLIENT_STATE_START;
+                        cl->buf_in_size=0;
+                        free(url_cpy1);
+                        free(url_cpy2);
+                        break;
+                    }
+#else
+                    cl_ht->state_read=DAP_HTTP_CLIENT_STATE_HEADERS;
+#endif
+                    //free(d_name);
+                    //free(b_name);
+                    free(url_cpy1);
+                    free(url_cpy2);
+                }else{
+                    log_it(L_WARNING, "Input: Wrong request line '%s'",buf_line);
+                    cl->buf_in_size=0;
+                    cl_ht->state_read=DAP_HTTP_CLIENT_STATE_NONE;
+                    dap_client_ready_to_read(cl_ht->client,false);
+                    dap_client_ready_to_write(cl_ht->client,true);
+                    cl_ht->reply_status_code=505;
+                    strcpy(cl_ht->reply_reason_phrase,"Error");
+                    cl_ht->state_write=DAP_HTTP_CLIENT_STATE_START;
+                }
+            }else{
+                log_it(L_WARNING,"Too big line in request, more than %llu symbols - thats very strange",sizeof(buf_line)-3);
+                cl->buf_in_size=0;
+                cl_ht->state_read=DAP_HTTP_CLIENT_STATE_NONE;
+                dap_client_ready_to_read(cl_ht->client,false);
+                dap_client_ready_to_write(cl_ht->client,true);
+                cl_ht->reply_status_code=505;
+                strcpy(cl_ht->reply_reason_phrase,"Error");
+                cl_ht->state_write=DAP_HTTP_CLIENT_STATE_START;
+            }
+        }break;
+        case DAP_HTTP_CLIENT_STATE_HEADERS:{ // Parse input headers
+            int eol = detect_end_of_line(cl->buf_in,cl->buf_in_size);
+            if(eol<0)
+                return;
+            else{
+                int parse_ret;
+                memcpy(buf_line,cl->buf_in,eol+1);
+                buf_line[eol-1]='\0';
+
+                parse_ret=dap_http_header_parse(cl_ht,buf_line);
+              //  log_it(L_WARNINGNG, "++ ALL HEADERS TO PARSE [%s]", buf_line);
+                if(parse_ret<0)
+                    log_it(L_WARNING,"Input: not a valid header '%s'",buf_line);
+                else if(parse_ret==1){
+                    log_it(L_INFO,"Input: HTTP headers are over");
+#ifdef DAP_SERVER
+                    if(cl_ht->proc->access_callback){
+                        bool isOk=true;
+                        cl_ht->proc->access_callback(cl_ht,&isOk);
+                        if(!isOk){
+                            log_it(L_NOTICE,"Access restricted");
+                            cl_ht->state_read=DAP_HTTP_CLIENT_STATE_NONE;
+                            dap_client_ready_to_read(cl_ht->client,false);
+                            dap_client_ready_to_write(cl_ht->client,true);
+                            cl_ht->reply_status_code=505;
+                            strcpy(cl_ht->reply_reason_phrase,"Error");
+                            cl_ht->state_write=DAP_HTTP_CLIENT_STATE_START;
+                        }
+                    }
+
+                    if(cl_ht->proc->headers_read_callback)
+                        cl_ht->proc->headers_read_callback(cl_ht,NULL);
+#endif
+                     // If no headers callback we go to the DATA processing
+                    if(cl_ht->in_content_length ){
+                        cl_ht->state_read=DAP_HTTP_CLIENT_STATE_DATA;
+                    }else{
+                        //log_it
+                        //cl_ht->state_read=DAP_HTTP_CLIENT_STATE_NONE;
+                        //cl_ht->client->ready_to_read=t;
+                        //cl_ht->client->signal_close=!cl_ht->keep_alive;
+                    }
+
+                }
+                dap_client_shrink_buf_in(cl,eol+1);
+            }
+        }break;
+        case DAP_HTTP_CLIENT_STATE_DATA:{//Read the data
+         //   log_it(L_WARNINGNG, "DBG_#002 [%s] [%s]",             cl_ht->in_query_string, cl_ht->url_path);
+
+#ifdef DAP_SERVER
+            int read_bytes=0;
+            if(cl_ht->proc->data_read_callback){
+                //while(cl_ht->client->buf_in_size){
+                    cl_ht->proc->data_read_callback(cl_ht,&read_bytes);
+                    dap_client_shrink_buf_in(cl,read_bytes);
+                //}
+            }else
+#endif
+                cl->buf_in_size=0;
+        } break;
+        case DAP_HTTP_CLIENT_STATE_NONE:{
+                cl->buf_in_size=0;
+        }break;
+
+    }
+    if(cl->buf_in_size>0){
+        //log_it(L_DEBUGUG,"Continue to process to parse input");
+        goto cnt;
+    }
+}
+
+/**
+ * @brief dap_http_client_write Process write event
+ * @param cl HTTP Client instance
+ * @param arg Additional argument (usualy not used)
+ */
+void dap_http_client_write(dap_client_remote_t * cl,void * arg)
+{
+
+    (void) arg;
+    dap_http_client_t * cl_ht=DAP_HTTP_CLIENT(cl);
+ //   log_it(L_DEBUGUG,"HTTP client write callback in state %d",cl_ht->state_write);
+    switch(cl_ht->state_write){
+        case DAP_HTTP_CLIENT_STATE_NONE: return;
+        case DAP_HTTP_CLIENT_STATE_START:{
+#ifdef DAP_SERVER
+            if(cl_ht->proc)
+                if(cl_ht->proc->headers_write_callback)
+                    cl_ht->proc->headers_write_callback(cl_ht,NULL);
+#endif
+            log_it(L_DEBUG,"Output: HTTP response with %u status code",cl_ht->reply_status_code);
+            dap_client_write_f(cl,"HTTP/1.1 %u %s\r\n",cl_ht->reply_status_code, cl_ht->reply_reason_phrase[0]?cl_ht->reply_reason_phrase:"UNDEFINED");
+            dap_http_client_out_header_generate(cl_ht);
+
+            cl_ht->state_write=DAP_HTTP_CLIENT_STATE_HEADERS;
+        }break;
+        case DAP_HTTP_CLIENT_STATE_HEADERS:{
+            dap_http_header_t * hdr=cl_ht->out_headers;
+            if(hdr==NULL){
+                log_it(L_DEBUG, "Output: headers are over (reply status code %u)",cl_ht->reply_status_code);
+                dap_client_write_f(cl,"\r\n");
+                if(cl_ht->out_content_length || cl_ht->out_content_ready){
+                    cl_ht->state_write=DAP_HTTP_CLIENT_STATE_DATA;
+                }else{
+                    log_it(L_DEBUG,"Nothing to output");
+                    cl_ht->state_write=DAP_HTTP_CLIENT_STATE_NONE;
+                    dap_client_ready_to_write(cl,false);
+
+                    cl->signal_close=true;
+                }
+                dap_client_ready_to_read(cl,true);
+            }else{
+                //log_it(L_DEBUGUG,"Output: header %s: %s",hdr->name,hdr->value);
+                dap_client_write_f(cl,"%s: %s\n",hdr->name,hdr->value);
+                dap_http_header_remove(&cl_ht->out_headers, hdr);
+            }
+        }break;
+        case DAP_HTTP_CLIENT_STATE_DATA:{
+#ifdef DAP_SERVER
+            if(cl_ht->proc)
+                if(cl_ht->proc->data_write_callback)
+                    cl_ht->proc->data_write_callback(cl_ht,NULL);
+#endif
+        }break;
+    }
+}
+
+/**
+ * @brief dap_http_client_out_header_generate Produce general headers
+ * @param cl_ht HTTP client instance
+ */
+void dap_http_client_out_header_generate(dap_http_client_t *cl_ht)
+{
+    char buf[1024];
+    time_t current_time=time(NULL);
+    time_to_rfc822(buf,sizeof(buf),current_time);
+    dap_http_header_add(&cl_ht->out_headers,"Date",buf);
+    if(cl_ht->reply_status_code==200){
+        if(cl_ht->out_last_modified){
+            time_to_rfc822(buf,sizeof(buf),cl_ht->out_last_modified);
+            dap_http_header_add(&cl_ht->out_headers,"Last-Modified",buf);
+        }
+        if(cl_ht->out_content_type[0]){
+            dap_http_header_add(&cl_ht->out_headers,"Content-Type",cl_ht->out_content_type);
+            log_it(L_DEBUG,"output: Content-Type = '%s'",cl_ht->out_content_type);
+        }
+        if(cl_ht->out_content_length){
+            snprintf(buf,sizeof(buf),"%llu",(unsigned long long)cl_ht->out_content_length);
+            dap_http_header_add(&cl_ht->out_headers,"Content-Length",buf);
+            log_it(L_DEBUG,"output: Content-Length = %llu",cl_ht->out_content_length);
+        }
+    }
+    if(cl_ht->out_connection_close ||  (!cl_ht->keep_alive) )
+        dap_http_header_add(&cl_ht->out_headers,"Connection","Close");
+#ifdef DAP_SERVER
+    dap_http_header_add(&cl_ht->out_headers,"Server-Name", cl_ht->http->server_name);
+#endif
+    log_it(L_DEBUG,"Output: Headers generated");
+}
+
+/**
+ * @brief dap_http_client_error Process errors
+ * @param cl HTTP Client instance
+ * @param arg Additional argument (usualy not used)
+ */
+void dap_http_client_error(struct dap_client_remote * cl,void * arg)
+{
+    (void) arg;
+#ifdef DAP_SERVER
+    dap_http_client_t * cl_ht=DAP_HTTP_CLIENT(cl);
+    if(cl_ht->proc)
+        if(cl_ht->proc->error_callback)
+        cl_ht->proc->error_callback(cl_ht,arg);
+#endif
+}
diff --git a/http_server/http_client/dap_http_client.h b/http_server/http_client/dap_http_client.h
new file mode 100644
index 0000000..8513365
--- /dev/null
+++ b/http_server/http_client/dap_http_client.h
@@ -0,0 +1,102 @@
+/*
+ Copyright (c) 2017-2018 (c) Project "DeM Labs Inc" https://github.com/demlabsinc
+  All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+    DAP (Deus Applicaions Prototypes) 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.
+
+    DAP 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 any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef _DAP_HTTP_CLIENT_H_
+#define _DAP_HTTP_CLIENT_H_
+
+#include <stdint.h>
+#include <time.h>
+#include <stdbool.h>
+typedef struct dap_client_remote dap_client_remote_t;
+struct dap_http_client;
+struct dap_http;
+struct dap_http_url_proc;
+
+typedef enum dap_http_client_state{
+    DAP_HTTP_CLIENT_STATE_NONE=0,
+    DAP_HTTP_CLIENT_STATE_START=1,
+    DAP_HTTP_CLIENT_STATE_HEADERS=2,
+    DAP_HTTP_CLIENT_STATE_DATA=3
+} dap_http_client_state_t;
+
+typedef void (*dap_http_client_callback_t) (struct dap_http_client *,void * arg); // Callback for specific client operations
+
+typedef struct dap_http_client
+{
+    char action[128]; // Type of HTTP action (GET, PUT and etc)
+    char url_path[2048]; // URL path of requested document
+    uint32_t http_version_major; // Major version of HTTP protocol
+    uint32_t http_version_minor; // Minor version of HTTP protocol
+    bool keep_alive;
+
+    dap_http_client_state_t state_read;
+    dap_http_client_state_t state_write;
+
+    struct dap_http_header * in_headers;
+    uint64_t in_content_length;
+    char in_content_type[256];
+    char in_query_string[1024];
+    char in_cookie[1024];
+
+    struct dap_http_header * out_headers;
+    uint64_t out_content_length;
+    bool out_content_ready;
+    char out_content_type[256];
+    time_t out_last_modified;
+    bool out_connection_close;
+
+
+    dap_client_remote_t * client;
+    struct dap_http * http;
+
+    uint32_t reply_status_code;
+    char reply_reason_phrase[256];
+
+    struct dap_http_url_proc * proc;
+
+    void * _inheritor;
+    void * _internal;
+
+} dap_http_client_t;
+
+#define DAP_HTTP_CLIENT(a)  ((dap_http_client_t *) (a)->_inheritor )
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int dap_http_client_init();
+void dap_http_client_deinit();
+
+
+void dap_http_client_new(dap_client_remote_t * cl,void * arg); // Creates HTTP client's internal structure
+void dap_http_client_delete(dap_client_remote_t * cl,void * arg); // Free memory for HTTP client's internal structure
+
+void dap_http_client_read( dap_client_remote_t * cl,void * arg); // Process read event
+void dap_http_client_write( dap_client_remote_t * cl,void * arg); // Process write event
+void dap_http_client_error( dap_client_remote_t * cl,void * arg); // Process error event
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/http_server/http_client/dap_http_client_simple.c b/http_server/http_client/dap_http_client_simple.c
new file mode 100644
index 0000000..643e818
--- /dev/null
+++ b/http_server/http_client/dap_http_client_simple.c
@@ -0,0 +1,326 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <time.h>
+#include <curl/curl.h>
+
+#include "utlist.h"
+
+#include "dap_common.h"
+
+#include "dap_http_client.h"
+#include "dap_http_client_simple.h"
+
+typedef struct dap_http_client_internal{
+
+    dap_http_client_simple_callback_data_t response_callback;
+    dap_http_client_simple_callback_error_t error_callback;
+    void * obj;
+    uint8_t * request;
+    size_t request_size;
+    size_t request_sent_size;
+    struct curl_slist * request_headers;
+
+    uint8_t * response;
+    size_t response_size;
+    size_t response_size_max;
+} dap_http_client_internal_t;
+
+CURLM *m_curl_mh; // Multi-thread handle to stack lot of parallel requests
+pthread_t curl_pid=0;
+pthread_cond_t m_curl_cond = PTHREAD_COND_INITIALIZER;
+pthread_mutex_t m_curl_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void *dap_http_client_thread(void * arg);
+size_t dap_http_client_curl_request_callback(char * a_ptr, size_t a_size, size_t a_nmemb, void * a_userdata);
+size_t dap_http_client_curl_response_callback(char * a_ptr, size_t a_size, size_t a_nmemb, void * a_userdata);
+void dap_http_client_internal_delete(dap_http_client_internal_t * a_client);
+
+#define DAP_HTTP_CLIENT_RESPONSE_SIZE_MAX 40960
+
+#define LOG_TAG "dap_http_client"
+
+/**
+ * @brief dap_http_client_init
+ * @return
+ */
+int dap_http_client_simple_init()
+{
+    curl_global_init(CURL_GLOBAL_ALL);
+    m_curl_mh = curl_multi_init();
+    pthread_create(&curl_pid,NULL,dap_http_client_thread,NULL );
+    return 0;
+}
+
+
+/**
+ * @brief dap_http_client_deinit
+ */
+void dap_http_client_simple_deinit()
+{
+    curl_multi_cleanup( m_curl_mh );
+}
+
+/**
+ * @brief dap_http_client_internal_delete
+ * @param a_client
+ */
+void dap_http_client_internal_delete(dap_http_client_internal_t * a_client_internal)
+{
+    if( a_client_internal->request_headers)
+          curl_slist_free_all( a_client_internal->request_headers );
+
+    if ( a_client_internal->request )
+        free(a_client_internal->request);
+
+    if ( a_client_internal->response )
+        free(a_client_internal->response);
+
+    free(a_client_internal);
+}
+
+/**
+ * @brief dap_http_client_simple_request
+ * @param a_url
+ * @param a_method
+ * @param a_request_content_type
+ * @param a_request
+ * @param a_request_size
+ * @param a_response_callback
+ * @param a_error_callback
+ * @param a_obj
+ */
+void dap_http_client_simple_request(const char * a_url, const char * a_method, const char* a_request_content_type, void *a_request, size_t a_request_size, char * a_cookie, dap_http_client_simple_callback_data_t a_response_callback,
+                                   dap_http_client_simple_callback_error_t a_error_callback, void *a_obj, void * a_custom)
+{
+    log_it(L_DEBUG,"Simple HTTP request with static predefined buffer (%lu bytes) on url '%s'",
+           DAP_HTTP_CLIENT_RESPONSE_SIZE_MAX, a_url);
+    CURL *l_curl_h = curl_easy_init();
+
+    dap_http_client_internal_t * l_client_internal = DAP_NEW_Z(dap_http_client_internal_t);
+    l_client_internal->error_callback = a_error_callback;
+    l_client_internal->response_callback = a_response_callback;
+    l_client_internal->obj = a_obj;
+
+    l_client_internal->response_size_max = DAP_HTTP_CLIENT_RESPONSE_SIZE_MAX;
+    l_client_internal->response = (uint8_t*) calloc(1,DAP_HTTP_CLIENT_RESPONSE_SIZE_MAX);
+
+    l_client_internal->request = malloc(a_request_size);
+    memcpy(l_client_internal->request, a_request, a_request_size);
+    l_client_internal->request_size = a_request_size;
+
+    if( ( a_request ) && ( (
+                              (strcmp( a_method , "POST" ) == 0)  ||
+                              (strcmp( a_method , "POST_ENC" ) == 0)
+                          ) ) ){
+        char l_buf[1024];
+        log_it ( L_DEBUG , "POST request with %u bytes of decoded data" , a_request_size );
+
+        if( a_request_content_type )
+            l_client_internal->request_headers = curl_slist_append(l_client_internal->request_headers, a_request_content_type );
+
+        if( a_custom )
+            l_client_internal->request_headers = curl_slist_append(l_client_internal->request_headers,(char*) a_custom );
+
+        if( a_cookie )
+            l_client_internal->request_headers = curl_slist_append(l_client_internal->request_headers,(char*) a_cookie );
+
+        snprintf(l_buf,sizeof(l_buf),"Content-Length: %lu", a_request_size );
+        l_client_internal->request_headers = curl_slist_append(l_client_internal->request_headers, l_buf);
+
+        //curl_easy_setopt( l_curl_h , CURLOPT_READDATA , l_client_internal );
+        curl_easy_setopt( l_curl_h , CURLOPT_POST , 1 );
+        curl_easy_setopt( l_curl_h , CURLOPT_POSTFIELDSIZE, a_request_size );
+
+    }
+    if(l_client_internal->request_headers)
+        curl_easy_setopt(l_curl_h, CURLOPT_HTTPHEADER, l_client_internal->request_headers);
+
+    curl_easy_setopt( l_curl_h , CURLOPT_PRIVATE, l_client_internal );
+    curl_easy_setopt( l_curl_h , CURLOPT_URL, a_url);
+
+    curl_easy_setopt( l_curl_h , CURLOPT_READDATA , l_client_internal  );
+    curl_easy_setopt( l_curl_h , CURLOPT_READFUNCTION , dap_http_client_curl_request_callback );
+
+    curl_easy_setopt( l_curl_h , CURLOPT_WRITEDATA , l_client_internal );
+    curl_easy_setopt( l_curl_h , CURLOPT_WRITEFUNCTION , dap_http_client_curl_response_callback );
+
+    curl_multi_add_handle( m_curl_mh, l_curl_h );
+    //curl_multi_perform(m_curl_mh, &m_curl_cond);
+    pthread_cond_signal( &m_curl_cond);
+    send_select_break();
+}
+
+/**
+ * @brief dap_http_client_curl_response_callback
+ * @param a_ptr
+ * @param a_size
+ * @param a_nmemb
+ * @param a_userdata
+ * @return
+ */
+size_t dap_http_client_curl_response_callback(char * a_ptr, size_t a_size, size_t a_nmemb, void * a_userdata)
+{
+    dap_http_client_internal_t * l_client_internal = (dap_http_client_internal_t *) a_userdata;
+    log_it(L_DEBUG, "Recieved %lu bytes in HTTP resonse", a_size*a_nmemb);
+    if( l_client_internal->response_size < l_client_internal->response_size_max){
+        size_t l_size = a_size * a_nmemb;
+        if( l_size > ( l_client_internal->response_size_max - l_client_internal->response_size) )
+            l_size = l_client_internal->response_size_max - l_client_internal->response_size;
+        memcpy(l_client_internal->response + l_client_internal->response_size,a_ptr,l_size);
+        l_client_internal->response_size += l_size;
+    }else{
+        log_it(L_WARNING,"Too big reply, %lu bytes a lost",a_size*a_nmemb);
+    }
+    return a_size*a_nmemb;
+}
+
+/**
+ * @brief dap_http_client_curl_request_callback
+ * @param a_ptr
+ * @param a_size
+ * @param a_nmemb
+ * @param a_userdata
+ * @return
+ */
+size_t dap_http_client_curl_request_callback(char * a_ptr, size_t a_size, size_t a_nmemb, void * a_userdata)
+{
+    dap_http_client_internal_t * l_client_internal = (dap_http_client_internal_t *) a_userdata;
+    size_t l_size = a_size * a_nmemb;
+    if( ( l_size + l_client_internal->request_sent_size) > l_client_internal->request_size )
+        l_size = l_client_internal->request_size - l_client_internal->request_sent_size;
+
+    if( l_size ) {
+        memcpy( a_ptr, l_client_internal->request + l_client_internal->request_sent_size, l_size );
+        l_client_internal->request_sent_size += l_size;
+    }
+    return l_size;
+}
+
+/**
+ * @brief dap_http_client_thread
+ * @param arg
+ */
+static void* dap_http_client_thread(void * arg)
+{
+    (void) arg;
+    bool l_still_running = true;
+    do {
+        struct timeval timeout;
+        int rc; /* select() return code */
+        CURLMcode mc; /* curl_multi_fdset() return code */
+
+        fd_set fdread;
+        fd_set fdwrite;
+        fd_set fdexcep;
+        int maxfd = -1;
+
+        long curl_timeo = -1;
+
+        FD_ZERO(&fdread);
+        FD_ZERO(&fdwrite);
+        FD_ZERO(&fdexcep);
+
+        /* set a suitable timeout to play around with */
+        timeout.tv_sec = 10;
+        timeout.tv_usec = 0;
+
+        curl_multi_timeout( m_curl_mh, &curl_timeo);
+        if(curl_timeo >= 0) {
+          timeout.tv_sec = curl_timeo / 1000;
+          if(timeout.tv_sec > 1)
+            timeout.tv_sec = 1;
+          else
+            timeout.tv_usec = (curl_timeo % 1000) * 1000;
+        }
+
+        /* get file descriptors from the transfers */
+        mc = curl_multi_fdset(m_curl_mh, &fdread, &fdwrite, &fdexcep, &maxfd);
+        FD_SET(get_select_breaker(),&fdread);
+        if(get_select_breaker() > maxfd)
+            maxfd = get_select_breaker();
+
+        if(mc != CURLM_OK) {
+          log_it(L_ERROR, "curl_multi_fdset() failed, code %d.\n", mc);
+          break;
+        }
+
+        /* On success the value of maxfd is guaranteed to be >= -1. We call
+           select(maxfd + 1, ...); specially in case of (maxfd == -1) there are
+           no fds ready yet so we call select(0, ...) --or Sleep() on Windows--
+           to sleep 100ms, which is the minimum suggested value in the
+           curl_multi_fdset() doc. */
+        if(maxfd == -1) {
+            //log_it(L_DEBUG, "Waiting for signal");
+            pthread_cond_wait(&m_curl_cond,&m_curl_mutex);
+        }  else {
+            //log_it(L_DEBUG, "Selecting stuff");
+          /* Note that on some platforms 'timeout' may be modified by select().
+             If you need access to the original value save a copy beforehand. */
+          rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout);
+        }
+
+        switch(rc) {
+            case -1: {
+              /* select error */
+            } break;
+            case 0: /* timeout */
+            default: { /* action */
+              int l_curl_eh_count = 0;
+              curl_multi_perform( m_curl_mh , &l_curl_eh_count );
+               // Check if we have smth complete
+              struct CURLMsg *m;
+              do {
+                  int msgq = 0;
+                  m = curl_multi_info_read(m_curl_mh, &msgq);
+                  if(m && (m->msg == CURLMSG_DONE)) {
+                      CURL *e = m->easy_handle;
+                      char * l_private = NULL;
+                      int l_err_code = 0;
+                      curl_easy_getinfo( e, CURLINFO_PRIVATE, &l_private );
+                      if( l_private ){
+                          bool l_is_ok = false;
+                          dap_http_client_internal_t * l_client_internal = (dap_http_client_internal_t *) l_private;
+                          switch ( m->data.result){
+                              case CURLE_OUT_OF_MEMORY: l_err_code = 1 ; log_it(L_CRITICAL, "Out of memory"); break;
+                              case CURLE_COULDNT_CONNECT: l_err_code = 2 ; log_it(L_ERROR, "Couldn't connect to the destination server"); break;
+                              case CURLE_COULDNT_RESOLVE_HOST: l_err_code = 3 ; log_it(L_ERROR, "Couldn't resolve destination address"); break;
+                              case CURLE_OPERATION_TIMEDOUT: l_err_code = 4 ; log_it(L_ERROR, "HTTP request timeout"); break;
+                              case CURLE_URL_MALFORMAT: l_err_code = 5 ; log_it(L_ERROR, "Wrong URL format in the outgoing request"); break;
+                              case CURLE_FTP_WEIRD_SERVER_REPLY: l_err_code = 6 ; log_it(L_WARNING, "Weird server reply"); break;
+                              case CURLE_OK:{
+                                l_is_ok = true;
+                                log_it(L_DEBUG, "Response size %u",l_client_internal->response_size);
+                              }break;
+                              default: l_err_code = 12345;
+                          }
+                          if( l_is_ok){
+                                l_client_internal->response_callback(l_client_internal->response,
+                                                                   l_client_internal->response_size,
+                                                                   l_client_internal->obj );
+                          }else {
+                                log_it(L_WARNING, "HTTP request wasn't processed well with error code %d",m->data.result );
+                                l_client_internal->error_callback(l_err_code , l_client_internal->obj );
+
+                          }
+                          dap_http_client_internal_delete(l_client_internal);
+                      } else {
+                        log_it(L_CRITICAL, "Can't get private information from libcurl handle to perform the reply to SAP connection");
+                      }
+                      curl_multi_remove_handle(m_curl_mh, e);
+                      curl_easy_cleanup(e);
+                  }
+             } while(m);
+            } break;
+        }
+    } while(l_still_running);
+
+    return NULL;
+}
+
+
diff --git a/http_server/http_client/dap_http_client_simple.h b/http_server/http_client/dap_http_client_simple.h
new file mode 100644
index 0000000..ac5ff11
--- /dev/null
+++ b/http_server/http_client/dap_http_client_simple.h
@@ -0,0 +1,20 @@
+#ifndef DAP_HTTP_CLIENT_H
+#define DAP_HTTP_CLIENT_H
+#include <stddef.h>
+struct dap_http_client_simple;
+typedef void (*dap_http_client_simple_callback_error_t) (int,void *); // Callback for specific http client operations
+typedef void (*dap_http_client_simple_callback_data_t) (void *,size_t,void *); // Callback for specific http client operations
+
+typedef struct dap_http_client_simple {
+    void * _inheritor;
+} dap_http_client_simple_t;
+
+int dap_http_client_simple_init();
+void dap_http_client_simple_deinit();
+
+void dap_http_client_simple_request(const char * a_url, const char * a_method,
+                                   const char* a_request_content_type , void *a_request, size_t a_request_size, char * a_cookie,
+                                   dap_http_client_simple_callback_data_t a_response_callback,
+                                   dap_http_client_simple_callback_error_t a_error_callback, void *a_obj, void * a_custom);
+
+#endif
diff --git a/http_server/http_client/dap_http_header.c b/http_server/http_client/dap_http_header.c
new file mode 100644
index 0000000..083f247
--- /dev/null
+++ b/http_server/http_client/dap_http_header.c
@@ -0,0 +1,193 @@
+/*
+ Copyright (c) 2017-2018 (c) Project "DeM Labs Inc" https://github.com/demlabsinc
+  All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+    DAP (Deus Applicaions Prototypes) 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.
+
+    DAP 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 any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include "dap_common.h"
+#include "dap_client.h"
+#include "dap_http_client.h"
+#include "dap_http_header.h"
+
+#define LOG_TAG "http_header"
+
+
+/**
+ * @brief dap_http_header_init Init module
+ * @return Zero if ok others if not
+ */
+int dap_http_header_init()
+{
+    log_it(L_NOTICE, "Initialized HTTP headers module");
+    return 0;
+}
+
+/**
+ * @brief dap_http_header_deinit Deinit module
+ */
+void dap_http_header_deinit()
+{
+    log_it(L_INFO, "HTTP headers module deinit");
+}
+
+
+/**
+ * @brief dap_http_header_parse Parse string with HTTP header
+ * @param top Top of list with HTTP header structures
+ * @param str String to parse
+ * @return Zero if parsed well -1 if it wasn't HTTP header 1 if its "\r\n" string
+ */
+int dap_http_header_parse(struct dap_http_client * cl_ht, const char * str)
+{
+
+    char name[256], value[1024];
+
+    size_t str_len=strlen(str);
+    //sn=sscanf(str,"%255s: %1023s\r\n",name,value);
+    size_t pos;
+    if( str_len==0 )
+        return 1;
+
+    //log_it(L_DEBUG, "Parse header string '%s'",str);
+    for(pos=1; pos<str_len;pos++)
+        if(str[pos]==':'){
+            size_t name_len;
+            name_len=pos;
+            if(name_len>(sizeof(name)-1) )
+                name_len=(sizeof(name)-1);
+            strncpy(name,str,name_len);
+            name[name_len]='\0';
+
+       //     log_it(L_DEBUGUG, "Found name '%s'",name);
+            pos+=2;
+            size_t value_len=str_len-pos;
+            if(value_len>(sizeof(value)-1))
+                value_len=(sizeof(value)-1);
+            strncpy(value,str+pos,value_len);
+            value[value_len]='\0';
+           // log_it(L_DEBUGUG, "Found value '%s'",value);
+
+            if(strcmp(name,"Connection")==0){
+                if(strcmp(value,"Keep-Alive")==0){
+                    log_it(L_INFO, "Input: Keep-Alive connection detected");
+                    cl_ht->keep_alive=true;
+                }
+            }else if(strcmp(name,"Content-Type")==0){
+                strncpy(cl_ht->in_content_type,value,sizeof(cl_ht->in_content_type));
+            }else if(strcmp(name,"Content-Length")==0){
+                cl_ht->in_content_length =atoi(value);
+            }else  if(strcmp(name,"Cookie")==0){
+                strncpy(cl_ht->in_cookie,value,sizeof(cl_ht->in_cookie));
+            }
+
+
+            //log_it(L_DEBUG, "Input: Header\t%s '%s'",name,value);
+
+            dap_http_header_add(&cl_ht->in_headers,name,value);
+            return 0;
+        }
+
+
+    log_it(L_ERROR,"Input: Wasn't found ':' symbol in the header");
+    return -1;
+}
+
+
+
+/**
+ * @brief http_header_add Add HTTP header to the HTTP server instance
+ * @param sh HTTP server instance
+ * @param name  Header's name
+ * @param value Header's value
+ * @return Pointer to the new HTTP header's structure
+ */
+dap_http_header_t* dap_http_header_add(dap_http_header_t ** top, const char*name, const char * value)
+{
+    dap_http_header_t * nh = (dap_http_header_t*) calloc(1,sizeof(dap_http_header_t));
+  //  log_it(L_DEBUG,"Added header %s",name);
+    nh->name=strdup(name);
+    nh->value=strdup(value);
+    nh->next=*top;
+    if(*top)
+        (*top)->prev=nh;
+    *top=nh;
+    return nh;
+}
+
+
+struct dap_http_header* dap_http_out_header_add(dap_http_client_t * ht, const char*name, const char * value)
+{
+    return dap_http_header_add(&ht->out_headers,name,value);
+}
+
+
+/**
+ * @brief dap_http_out_header_add_f Add header to the output queue with format-filled string
+ * @param ht HTTP client instance
+ * @param name Header name
+ * @param value Formatted string to header value
+ * @param ... Arguments for formatted string
+ * @return
+ */
+dap_http_header_t * dap_http_out_header_add_f(dap_http_client_t * ht, const char*name, const char * value,...)
+{
+    va_list ap;
+    dap_http_header_t * ret;
+    char buf[1024];
+    va_start(ap,value);
+    vsnprintf(buf,sizeof(buf)-1,value,ap);
+    ret=dap_http_out_header_add(ht,name,buf);
+    va_end(ap);
+    return ret;
+}
+
+/**
+ * @brief dap_http_header_remove Removes header from the list
+ * @param dap_hdr HTTP header
+ */
+void dap_http_header_remove(dap_http_header_t ** top, dap_http_header_t * hdr )
+{
+    if(hdr->prev)
+        hdr->prev=hdr->next;
+    else
+        *top=hdr->next;
+
+    if(hdr->next)
+        hdr->next->prev=hdr->prev;
+    free(hdr->name);
+    free(hdr->value);
+}
+
+/**
+ * @brief dap_http_header_find Looks or the header with specified name
+ * @param top Top of the list
+ * @param name Name of the header
+ * @return NULL if not found or pointer to structure with found item
+ */
+dap_http_header_t * dap_http_header_find(dap_http_header_t * top, const char*name)
+{
+    dap_http_header_t * ret;
+    for(ret=top; ret; ret=ret->next)
+        if(strcmp(ret->name,name)==0)
+            return ret;
+    return ret;
+}
diff --git a/http_server/http_client/dap_http_header.h b/http_server/http_client/dap_http_header.h
new file mode 100644
index 0000000..23ed7c9
--- /dev/null
+++ b/http_server/http_client/dap_http_header.h
@@ -0,0 +1,49 @@
+/*
+ Copyright (c) 2017-2018 (c) Project "DeM Labs Inc" https://github.com/demlabsinc
+  All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+    DAP (Deus Applicaions Prototypes) 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.
+
+    DAP 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 any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef _DAP_HTTP_HEADER_H_
+#define _DAP_HTTP_HEADER_H_
+
+//Structure for holding HTTP header in the bidirectional list
+typedef struct dap_http_header{
+    char * name;
+    char * value;
+    struct dap_http_header * next;
+    struct dap_http_header * prev;
+} dap_http_header_t;
+
+struct dap_http_client;
+
+extern int dap_http_header_init(); // Init module
+extern void dap_http_header_deinit(); // Deinit module
+
+extern int dap_http_header_parse(struct dap_http_client * cl_ht, const char * str);
+
+extern dap_http_header_t * dap_http_header_add(dap_http_header_t ** top, const char*name, const char * value);
+
+extern dap_http_header_t * dap_http_out_header_add(struct dap_http_client * ht, const char*name, const char * value);
+extern dap_http_header_t * dap_http_out_header_add_f(struct dap_http_client * ht, const char*name, const char * value,...);
+
+extern dap_http_header_t * dap_http_header_find(dap_http_header_t * top, const char*name);
+
+extern void dap_http_header_remove(dap_http_header_t ** top,dap_http_header_t * hdr );
+
+#endif
diff --git a/http_server/http_client/http.pri b/http_server/http_client/http.pri
new file mode 100644
index 0000000..4c88864
--- /dev/null
+++ b/http_server/http_client/http.pri
@@ -0,0 +1,15 @@
+HEADERS += $$PWD/dap_http_client.h \
+    $$PWD/dap_http_client_simple.h \
+    $$PWD/dap_http_header.h
+
+
+SOURCES += $$PWD/dap_http_client.c \
+    $$PWD/dap_http_client_simple.c \
+    $$PWD/dap_http_header.c
+
+
+linux-* {
+    LIBS += -lcurl
+}
+
+INCLUDEPATH += $$PWD
diff --git a/udp_server/dap_udp_client.h b/udp_server/dap_udp_client.h
index 96b465f..1206fc0 100644
--- a/udp_server/dap_udp_client.h
+++ b/udp_server/dap_udp_client.h
@@ -27,7 +27,7 @@
 #include <stdbool.h>
 #include <sys/queue.h>
 #include "uthash.h"
-#include "dap_client_remote.h"
+#include "../core_server/client/dap_client_remote.h"
 #include <ev.h>
 
 
@@ -62,4 +62,4 @@ size_t dap_udp_client_write_f(dap_client_remote_t *a_client, const char * a_form
 void add_waiting_client(dap_client_remote_t* client); // Add client to writing queue
 
 
-#endif
\ No newline at end of file
+#endif
diff --git a/udp_server/dap_udp_server.h b/udp_server/dap_udp_server.h
index 192d945..364e786 100644
--- a/udp_server/dap_udp_server.h
+++ b/udp_server/dap_udp_server.h
@@ -32,7 +32,7 @@
 #include <sys/queue.h>
 #include "dap_udp_client.h"
 #include "../../libdap-server/core_server/dap_server.h"
-#include "dap_client_remote.h"
+#include "../core_server/client/dap_client_remote.h"
 
 struct dap_udp_server;
 
-- 
GitLab