From 888ca6cf83cbd682a41a3b8d3d949045cb474b6f Mon Sep 17 00:00:00 2001
From: "Constantin P." <papizh.konstantin@demlabs.net>
Date: Fri, 21 Feb 2025 09:02:24 +0000
Subject: [PATCH] Develop port 02.25

---
 core/include/dap_strfuncs.h            |   1 +
 core/src/dap_file_utils.c              |   4 +-
 core/src/dap_strfuncs.c                |  10 +
 core/src/dap_time.c                    |  31 ++-
 io/dap_server.c                        |  55 +++--
 io/include/dap_net.h                   |   2 +
 io/include/dap_server.h                |   1 +
 net/server/cli_server/dap_cli_server.c | 275 ++++++++++++-------------
 8 files changed, 201 insertions(+), 178 deletions(-)

diff --git a/core/include/dap_strfuncs.h b/core/include/dap_strfuncs.h
index b9a238400..c00e70bbd 100755
--- a/core/include/dap_strfuncs.h
+++ b/core/include/dap_strfuncs.h
@@ -39,6 +39,7 @@ DAP_PRINTF_ATTR(1, 2) char *dap_strdup_printf(const char *a_format, ...);
 char *dap_strncpy(char *a_dst, const char *a_src, size_t a_limit);
 char* dap_stpcpy(char *a_dest, const char *a_src);
 char* dap_strstr_len(const char *a_haystack, ssize_t a_haystack_len, const char *a_needle);
+const char* dap_str_find(const char **a_str_array, const char *a_str);
 // concatenates all of str_array's strings, sliding in an optional separator, the returned string is newly allocated.
 char* dap_strjoinv(const char *a_separator, char **a_str_array);
 char *dap_strjoin(const char *a_separator, ...);
diff --git a/core/src/dap_file_utils.c b/core/src/dap_file_utils.c
index 19007826a..6d9084af8 100755
--- a/core/src/dap_file_utils.c
+++ b/core/src/dap_file_utils.c
@@ -775,12 +775,12 @@ char *dap_file_get_contents2(const char *a_filename, size_t *length)
 #endif
         return log_it(L_ERROR, "Can't open file \"%s\", error %d: %s", a_filename, l_err, dap_strerror(l_err)), NULL;
     }
-    off_t l_size = fseeko(f, 0, SEEK_END) ? ftello(f) : -1;
+    off_t l_size = !fseeko(f, 0, SEEK_END) ? ftello(f) : -1;
     char *l_buffer = NULL;
     if (l_size <= 0) {
         log_it(L_ERROR, "Can't get file %s size or file is empty", a_filename);
         l_err = -3;
-    } else if (!( l_buffer = DAP_NEW_Z_SIZE(char, l_size)) ) {
+    } else if (!( l_buffer = DAP_NEW_Z_SIZE(char, l_size + 1)) ) {
         log_it(L_CRITICAL, "%s", c_error_memory_alloc);
         l_err = -4;
     } else {
diff --git a/core/src/dap_strfuncs.c b/core/src/dap_strfuncs.c
index d8f1923f9..baa48d5e2 100755
--- a/core/src/dap_strfuncs.c
+++ b/core/src/dap_strfuncs.c
@@ -516,6 +516,16 @@ char* dap_strjoinv(const char *a_separator, char **a_str_array)
     return l_string;
 }
 
+const char* dap_str_find(const char **a_str_array, const char *a_str) {
+    if (!a_str_array || !a_str)
+        return NULL;
+    for (size_t i = 0; !!a_str_array[i]; ++i) {
+        if ( !dap_strcmp(a_str, a_str_array[i]) )
+            return (const char*)a_str_array[i];
+    }
+    return NULL;
+}
+
 /**
  * dap_strjoin:
  * @a_separator: (allow-none): a string to insert between each of the
diff --git a/core/src/dap_time.c b/core/src/dap_time.c
index 51a6d4af4..eb45e4bab 100644
--- a/core/src/dap_time.c
+++ b/core/src/dap_time.c
@@ -83,11 +83,9 @@ dap_time_t dap_time_now(void)
  */
 dap_nanotime_t dap_nanotime_now(void)
 {
-    dap_nanotime_t l_time_nsec;
     struct timespec cur_time;
     clock_gettime(CLOCK_REALTIME, &cur_time);
-    l_time_nsec = (dap_nanotime_t)cur_time.tv_sec * DAP_NSEC_PER_SEC + cur_time.tv_nsec;
-    return l_time_nsec;
+    return (dap_nanotime_t)cur_time.tv_sec * DAP_NSEC_PER_SEC + cur_time.tv_nsec;
 }
 
 /**
@@ -180,16 +178,13 @@ int dap_time_to_str_rfc822(char *a_out, size_t a_out_size_max, dap_time_t a_time
  */
 dap_time_t dap_time_from_str_rfc822(const char *a_time_str)
 {
-    dap_time_t l_time = 0;
-    if(!a_time_str) {
-        return l_time;
-    }
-    struct tm l_tm = {};
-    strptime(a_time_str, "%d %b %Y %T %z", &l_tm);
-
+    dap_return_val_if_fail(a_time_str, 0);
+    struct tm l_tm = { };
+    char *ret = strptime(a_time_str, "%d %b %Y %T %z", &l_tm);
+    if ( !ret || *ret )
+        return log_it(L_ERROR, "Invalid timestamp \"%s\", expected RFC822 string", a_time_str), 0;
     time_t tmp = mktime(&l_tm);
-    l_time = (tmp <= 0) ? 0 : tmp;
-    return l_time;
+    return tmp > 0 ? (dap_time_t)tmp : 0;
 }
 
 /**
@@ -199,16 +194,14 @@ dap_time_t dap_time_from_str_rfc822(const char *a_time_str)
  */
 dap_time_t dap_time_from_str_simplified(const char *a_time_str)
 {
-    dap_time_t l_time = 0;
-    if(!a_time_str) {
-        return l_time;
-    }
+    dap_return_val_if_fail(a_time_str, 0);
     struct tm l_tm = {};
-    strptime(a_time_str, "%y%m%d", &l_tm);
+    char *ret = strptime(a_time_str, "%y%m%d", &l_tm);
+    if ( !ret || *ret )
+        return log_it(L_ERROR, "Invalid timestamp \"%s\", expected simplified string \"yy\"mm\"dd", a_time_str), 0;
     l_tm.tm_sec++;
     time_t tmp = mktime(&l_tm);
-    l_time = (tmp <= 0) ? 0 : tmp;
-    return l_time;
+    return tmp > 0 ? (dap_time_t)tmp : 0;
 }
 
 /**
diff --git a/io/dap_server.c b/io/dap_server.c
index 0317f1541..f235d0f69 100644
--- a/io/dap_server.c
+++ b/io/dap_server.c
@@ -289,6 +289,14 @@ dap_server_t *dap_server_new(const char *a_cfg_section, dap_events_socket_callba
             if ( dap_server_listen_addr_add(l_server, l_cur_ip, l_cur_port, DESCRIPTOR_TYPE_SOCKET_LISTENING, &l_callbacks) )
                 log_it( L_ERROR, "Can't add address \"%s : %u\" to listen in server", l_cur_ip, l_cur_port);
         }
+
+        l_server->whitelist = dap_config_get_array_str(g_config, a_cfg_section, DAP_CFG_PARAM_WHITE_LIST, NULL);
+        l_server->blacklist = dap_config_get_array_str(g_config, a_cfg_section, DAP_CFG_PARAM_BLACK_LIST, NULL);
+
+        if (l_server->whitelist && l_server->blacklist) {
+            log_it(L_CRITICAL, "Server can't have both black- and whitelists, fix section [%s]", a_cfg_section);
+            l_server->whitelist = NULL; /* Blacklist will have priority */
+        }
     }
     if (!l_server->es_listeners) {
         log_it(L_INFO, "Server with no listeners created. "
@@ -329,7 +337,7 @@ static void s_es_server_accept(dap_events_socket_t *a_es_listener, SOCKET a_remo
     dap_server_t *l_server = a_es_listener->server;
     assert(l_server);
 
-    dap_events_socket_t * l_es_new = NULL;
+    dap_events_socket_t *l_es_new = NULL;
     debug_if(l_server->ext_log, L_DEBUG, "Listening socket %"DAP_FORMAT_SOCKET" uuid "DAP_FORMAT_ESOCKET_UUID" binded on %s:%u "
                                          "accepted new connection from remote %"DAP_FORMAT_SOCKET"",
                                          a_es_listener->socket, a_es_listener->uuid,
@@ -342,43 +350,54 @@ static void s_es_server_accept(dap_events_socket_t *a_es_listener, SOCKET a_remo
                         a_es_listener->socket, errno, dap_strerror(errno));
         return;
     }
-    l_es_new = dap_events_socket_wrap_no_add(a_remote_socket, &l_server->client_callbacks);
-    l_es_new->server = l_server;
-    unsigned short l_family = a_remote_addr->ss_family;
-    l_es_new->type = DESCRIPTOR_TYPE_SOCKET_CLIENT;
-    l_es_new->addr_storage = *a_remote_addr;
-    char l_port_str[NI_MAXSERV];
+    char l_remote_addr_str[INET6_ADDRSTRLEN] = "", l_port_str[NI_MAXSERV] = "";
 
-    switch (l_family) {
+    dap_events_desc_type_t l_es_type = DESCRIPTOR_TYPE_SOCKET_CLIENT;
+    switch (a_remote_addr->ss_family) {
 #ifdef DAP_OS_UNIX
     case AF_UNIX:
-        l_es_new->type = DESCRIPTOR_TYPE_SOCKET_LOCAL_CLIENT;
+        l_es_type = DESCRIPTOR_TYPE_SOCKET_LOCAL_CLIENT;
         debug_if(l_server->ext_log, L_INFO, "Connection accepted at \"%s\", socket %"DAP_FORMAT_SOCKET,
                                             a_es_listener->remote_addr_str, a_remote_socket);
         break;
 #endif
     case AF_INET:
     case AF_INET6:
-        if (getnameinfo((struct sockaddr*)a_remote_addr, sizeof(*a_remote_addr), l_es_new->remote_addr_str,
-            sizeof(l_es_new->remote_addr_str), l_port_str, sizeof(l_port_str), NI_NUMERICHOST | NI_NUMERICSERV))
+        if ( getnameinfo((struct sockaddr*)a_remote_addr, sizeof(*a_remote_addr), 
+                         l_remote_addr_str, sizeof(l_remote_addr_str),
+                         l_port_str, sizeof(l_port_str), NI_NUMERICHOST | NI_NUMERICSERV) )
         {
 #ifdef DAP_OS_WINDOWS
             _set_errno(WSAGetLastError());
 #endif
             log_it(L_ERROR, "getnameinfo() error %d: %s", errno, dap_strerror(errno));
+            closesocket(a_remote_socket);
             return;
-        } 
-        l_es_new->remote_port = strtol(l_port_str, NULL, 10);
-        debug_if(l_server->ext_log, L_INFO, "Connection accepted from %s : %hu, socket %"DAP_FORMAT_SOCKET,
-                                            l_es_new->remote_addr_str, l_es_new->remote_port, a_remote_socket);
+        }
+        if (( l_server->whitelist
+            ? !dap_str_find(l_server->whitelist, l_remote_addr_str) 
+            : !!dap_str_find(l_server->blacklist, l_remote_addr_str) )) {
+                closesocket(a_remote_socket);
+                return debug_if(l_server->ext_log, L_INFO, "Connection from %s : %s denied. Dump it",
+                                l_remote_addr_str, l_port_str);
+            }
+                
+        debug_if(l_server->ext_log, L_INFO, "Connection accepted from %s : %s, socket %"DAP_FORMAT_SOCKET,
+                                            l_remote_addr_str, l_port_str, a_remote_socket);
         int one = 1;
-        if ( setsockopt(l_es_new->socket, IPPROTO_TCP, TCP_NODELAY, (const char*)&one, sizeof(one)) < 0 )
+        if ( setsockopt(a_remote_socket, IPPROTO_TCP, TCP_NODELAY, (const char*)&one, sizeof(one)) < 0 )
             log_it(L_WARNING, "Can't disable Nagle alg, error %d: %s", errno, dap_strerror(errno));
         break;
     default:
-        log_it(L_ERROR, "Unsupported protocol family %hu from accept()", l_family);
-        break;
+        closesocket(a_remote_socket);
+        return log_it(L_ERROR, "Unsupported protocol family %hu from accept()", a_remote_addr->ss_family);
     }
+    l_es_new = dap_events_socket_wrap_no_add(a_remote_socket, &l_server->client_callbacks);
+    l_es_new->server = l_server;
+    l_es_new->type = l_es_type;
+    l_es_new->addr_storage = *a_remote_addr;
+    l_es_new->remote_port = strtol(l_port_str, NULL, 10);
+    dap_strncpy(l_es_new->remote_addr_str, l_remote_addr_str, INET6_ADDRSTRLEN);
     dap_worker_add_events_socket( dap_events_worker_get_auto(), l_es_new );
 }
 
diff --git a/io/include/dap_net.h b/io/include/dap_net.h
index 81d337fb7..0afab9c11 100644
--- a/io/include/dap_net.h
+++ b/io/include/dap_net.h
@@ -47,6 +47,8 @@
 #define DAP_CFG_PARAM_SOCK_PATH         "listen-path"
 #define DAP_CFG_PARAM_SOCK_PERMISSIONS  "listen-unix-socket-permissions"
 #define DAP_CFG_PARAM_LEGACY_PORT       "listen-port-tcp"
+#define DAP_CFG_PARAM_WHITE_LIST        "white-list"
+#define DAP_CFG_PARAM_BLACK_LIST        "black-list"
 
 typedef struct dap_link_info {
     dap_stream_node_addr_t node_addr;
diff --git a/io/include/dap_server.h b/io/include/dap_server.h
index 4b83316c3..3a2855e99 100644
--- a/io/include/dap_server.h
+++ b/io/include/dap_server.h
@@ -53,6 +53,7 @@ typedef struct dap_server {
     dap_server_callback_t delete_callback;
     dap_cpu_stats_t cpu_stats;
     dap_list_t *es_listeners;
+    const char **whitelist, **blacklist;
     void *_inheritor;
     bool ext_log;
 } dap_server_t;
diff --git a/net/server/cli_server/dap_cli_server.c b/net/server/cli_server/dap_cli_server.c
index 33c494703..b8b1fe6d6 100644
--- a/net/server/cli_server/dap_cli_server.c
+++ b/net/server/cli_server/dap_cli_server.c
@@ -67,15 +67,25 @@ typedef struct cli_cmd_arg {
     char *buf, status;
 } cli_cmd_arg_t;
 
-static bool s_cli_cmd_exec(void *a_arg);
-
-dap_cli_handler_cl_t *s_json_rpc_handler = NULL;
+static void* s_cli_cmd_exec(void *a_arg);
+
+static bool s_allowed_cmd_check(char *a_buf) {
+    enum json_tokener_error jterr;
+    const char *l_method;
+    json_object *jobj = json_tokener_parse_verbose(a_buf, &jterr),
+                *jobj_method = NULL;
+    if ( jterr != json_tokener_success ) 
+        return log_it(L_ERROR, "Can't parse json command, error %s", json_tokener_error_desc(jterr)), false;
+    if ( json_object_object_get_ex(jobj, "method", &jobj_method) )
+        l_method = json_object_get_string(jobj_method);
+    else {
+        log_it(L_ERROR, "Invalid command request, dump it");
+        json_object_put(jobj);
+        return false;
+    }
 
-void dap_json_rpc_cli_handler_add(const char *a_method, handler_func_cli_t* a_fund) {
-    dap_cli_handler_cl_t *l_handler = DAP_NEW(dap_cli_handler_cl_t);
-    l_handler->func = a_fund;
-    l_handler->method = a_method;
-    HASH_ADD_STR(s_json_rpc_handler, method, l_handler);
+    bool l_allowed = !!dap_str_find( dap_config_get_array_str(g_config, "cli-server", "allowed_cmd", NULL), l_method );
+    return debug_if(!l_allowed, L_ERROR, "Command %s is restricted", l_method), json_object_put(jobj), l_allowed;
 }
 
 DAP_STATIC_INLINE void s_cli_cmd_schedule(dap_events_socket_t *a_es, void *a_arg) {
@@ -108,14 +118,31 @@ DAP_STATIC_INLINE void s_cli_cmd_schedule(dap_events_socket_t *a_es, void *a_arg
         size_t l_hdr_len = (size_t)(l_arg->buf - (char*)a_es->buf_in);
         if ( a_es->buf_in_size < l_arg->buf_size + l_hdr_len )
             return;
-        l_arg->buf = DAP_DUP_SIZE(l_arg->buf, l_arg->buf_size);
+
+        if ( ((struct sockaddr_in*)&a_es->addr_storage)->sin_addr.s_addr != htonl(INADDR_LOOPBACK)
+#ifdef DAP_OS_UNIX
+            && a_es->addr_storage.ss_family != AF_UNIX
+#endif
+            && !s_allowed_cmd_check(l_arg->buf) ) {
+                dap_events_socket_write_f_unsafe(a_es, "HTTP/1.1 403 Forbidden\r\n");
+                a_es->flags |= DAP_SOCK_SIGNAL_CLOSE;
+                return;
+            }
+
+        l_arg->buf = strndup(l_arg->buf, l_arg->buf_size);
         l_arg->worker = a_es->worker;
         l_arg->es_uid = a_es->uuid;
-        dap_proc_thread_callback_add_pri(NULL, s_cli_cmd_exec, l_arg, DAP_QUEUE_MSG_PRIORITY_HIGH);
+
+        pthread_t l_tid;
+        pthread_attr_t attr;
+        pthread_attr_init(&attr);
+        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+        pthread_create(&l_tid, &attr, s_cli_cmd_exec, l_arg);
+
+        //dap_proc_thread_callback_add_pri(NULL, s_cli_cmd_exec, l_arg, DAP_QUEUE_MSG_PRIORITY_HIGH);
         a_es->buf_in_size = 0;
         a_es->callbacks.arg = NULL;
-        return;
-    }
+    } return;
     }
 
     dap_events_socket_write_f_unsafe(a_es, "HTTP/1.1 500 Internal Server Error\r\n");
@@ -198,19 +225,6 @@ static inline void s_cmd_add_ex(const char * a_name, dap_cli_server_cmd_callback
     log_it(L_DEBUG,"Added command %s",l_cmd_item->name);
 }
 
-int is_json_clear_method(const char *a_name) {
-    static const char *long_method[] = {
-            "j_tx_create"
-    };
-    for (size_t i = 0; i < sizeof(long_method)/sizeof(long_method[0]); i++) {
-        if (!strcmp(a_name, long_method[i])) {
-            return 1;
-        }
-    }
-    return 0;
-}
-
-
 int json_commands(const char * a_name) {
     static const char* long_cmd[] = {
             "tx_history",
@@ -230,6 +244,7 @@ int json_commands(const char * a_name) {
             "token",
             "esbocs",
             "global_db",
+            "net_srv",
             "net",
             "srv_stake",
             "voting",
@@ -247,6 +262,7 @@ int json_commands(const char * a_name) {
             "stats",
             "stake_lock",
             "exec_cmd",
+            "print_log",
             "srv_xchange",
             "file",
             "policy"
@@ -410,16 +426,17 @@ dap_cli_cmd_t *dap_cli_server_cmd_find_by_alias(const char *a_alias, char **a_ap
     return l_alias->standard_command;
 }
 
-static bool s_cli_cmd_exec(void *a_arg) {
+static void* s_cli_cmd_exec(void *a_arg) {
     cli_cmd_arg_t *l_arg = (cli_cmd_arg_t*)a_arg;
     char    *l_ret = dap_cli_cmd_exec(l_arg->buf),
             *l_full_ret = dap_strdup_printf("HTTP/1.1 200 OK\r\n"
                                             "Content-Length: %zu\r\n\r\n"
                                             "%s", dap_strlen(l_ret), l_ret);
+    DAP_DELETE(l_ret);
     dap_events_socket_write(l_arg->worker, l_arg->es_uid, l_full_ret, dap_strlen(l_full_ret));
-    // TODO: pagination and ouput optimizations
-    DAP_DEL_MULTY(l_ret, l_arg->buf, l_full_ret, l_arg);
-    return false;
+    // TODO: pagination and output optimizations
+    DAP_DEL_MULTY(l_arg->buf, l_full_ret, l_arg);
+    return NULL;
 }
 
 char *dap_cli_cmd_exec(char *a_req_str) {
@@ -429,122 +446,102 @@ char *dap_cli_cmd_exec(char *a_req_str) {
     int l_verbose = 0;
     // command is found
     char *cmd_name = request->method;
-    if (is_json_clear_method(cmd_name)) {
-        json_object *l_obj_ret = json_object_new_object();
-        dap_cli_handler_cl_t *l_handler = NULL;
-        HASH_FIND_STR(s_json_rpc_handler, cmd_name, l_handler);
-        if (!l_handler) {
-            dap_json_rpc_error_add(l_obj_ret, 1, "Can't find handler '%s' method", cmd_name);
-        } else {
-            dap_json_rpc_params_t *params = request->params;
-            if (params->length == 1){
-                json_object *l_obj_params = dap_json_rpc_params_get(params, 0);
-                l_handler->func(l_obj_params, l_obj_ret);
-            }
-        }
-        dap_json_rpc_response_t *l_response = dap_json_rpc_response_create(l_obj_ret,
-                                     TYPE_RESPONSE_JSON, request->id);
-        char *response_string = dap_json_rpc_response_to_string(l_response);
-        json_object_put(l_obj_ret);
-        dap_json_rpc_response_free(l_response);
-        dap_json_rpc_request_free(request);
-        return response_string;
-    } else {
-        dap_cli_cmd_t *l_cmd = dap_cli_server_cmd_find(cmd_name);
-        bool l_finded_by_alias = false;
-        char *l_append_cmd = NULL;
-        char *l_ncmd = NULL;
-        if (!l_cmd) {
-            l_cmd = dap_cli_server_cmd_find_by_alias(cmd_name, &l_append_cmd, &l_ncmd);
-            l_finded_by_alias = true;
-        }
-        dap_json_rpc_params_t *params = request->params;
-
-        char *str_cmd = dap_json_rpc_params_get(params, 0);
-        int res = -1;
-        char *str_reply = NULL;
-        json_object *l_json_arr_reply = json_object_new_array();
-        if (l_cmd) {
-            if (l_cmd->overrides.log_cmd_call)
-                l_cmd->overrides.log_cmd_call(str_cmd);
-            else {
-                char *l_str_cmd = dap_strdup(str_cmd);
-                char *l_ptr = strstr(l_str_cmd, "-password");
-                if (l_ptr) {
-                    l_ptr += 10;
-                    while (l_ptr[0] != '\0' && l_ptr[0] != ';') {
-                        *l_ptr = '*';
-                        l_ptr += 1;
-                    }
+    dap_cli_cmd_t *l_cmd = dap_cli_server_cmd_find(cmd_name);
+    bool l_finded_by_alias = false;
+    char *l_append_cmd = NULL;
+    char *l_ncmd = NULL;
+    if (!l_cmd) {
+        l_cmd = dap_cli_server_cmd_find_by_alias(cmd_name, &l_append_cmd, &l_ncmd);
+        l_finded_by_alias = true;
+    }
+    dap_json_rpc_params_t *params = request->params;
+
+    char *str_cmd = dap_json_rpc_params_get(params, 0);
+    if (!str_cmd)
+        str_cmd = cmd_name;
+    int res = -1;
+    char *str_reply = NULL;
+    json_object *l_json_arr_reply = json_object_new_array();
+    if (l_cmd) {
+        if (l_cmd->overrides.log_cmd_call)
+            l_cmd->overrides.log_cmd_call(str_cmd);
+        else {
+            char *l_str_cmd = dap_strdup(str_cmd);
+            char *l_ptr = strstr(l_str_cmd, "-password");
+            if (l_ptr) {
+                l_ptr += 10;
+                while (l_ptr[0] != '\0' && l_ptr[0] != ';') {
+                    *l_ptr = '*';
+                    l_ptr += 1;
                 }
-                debug_if(dap_config_get_item_bool_default(g_config, "cli-server", "debug-more", false),
-                         L_DEBUG, "execute command=%s", l_str_cmd);
-                DAP_DELETE(l_str_cmd);
             }
+            debug_if(dap_config_get_item_bool_default(g_config, "cli-server", "debug-more", false),
+                        L_DEBUG, "execute command=%s", l_str_cmd);
+            DAP_DELETE(l_str_cmd);
+        }
 
-            char **l_argv = dap_strsplit(str_cmd, ";", -1);
-            int argc = 0;
-            // Count argc
-            while (l_argv[argc] != NULL) argc++;
-            // Support alias
-            if (l_finded_by_alias) {
-                int l_argc = argc + 1;
-                char **al_argv = DAP_NEW_Z_COUNT(char*, l_argc + 1);
-                al_argv[0] = l_ncmd;
-                al_argv[1] = l_append_cmd;
-                for (int i = 1; i < argc; i++)
-                    al_argv[i + 1] = l_argv[i];
-                cmd_name = l_ncmd;
-                DAP_FREE(l_argv[0]);
-                DAP_DEL_Z(l_argv);
-                l_argv = al_argv;
-                argc = l_argc;
-            }
-            // Call the command function
-            if (l_cmd && l_argv && l_cmd->func) {
-                if (json_commands(cmd_name)) {
-                    res = l_cmd->func(argc, l_argv, (void *) &l_json_arr_reply);
-                } else if (l_cmd->arg_func) {
-                    res = l_cmd->func_ex(argc, l_argv, l_cmd->arg_func, (void *) &str_reply);
-                } else {
-                    res = l_cmd->func(argc, l_argv, (void *) &str_reply);
-                }
-            } else if (l_cmd) {
-                log_it(L_WARNING, "NULL arguments for input for command \"%s\"", str_cmd);
-                dap_json_rpc_error_add(l_json_arr_reply, -1, "NULL arguments for input for command \"%s\"", str_cmd);
+        char **l_argv = dap_strsplit(str_cmd, ";", -1);
+        int argc = 0;
+        // Count argc
+        while (l_argv[argc] != NULL) argc++;
+        // Support alias
+        if (l_finded_by_alias) {
+            int l_argc = argc + 1;
+            char **al_argv = DAP_NEW_Z_COUNT(char*, l_argc + 1);
+            al_argv[0] = l_ncmd;
+            al_argv[1] = l_append_cmd;
+            for (int i = 1; i < argc; i++)
+                al_argv[i + 1] = l_argv[i];
+            cmd_name = l_ncmd;
+            DAP_FREE(l_argv[0]);
+            DAP_DEL_Z(l_argv);
+            l_argv = al_argv;
+            argc = l_argc;
+        }
+        // Call the command function
+        if (l_cmd && l_argv && l_cmd->func) {
+            if (json_commands(cmd_name)) {
+                res = l_cmd->func(argc, l_argv, (void *) &l_json_arr_reply);
+            } else if (l_cmd->arg_func) {
+                res = l_cmd->func_ex(argc, l_argv, l_cmd->arg_func, (void *) &str_reply);
             } else {
-                log_it(L_WARNING, "No function for command \"%s\" but it registred?!", str_cmd);
-                dap_json_rpc_error_add(l_json_arr_reply, -1, "No function for command \"%s\" but it registred?!", str_cmd);
+                res = l_cmd->func(argc, l_argv, (void *) &str_reply);
             }
-            // find '-verbose' command
-            l_verbose = dap_cli_server_cmd_find_option_val(l_argv, 1, argc, "-verbose", NULL);
-            dap_strfreev(l_argv);
+        } else if (l_cmd) {
+            log_it(L_WARNING, "NULL arguments for input for command \"%s\"", str_cmd);
+            dap_json_rpc_error_add(l_json_arr_reply, -1, "NULL arguments for input for command \"%s\"", str_cmd);
         } else {
-            dap_json_rpc_error_add(l_json_arr_reply, -1, "can't recognize command=%s", str_cmd);
-            log_it(L_ERROR, "Reply string: \"%s\"", str_reply);
+            log_it(L_WARNING, "No function for command \"%s\" but it registred?!", str_cmd);
+            dap_json_rpc_error_add(l_json_arr_reply, -1, "No function for command \"%s\" but it registred?!", str_cmd);
         }
-        char *reply_body = NULL;
-        // -verbose
-        if (l_verbose) {
-            if (str_reply) {
-                reply_body = dap_strdup_printf("%d\r\nret_code: %d\r\n%s\r\n", res, res, str_reply);
-                DAP_DELETE(str_reply);
-            } else {
-                json_object *json_res = json_object_new_object();
-                json_object_object_add(json_res, "ret_code", json_object_new_int(res));
-                json_object_array_add(l_json_arr_reply, json_res);
-            }
-        } else
-            reply_body = str_reply;
-
-        // create response
-        dap_json_rpc_response_t *response = reply_body
-                ? dap_json_rpc_response_create(reply_body, TYPE_RESPONSE_STRING, request->id)
-                : dap_json_rpc_response_create(json_object_get(l_json_arr_reply), TYPE_RESPONSE_JSON, request->id);
-        json_object_put(l_json_arr_reply);
-        char *response_string = dap_json_rpc_response_to_string(response);
-        dap_json_rpc_response_free(response);
-        dap_json_rpc_request_free(request);
-        return response_string;
+        // find '-verbose' command
+        l_verbose = dap_cli_server_cmd_find_option_val(l_argv, 1, argc, "-verbose", NULL);
+        dap_strfreev(l_argv);
+    } else {
+        dap_json_rpc_error_add(l_json_arr_reply, -1, "can't recognize command=%s", str_cmd);
+        log_it(L_ERROR, "Reply string: \"%s\"", str_reply);
     }
+    char *reply_body = NULL;
+    // -verbose
+    if (l_verbose) {
+        if (str_reply) {
+            reply_body = dap_strdup_printf("%d\r\nret_code: %d\r\n%s\r\n", res, res, str_reply);
+            DAP_DELETE(str_reply);
+        } else {
+            json_object *json_res = json_object_new_object();
+            json_object_object_add(json_res, "ret_code", json_object_new_int(res));
+            json_object_array_add(l_json_arr_reply, json_res);
+        }
+    } else
+        reply_body = str_reply;
+
+    // create response
+    dap_json_rpc_response_t *response = reply_body
+            ? dap_json_rpc_response_create(reply_body, TYPE_RESPONSE_STRING, request->id)
+            : dap_json_rpc_response_create(json_object_get(l_json_arr_reply), TYPE_RESPONSE_JSON, request->id);
+    json_object_put(l_json_arr_reply);
+    char *response_string = dap_json_rpc_response_to_string(response);
+    dap_json_rpc_response_free(response);
+    dap_json_rpc_request_free(request);
+    return response_string;
 }
-- 
GitLab