]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
FS-10167: First pass of adding a test protocol
authorShane Bryldt <astaelan@gmail.com>
Wed, 21 Jun 2017 23:15:53 +0000 (17:15 -0600)
committerShane Bryldt <astaelan@gmail.com>
Wed, 21 Jun 2017 23:15:53 +0000 (17:15 -0600)
12 files changed:
libs/libblade/libblade.sln
libs/libblade/src/blade_stack.c
libs/libblade/src/include/blade_stack.h
libs/libblade/test/Makefile.am
libs/libblade/test/bladec.c
libs/libblade/test/blades.c
libs/libblade/test/testcli.c [new file with mode: 0644]
libs/libblade/test/testcli.cfg [new file with mode: 0644]
libs/libblade/test/testcli.vcxproj [new file with mode: 0644]
libs/libblade/test/testcon.c [new file with mode: 0644]
libs/libblade/test/testcon.cfg [new file with mode: 0644]
libs/libblade/test/testcon.vcxproj [new file with mode: 0644]

index 8b9d9cce67fa7831da6399aab64223839c0d9758..4eed1e76699f7900fadb3402ccfb69ce9e9feca2 100644 (file)
@@ -23,6 +23,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bladec", "test\bladec.vcxpr
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "switchblade", "switchblade\switchblade.vcxproj", "{8330E669-77F3-4F70-A275-6F7BABE050A7}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testcli", "test\testcli.vcxproj", "{CF89E839-AB50-4BBB-AC34-0D6232E1EBB5}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testcon", "test\testcon.vcxproj", "{D67EEF66-B323-4BCF-9E3C-3A640B9949B7}"
+EndProject
 Global
        GlobalSection(SolutionConfigurationPlatforms) = preSolution
                Debug|x64 = Debug|x64
@@ -195,6 +199,38 @@ Global
                {8330E669-77F3-4F70-A275-6F7BABE050A7}.ReleaseDLL|x64.Build.0 = Release|x64
                {8330E669-77F3-4F70-A275-6F7BABE050A7}.ReleaseDLL|x86.ActiveCfg = Release|Win32
                {8330E669-77F3-4F70-A275-6F7BABE050A7}.ReleaseDLL|x86.Build.0 = Release|Win32
+               {CF89E839-AB50-4BBB-AC34-0D6232E1EBB5}.Debug|x64.ActiveCfg = Debug|x64
+               {CF89E839-AB50-4BBB-AC34-0D6232E1EBB5}.Debug|x64.Build.0 = Debug|x64
+               {CF89E839-AB50-4BBB-AC34-0D6232E1EBB5}.Debug|x86.ActiveCfg = Debug|Win32
+               {CF89E839-AB50-4BBB-AC34-0D6232E1EBB5}.Debug|x86.Build.0 = Debug|Win32
+               {CF89E839-AB50-4BBB-AC34-0D6232E1EBB5}.DebugDLL|x64.ActiveCfg = Debug|x64
+               {CF89E839-AB50-4BBB-AC34-0D6232E1EBB5}.DebugDLL|x64.Build.0 = Debug|x64
+               {CF89E839-AB50-4BBB-AC34-0D6232E1EBB5}.DebugDLL|x86.ActiveCfg = Debug|Win32
+               {CF89E839-AB50-4BBB-AC34-0D6232E1EBB5}.DebugDLL|x86.Build.0 = Debug|Win32
+               {CF89E839-AB50-4BBB-AC34-0D6232E1EBB5}.Release|x64.ActiveCfg = Release|x64
+               {CF89E839-AB50-4BBB-AC34-0D6232E1EBB5}.Release|x64.Build.0 = Release|x64
+               {CF89E839-AB50-4BBB-AC34-0D6232E1EBB5}.Release|x86.ActiveCfg = Release|Win32
+               {CF89E839-AB50-4BBB-AC34-0D6232E1EBB5}.Release|x86.Build.0 = Release|Win32
+               {CF89E839-AB50-4BBB-AC34-0D6232E1EBB5}.ReleaseDLL|x64.ActiveCfg = Release|x64
+               {CF89E839-AB50-4BBB-AC34-0D6232E1EBB5}.ReleaseDLL|x64.Build.0 = Release|x64
+               {CF89E839-AB50-4BBB-AC34-0D6232E1EBB5}.ReleaseDLL|x86.ActiveCfg = Release|Win32
+               {CF89E839-AB50-4BBB-AC34-0D6232E1EBB5}.ReleaseDLL|x86.Build.0 = Release|Win32
+               {D67EEF66-B323-4BCF-9E3C-3A640B9949B7}.Debug|x64.ActiveCfg = Debug|x64
+               {D67EEF66-B323-4BCF-9E3C-3A640B9949B7}.Debug|x64.Build.0 = Debug|x64
+               {D67EEF66-B323-4BCF-9E3C-3A640B9949B7}.Debug|x86.ActiveCfg = Debug|Win32
+               {D67EEF66-B323-4BCF-9E3C-3A640B9949B7}.Debug|x86.Build.0 = Debug|Win32
+               {D67EEF66-B323-4BCF-9E3C-3A640B9949B7}.DebugDLL|x64.ActiveCfg = Debug|x64
+               {D67EEF66-B323-4BCF-9E3C-3A640B9949B7}.DebugDLL|x64.Build.0 = Debug|x64
+               {D67EEF66-B323-4BCF-9E3C-3A640B9949B7}.DebugDLL|x86.ActiveCfg = Debug|Win32
+               {D67EEF66-B323-4BCF-9E3C-3A640B9949B7}.DebugDLL|x86.Build.0 = Debug|Win32
+               {D67EEF66-B323-4BCF-9E3C-3A640B9949B7}.Release|x64.ActiveCfg = Release|x64
+               {D67EEF66-B323-4BCF-9E3C-3A640B9949B7}.Release|x64.Build.0 = Release|x64
+               {D67EEF66-B323-4BCF-9E3C-3A640B9949B7}.Release|x86.ActiveCfg = Release|Win32
+               {D67EEF66-B323-4BCF-9E3C-3A640B9949B7}.Release|x86.Build.0 = Release|Win32
+               {D67EEF66-B323-4BCF-9E3C-3A640B9949B7}.ReleaseDLL|x64.ActiveCfg = Release|x64
+               {D67EEF66-B323-4BCF-9E3C-3A640B9949B7}.ReleaseDLL|x64.Build.0 = Release|x64
+               {D67EEF66-B323-4BCF-9E3C-3A640B9949B7}.ReleaseDLL|x86.ActiveCfg = Release|Win32
+               {D67EEF66-B323-4BCF-9E3C-3A640B9949B7}.ReleaseDLL|x86.Build.0 = Release|Win32
        EndGlobalSection
        GlobalSection(SolutionProperties) = preSolution
                HideSolutionNode = FALSE
index 38c20290458133a979d6be02c12cee0342c3948d..28e92ae9bf3cfd145e8e52464eae755276f88527 100644 (file)
@@ -1557,6 +1557,9 @@ done:
 
 
 // blade.locate request generator
+// @todo discuss system to support caching locate results, and internally subscribing to receive event updates related to protocols which have been located
+// to ensure local caches remain synced when protocol providers change, but this requires additional filters for event propagating to avoid broadcasting
+// every protocol update to everyone which may actually be a better way than an explicit locate request
 KS_DECLARE(ks_status_t) blade_protocol_locate(blade_handle_t *bh, const char *name, const char *realm, blade_rpc_response_callback_t callback, void *data)
 {
        ks_status_t ret = KS_STATUS_SUCCESS;
@@ -1878,6 +1881,40 @@ done:
        return ret;
 }
 
+KS_DECLARE(const char *) blade_protocol_execute_request_requester_nodeid_get(blade_rpc_request_t *brpcreq)
+{
+       cJSON *req = NULL;
+       cJSON *req_params = NULL;
+       const char *req_requester_nodeid = NULL;
+
+       ks_assert(brpcreq);
+
+       req = blade_rpc_request_message_get(brpcreq);
+       ks_assert(req);
+
+       req_params = cJSON_GetObjectItem(req, "params");
+       if (req_params) req_requester_nodeid = cJSON_GetObjectCstr(req_params, "requester-nodeid");
+
+       return req_requester_nodeid;
+}
+
+KS_DECLARE(const char *) blade_protocol_execute_request_responder_nodeid_get(blade_rpc_request_t *brpcreq)
+{
+       cJSON *req = NULL;
+       cJSON *req_params = NULL;
+       const char *req_responder_nodeid = NULL;
+
+       ks_assert(brpcreq);
+
+       req = blade_rpc_request_message_get(brpcreq);
+       ks_assert(req);
+
+       req_params = cJSON_GetObjectItem(req, "params");
+       if (req_params) req_responder_nodeid = cJSON_GetObjectCstr(req_params, "responder-nodeid");
+
+       return req_responder_nodeid;
+}
+
 KS_DECLARE(cJSON *) blade_protocol_execute_request_params_get(blade_rpc_request_t *brpcreq)
 {
        cJSON *req = NULL;
@@ -2147,7 +2184,7 @@ done:
 
 
 // blade.broadcast request generator
-KS_DECLARE(ks_status_t) blade_protocol_broadcast(blade_handle_t *bh, const char *event, const char *protocol, const char *realm, cJSON *params, blade_rpc_response_callback_t callback, void *data)
+KS_DECLARE(ks_status_t) blade_protocol_broadcast(blade_handle_t *bh, const char *broadcaster_nodeid, const char *event, const char *protocol, const char *realm, cJSON *params, blade_rpc_response_callback_t callback, void *data)
 {
        ks_status_t ret = KS_STATUS_SUCCESS;
 
@@ -2157,7 +2194,10 @@ KS_DECLARE(ks_status_t) blade_protocol_broadcast(blade_handle_t *bh, const char
        ks_assert(realm);
 
        // this will ensure any downstream subscriber sessions, and upstream session if available will be broadcasted to
-       ret = blade_protocol_broadcast_raw(bh, NULL, event, protocol, realm, params, callback, data);
+       ks_rwl_read_lock(bh->local_nodeid_rwl);
+       if (!broadcaster_nodeid) broadcaster_nodeid = bh->local_nodeid;
+       ret = blade_protocol_broadcast_raw(bh, broadcaster_nodeid, NULL, event, protocol, realm, params, callback, data);
+       ks_rwl_read_unlock(bh->local_nodeid_rwl);
 
        // @todo must check if the local node is also subscribed to receive the event, this is a special edge case which has some extra considerations
        // if the local node is subscribed to receive the event, it should be received here as a special case, otherwise the broadcast request handler
@@ -2166,13 +2206,14 @@ KS_DECLARE(ks_status_t) blade_protocol_broadcast(blade_handle_t *bh, const char
        return ret;
 }
 
-KS_DECLARE(ks_status_t) blade_protocol_broadcast_raw(blade_handle_t *bh, const char *excluded_nodeid, const char *event, const char *protocol, const char *realm, cJSON *params, blade_rpc_response_callback_t callback, void *data)
+KS_DECLARE(ks_status_t) blade_protocol_broadcast_raw(blade_handle_t *bh, const char *broadcaster_nodeid, const char *excluded_nodeid, const char *event, const char *protocol, const char *realm, cJSON *params, blade_rpc_response_callback_t callback, void *data)
 {
        const char *bsub_key = NULL;
        blade_subscription_t *bsub = NULL;
        blade_session_t *bs = NULL;
 
        ks_assert(bh);
+       ks_assert(broadcaster_nodeid);
        ks_assert(event);
        ks_assert(protocol);
        ks_assert(realm);
@@ -2205,6 +2246,7 @@ KS_DECLARE(ks_status_t) blade_protocol_broadcast_raw(blade_handle_t *bh, const c
 
                                blade_rpc_request_raw_create(bh->pool, &req, &req_params, NULL, "blade.broadcast");
 
+                               cJSON_AddStringToObject(req_params, "broadcaster-nodeid", broadcaster_nodeid);
                                cJSON_AddStringToObject(req_params, "event", event);
                                cJSON_AddStringToObject(req_params, "protocol", protocol);
                                cJSON_AddStringToObject(req_params, "realm", realm);
@@ -2234,6 +2276,7 @@ KS_DECLARE(ks_status_t) blade_protocol_broadcast_raw(blade_handle_t *bh, const c
 
                        blade_rpc_request_raw_create(bh->pool, &req, &req_params, NULL, "blade.broadcast");
 
+                       cJSON_AddStringToObject(req_params, "broadcaster-nodeid", broadcaster_nodeid);
                        cJSON_AddStringToObject(req_params, "event", event);
                        cJSON_AddStringToObject(req_params, "protocol", protocol);
                        cJSON_AddStringToObject(req_params, "realm", realm);
@@ -2258,6 +2301,7 @@ ks_bool_t blade_protocol_broadcast_request_handler(blade_rpc_request_t *brpcreq,
        blade_session_t *bs = NULL;
        cJSON *req = NULL;
        cJSON *req_params = NULL;
+       const char *req_params_broadcaster_nodeid = NULL;
        const char *req_params_event = NULL;
        const char *req_params_protocol = NULL;
        const char *req_params_realm = NULL;
@@ -2287,6 +2331,14 @@ ks_bool_t blade_protocol_broadcast_request_handler(blade_rpc_request_t *brpcreq,
                goto done;
        }
 
+       req_params_broadcaster_nodeid = cJSON_GetObjectCstr(req_params, "broadcaster-nodeid");
+       if (!req_params_broadcaster_nodeid) {
+               ks_log(KS_LOG_DEBUG, "Session (%s) broadcast request missing 'broadcaster-nodeid'\n", blade_session_id_get(bs));
+               blade_rpc_error_raw_create(&res, NULL, blade_rpc_request_messageid_get(brpcreq), -32602, "Missing params broadcaster-nodeid");
+               blade_session_send(bs, res, NULL, NULL);
+               goto done;
+       }
+
        req_params_event = cJSON_GetObjectCstr(req_params, "event");
        if (!req_params_event) {
                ks_log(KS_LOG_DEBUG, "Session (%s) broadcast request missing 'event'\n", blade_session_id_get(bs));
@@ -2314,7 +2366,7 @@ ks_bool_t blade_protocol_broadcast_request_handler(blade_rpc_request_t *brpcreq,
        req_params_params = cJSON_GetObjectItem(req_params, "params");
 
 
-       blade_protocol_broadcast_raw(bh, blade_session_id_get(bs), req_params_event, req_params_protocol, req_params_realm, req_params_params, NULL, NULL);
+       blade_protocol_broadcast_raw(bh, req_params_broadcaster_nodeid, blade_session_id_get(bs), req_params_event, req_params_protocol, req_params_realm, req_params_params, NULL, NULL);
 
 
        bsub_key = ks_psprintf(bh->pool, "%s@%s/%s", req_params_protocol, req_params_realm, req_params_event);
@@ -2338,6 +2390,7 @@ ks_bool_t blade_protocol_broadcast_request_handler(blade_rpc_request_t *brpcreq,
        // build the actual response finally
        blade_rpc_response_raw_create(&res, &res_result, blade_rpc_request_messageid_get(brpcreq));
 
+       cJSON_AddStringToObject(res_result, "broadcaster-nodeid", req_params_broadcaster_nodeid);
        cJSON_AddStringToObject(res_result, "event", req_params_event);
        cJSON_AddStringToObject(res_result, "protocol", req_params_protocol);
        cJSON_AddStringToObject(res_result, "realm", req_params_realm);
@@ -2354,6 +2407,23 @@ done:
        return ret;
 }
 
+KS_DECLARE(const char *) blade_protocol_broadcast_request_broadcaster_nodeid_get(blade_rpc_request_t *brpcreq)
+{
+       cJSON *req = NULL;
+       cJSON *req_params = NULL;
+       const char *req_broadcaster_nodeid = NULL;
+
+       ks_assert(brpcreq);
+
+       req = blade_rpc_request_message_get(brpcreq);
+       ks_assert(req);
+
+       req_params = cJSON_GetObjectItem(req, "params");
+       if (req_params) req_broadcaster_nodeid = cJSON_GetObjectCstr(req_params, "broadcaster-nodeid");
+
+       return req_broadcaster_nodeid;
+}
+
 KS_DECLARE(cJSON *) blade_protocol_broadcast_request_params_get(blade_rpc_request_t *brpcreq)
 {
        cJSON *req = NULL;
index 9ab945612ea586a5919a542222e34d2fc62a1ff7..4b33cea51c174b738b2ae437f13d656338bdf3b3 100644 (file)
@@ -105,6 +105,8 @@ KS_DECLARE(ks_status_t) blade_protocol_publish(blade_handle_t *bh, const char *n
 KS_DECLARE(ks_status_t) blade_protocol_locate(blade_handle_t *bh, const char *name, const char *realm, blade_rpc_response_callback_t callback, void *data);
 
 KS_DECLARE(ks_status_t) blade_protocol_execute(blade_handle_t *bh, const char *nodeid, const char *method, const char *protocol, const char *realm, cJSON *params, blade_rpc_response_callback_t callback, void *data);
+KS_DECLARE(const char *) blade_protocol_execute_request_requester_nodeid_get(blade_rpc_request_t *brpcreq);
+KS_DECLARE(const char *) blade_protocol_execute_request_responder_nodeid_get(blade_rpc_request_t *brpcreq);
 KS_DECLARE(cJSON *) blade_protocol_execute_request_params_get(blade_rpc_request_t *brpcreq);
 KS_DECLARE(cJSON *) blade_protocol_execute_response_result_get(blade_rpc_response_t *brpcres);
 KS_DECLARE(void) blade_protocol_execute_response_send(blade_rpc_request_t *brpcreq, cJSON *result);
@@ -112,8 +114,9 @@ KS_DECLARE(void) blade_protocol_execute_response_send(blade_rpc_request_t *brpcr
 KS_DECLARE(ks_status_t) blade_protocol_subscribe(blade_handle_t *bh, const char *event, const char *protocol, const char *realm, ks_bool_t remove, blade_rpc_response_callback_t callback, void *data, blade_rpc_request_callback_t event_callback, void *event_data);
 KS_DECLARE(ks_status_t) blade_protocol_subscribe_raw(blade_handle_t *bh, const char *event, const char *protocol, const char *realm, ks_bool_t remove, blade_rpc_response_callback_t callback, void *data);
 
-KS_DECLARE(ks_status_t) blade_protocol_broadcast(blade_handle_t *bh, const char *event, const char *protocol, const char *realm, cJSON *params, blade_rpc_response_callback_t callback, void *data);
-KS_DECLARE(ks_status_t) blade_protocol_broadcast_raw(blade_handle_t *bh, const char *excluded_nodeid, const char *event, const char *protocol, const char *realm, cJSON *params, blade_rpc_response_callback_t callback, void *data);
+KS_DECLARE(ks_status_t) blade_protocol_broadcast(blade_handle_t *bh, const char *broadcaster_nodeid, const char *event, const char *protocol, const char *realm, cJSON *params, blade_rpc_response_callback_t callback, void *data);
+KS_DECLARE(ks_status_t) blade_protocol_broadcast_raw(blade_handle_t *bh, const char *broadcaster_nodeid, const char *excluded_nodeid, const char *event, const char *protocol, const char *realm, cJSON *params, blade_rpc_response_callback_t callback, void *data);
+KS_DECLARE(const char *) blade_protocol_broadcast_request_broadcaster_nodeid_get(blade_rpc_request_t *brpcreq);
 KS_DECLARE(cJSON *) blade_protocol_broadcast_request_params_get(blade_rpc_request_t *brpcreq);
 
 KS_END_EXTERN_C
index 9d4f9f6b50e13113338eda1ce2b50b58434d04c7..1884b5847e75fb5d232c71d009832cb1697004a5 100644 (file)
@@ -18,6 +18,16 @@ blades_SOURCES = blades.c tap.c
 blades_CFLAGS = $(AM_CFLAGS)
 blades_LDADD = $(TEST_LDADD)
 
+check_PROGRAMS += testcli
+testcli_SOURCES = testcli.c tap.c
+testcli_CFLAGS = $(AM_CFLAGS)
+testcli_LDADD = $(TEST_LDADD)
+
+check_PROGRAMS += testcon
+testcon_SOURCES = testcon.c tap.c
+testcon_CFLAGS = $(AM_CFLAGS)
+testcon_LDADD = $(TEST_LDADD)
+
 #check_PROGRAMS += testdht2
 #testdht2_SOURCES = testdht2.c tap.c
 #testdht2_CFLAGS = $(AM_CFLAGS)
index 2858dc0946d6183c7c52a7cd64db319e45a5e741..e1a34a6b83d10911e5c84e3181a120dd258d1871 100644 (file)
@@ -201,9 +201,7 @@ int main(int argc, char **argv)
 
                blade_identity_destroy(&target);
 
-               ks_sleep_ms(5000);
-
-
+               ks_sleep_ms(3000);
        }
        
        loop(bh);
index 15a323f23ca1fac2390f2610b446698cb37a3a33..bc3fc6f09e4da8ed35ac185ff15bc0964f952201 100644 (file)
@@ -16,10 +16,12 @@ struct command_def_s {
 };
 
 void command_quit(blade_handle_t *bh, char *args);
+void command_publish(blade_handle_t *bh, char *args);
 void command_broadcast(blade_handle_t *bh, char *args);
 
 static const struct command_def_s command_defs[] = {
        { "quit", command_quit },
+       { "publish", command_publish },
        { "broadcast", command_broadcast },
 
        { NULL, NULL }
@@ -144,7 +146,6 @@ int main(int argc, char **argv)
        if (autoconnect) {
                blade_connection_t *bc = NULL;
                blade_identity_t *target = NULL;
-               blade_rpc_t *brpc = NULL;
 
                blade_identity_create(&target, blade_handle_pool_get(bh));
 
@@ -152,13 +153,7 @@ int main(int argc, char **argv)
 
                blade_identity_destroy(&target);
 
-               ks_sleep_ms(5000); // @todo use session state change callback to know when the session is ready, this hack temporarily ensures it's ready before trying to publish upstream
-
-               blade_rpc_create(&brpc, bh, "test.echo", "test", "mydomain.com", test_echo_request_handler, NULL);
-               blade_handle_protocolrpc_register(brpc);
-
-               // @todo build up json-based method schema for each protocolrpc registered above, and pass into blade_protocol_publish() to attach to the request, to be stored in the blade_protocol_t tracked by the master node
-               blade_protocol_publish(bh, "test", "mydomain.com", blade_publish_response_handler, NULL);
+               ks_sleep_ms(3000); // @todo use session state change callback to know when the session is ready, this hack temporarily ensures it's ready before trying to publish upstream
        }
 
        loop(bh);
@@ -237,12 +232,26 @@ void command_quit(blade_handle_t *bh, char *args)
        g_shutdown = KS_TRUE;
 }
 
+void command_publish(blade_handle_t *bh, char *args)
+{
+       blade_rpc_t *brpc = NULL;
+
+       ks_assert(bh);
+       ks_assert(args);
+
+       blade_rpc_create(&brpc, bh, "test.echo", "test", "mydomain.com", test_echo_request_handler, NULL);
+       blade_handle_protocolrpc_register(brpc);
+
+       // @todo build up json-based method schema for each protocolrpc registered above, and pass into blade_protocol_publish() to attach to the request, to be stored in the blade_protocol_t tracked by the master node
+       blade_protocol_publish(bh, "test", "mydomain.com", blade_publish_response_handler, NULL);
+}
+
 void command_broadcast(blade_handle_t *bh, char *args)
 {
        ks_assert(bh);
        ks_assert(args);
 
-       blade_protocol_broadcast(bh, "test.event", "test", "mydomain.com", NULL, test_event_response_handler, NULL);
+       blade_protocol_broadcast(bh, NULL, "test.event", "test", "mydomain.com", NULL, test_event_response_handler, NULL);
 }
 
 
diff --git a/libs/libblade/test/testcli.c b/libs/libblade/test/testcli.c
new file mode 100644 (file)
index 0000000..60db6dc
--- /dev/null
@@ -0,0 +1,462 @@
+#include "blade.h"
+#include "tap.h"
+
+#define CONSOLE_INPUT_MAX 512
+
+ks_bool_t g_shutdown = KS_FALSE;
+
+void loop(blade_handle_t *bh);
+void process_console_input(blade_handle_t *bh, char *line);
+
+typedef void (*command_callback)(blade_handle_t *bh, char *args);
+
+struct command_def_s {
+       const char *cmd;
+       command_callback callback;
+};
+
+void command_quit(blade_handle_t *bh, char *args);
+void command_locate(blade_handle_t *bh, char *args);
+void command_join(blade_handle_t *bh, char *args);
+void command_leave(blade_handle_t *bh, char *args);
+void command_talk(blade_handle_t *bh, char *args);
+
+static const struct command_def_s command_defs[] = {
+       { "quit", command_quit },
+       { "locate", command_locate },
+       { "join", command_join },
+       { "leave", command_leave },
+       { "talk", command_talk },
+
+       { NULL, NULL }
+};
+
+const char *g_testcon_nodeid = NULL;
+
+ks_bool_t test_locate_response_handler(blade_rpc_response_t *brpcres, void *data)
+{
+       blade_handle_t *bh = NULL;
+       blade_session_t *bs = NULL;
+       const char *nodeid = NULL;
+       cJSON *res = NULL;
+       cJSON *res_result = NULL;
+       cJSON *res_result_providers = NULL;
+       const char *res_result_protocol = NULL;
+       const char *res_result_realm = NULL;
+       //cJSON *params = NULL;
+
+       ks_assert(brpcres);
+
+       bh = blade_rpc_response_handle_get(brpcres);
+       ks_assert(bh);
+
+       bs = blade_handle_sessions_lookup(bh, blade_rpc_response_sessionid_get(brpcres));
+       ks_assert(bs);
+
+       res = blade_rpc_response_message_get(brpcres);
+       ks_assert(res);
+
+       res_result = cJSON_GetObjectItem(res, "result");
+       ks_assert(res_result);
+
+       res_result_protocol = cJSON_GetObjectCstr(res_result, "protocol");
+       ks_assert(res_result_protocol);
+
+       res_result_realm = cJSON_GetObjectCstr(res_result, "realm");
+       ks_assert(res_result_realm);
+
+       res_result_providers = cJSON_GetObjectItem(res_result, "providers");
+       ks_assert(res_result_providers);
+
+       ks_log(KS_LOG_DEBUG, "Session (%s) locate (%s@%s) response processing\n", blade_session_id_get(bs), res_result_protocol, res_result_realm);
+
+       for (int index = 0; index < cJSON_GetArraySize(res_result_providers); ++index) {
+               cJSON *elem = cJSON_GetArrayItem(res_result_providers, index);
+               if (elem->type == cJSON_String) {
+                       nodeid = elem->valuestring;
+               }
+       }
+
+       blade_session_read_unlock(bs);
+
+       if (nodeid) {
+               g_testcon_nodeid = ks_pstrdup(blade_handle_pool_get(bh), nodeid);
+       }
+       ks_log(KS_LOG_DEBUG, "Session (%s) locate (%s@%s) provider (%s)\n", blade_session_id_get(bs), res_result_protocol, res_result_realm, g_testcon_nodeid);
+
+       return KS_FALSE;
+}
+
+ks_bool_t test_join_response_handler(blade_rpc_response_t *brpcres, void *data)
+{
+       blade_handle_t *bh = NULL;
+       blade_session_t *bs = NULL;
+       cJSON *result = NULL;
+
+       ks_assert(brpcres);
+
+       bh = blade_rpc_response_handle_get(brpcres);
+       ks_assert(bh);
+
+       bs = blade_handle_sessions_lookup(bh, blade_rpc_response_sessionid_get(brpcres));
+       ks_assert(bs);
+
+       result = blade_protocol_execute_response_result_get(brpcres);
+       ks_assert(result);
+
+       ks_log(KS_LOG_DEBUG, "Session (%s) test.join response processing\n", blade_session_id_get(bs));
+
+       blade_session_read_unlock(bs);
+
+       return KS_FALSE;
+}
+
+ks_bool_t test_leave_response_handler(blade_rpc_response_t *brpcres, void *data)
+{
+       blade_handle_t *bh = NULL;
+       blade_session_t *bs = NULL;
+       cJSON *result = NULL;
+
+       ks_assert(brpcres);
+
+       bh = blade_rpc_response_handle_get(brpcres);
+       ks_assert(bh);
+
+       bs = blade_handle_sessions_lookup(bh, blade_rpc_response_sessionid_get(brpcres));
+       ks_assert(bs);
+
+       result = blade_protocol_execute_response_result_get(brpcres);
+       ks_assert(result);
+
+       ks_log(KS_LOG_DEBUG, "Session (%s) test.leave response processing\n", blade_session_id_get(bs));
+
+       blade_session_read_unlock(bs);
+
+       return KS_FALSE;
+}
+
+ks_bool_t test_talk_response_handler(blade_rpc_response_t *brpcres, void *data)
+{
+       blade_handle_t *bh = NULL;
+       blade_session_t *bs = NULL;
+       cJSON *result = NULL;
+
+       ks_assert(brpcres);
+
+       bh = blade_rpc_response_handle_get(brpcres);
+       ks_assert(bh);
+
+       bs = blade_handle_sessions_lookup(bh, blade_rpc_response_sessionid_get(brpcres));
+       ks_assert(bs);
+
+       result = blade_protocol_execute_response_result_get(brpcres);
+       ks_assert(result);
+
+       ks_log(KS_LOG_DEBUG, "Session (%s) test.talk response processing\n", blade_session_id_get(bs));
+
+       blade_session_read_unlock(bs);
+
+       return KS_FALSE;
+}
+
+ks_bool_t test_join_broadcast_handler(blade_rpc_request_t *brpcreq, void *data)
+{
+       blade_handle_t *bh = NULL;
+       blade_session_t *bs = NULL;
+       const char *broadcaster_nodeid = NULL;
+       cJSON *params = NULL;
+       cJSON *result = NULL;
+
+       ks_assert(brpcreq);
+
+       bh = blade_rpc_request_handle_get(brpcreq);
+       ks_assert(bh);
+
+       bs = blade_handle_sessions_lookup(bh, blade_rpc_request_sessionid_get(brpcreq));
+       ks_assert(bs);
+
+       params = blade_protocol_broadcast_request_params_get(brpcreq);
+       ks_assert(params);
+
+       broadcaster_nodeid = blade_protocol_broadcast_request_broadcaster_nodeid_get(brpcreq);
+       ks_assert(broadcaster_nodeid);
+
+       ks_log(KS_LOG_DEBUG, "Session (%s) test.join (%s) broadcast processing\n", blade_session_id_get(bs), broadcaster_nodeid);
+
+       blade_session_read_unlock(bs);
+
+       return KS_FALSE;
+}
+
+ks_bool_t test_leave_broadcast_handler(blade_rpc_request_t *brpcreq, void *data)
+{
+       blade_handle_t *bh = NULL;
+       blade_session_t *bs = NULL;
+       const char *broadcaster_nodeid = NULL;
+       cJSON *params = NULL;
+       cJSON *result = NULL;
+
+       ks_assert(brpcreq);
+
+       bh = blade_rpc_request_handle_get(brpcreq);
+       ks_assert(bh);
+
+       bs = blade_handle_sessions_lookup(bh, blade_rpc_request_sessionid_get(brpcreq));
+       ks_assert(bs);
+
+       params = blade_protocol_broadcast_request_params_get(brpcreq);
+       ks_assert(params);
+
+       broadcaster_nodeid = blade_protocol_broadcast_request_broadcaster_nodeid_get(brpcreq);
+       ks_assert(broadcaster_nodeid);
+
+       ks_log(KS_LOG_DEBUG, "Session (%s) test.leave (%s) broadcast processing\n", blade_session_id_get(bs), broadcaster_nodeid);
+
+       blade_session_read_unlock(bs);
+
+       return KS_FALSE;
+}
+
+ks_bool_t test_talk_broadcast_handler(blade_rpc_request_t *brpcreq, void *data)
+{
+       blade_handle_t *bh = NULL;
+       blade_session_t *bs = NULL;
+       const char *broadcaster_nodeid = NULL;
+       cJSON *params = NULL;
+       cJSON *result = NULL;
+
+       ks_assert(brpcreq);
+
+       bh = blade_rpc_request_handle_get(brpcreq);
+       ks_assert(bh);
+
+       bs = blade_handle_sessions_lookup(bh, blade_rpc_request_sessionid_get(brpcreq));
+       ks_assert(bs);
+
+       broadcaster_nodeid = blade_protocol_broadcast_request_broadcaster_nodeid_get(brpcreq);
+       ks_assert(broadcaster_nodeid);
+
+       params = blade_protocol_broadcast_request_params_get(brpcreq);
+       ks_assert(params);
+
+       // @todo pull out text from params
+
+       ks_log(KS_LOG_DEBUG, "Session (%s) test.talk (%s) broadcast processing\n", blade_session_id_get(bs), broadcaster_nodeid);
+
+       blade_session_read_unlock(bs);
+
+       return KS_FALSE;
+}
+
+
+int main(int argc, char **argv)
+{
+       blade_handle_t *bh = NULL;
+       config_t config;
+       config_setting_t *config_blade = NULL;
+       const char *cfgpath = "testcli.cfg";
+       //const char *session_state_callback_id = NULL;
+       const char *autoconnect = NULL;
+
+       ks_global_set_default_logger(KS_LOG_LEVEL_DEBUG);
+
+       blade_init();
+
+       blade_handle_create(&bh);
+
+       //if (argc > 1) cfgpath = argv[1];
+       if (argc > 1) autoconnect = argv[1];
+
+       config_init(&config);
+       if (!config_read_file(&config, cfgpath)) {
+               ks_log(KS_LOG_ERROR, "%s:%d - %s\n", config_error_file(&config), config_error_line(&config), config_error_text(&config));
+               config_destroy(&config);
+               return EXIT_FAILURE;
+       }
+       config_blade = config_lookup(&config, "blade");
+       if (!config_blade) {
+               ks_log(KS_LOG_ERROR, "Missing 'blade' config group\n");
+               config_destroy(&config);
+               return EXIT_FAILURE;
+       }
+       if (config_setting_type(config_blade) != CONFIG_TYPE_GROUP) {
+               ks_log(KS_LOG_ERROR, "The 'blade' config setting is not a group\n");
+               return EXIT_FAILURE;
+       }
+
+       if (blade_handle_startup(bh, config_blade) != KS_STATUS_SUCCESS) {
+               ks_log(KS_LOG_ERROR, "Blade startup failed\n");
+               return EXIT_FAILURE;
+       }
+
+       if (autoconnect) {
+               blade_connection_t *bc = NULL;
+               blade_identity_t *target = NULL;
+               ks_bool_t connected = KS_FALSE;
+
+               blade_identity_create(&target, blade_handle_pool_get(bh));
+
+               if (blade_identity_parse(target, autoconnect) == KS_STATUS_SUCCESS) connected = blade_handle_connect(bh, &bc, target, NULL) == KS_STATUS_SUCCESS;
+
+               blade_identity_destroy(&target);
+
+               ks_sleep_ms(3000);
+       }
+       
+       loop(bh);
+
+       blade_handle_destroy(&bh);
+
+       config_destroy(&config);
+
+       blade_shutdown();
+
+       return 0;
+}
+
+void loop(blade_handle_t *bh)
+{
+       char buf[CONSOLE_INPUT_MAX];
+       while (!g_shutdown) {
+               if (!fgets(buf, CONSOLE_INPUT_MAX, stdin)) break;
+
+               for (int index = 0; buf[index]; ++index) {
+                       if (buf[index] == '\r' || buf[index] == '\n') {
+                               buf[index] = '\0';
+                               break;
+                       }
+               }
+               process_console_input(bh, buf);
+       }
+}
+
+void parse_argument(char **input, char **arg, char terminator)
+{
+       char *tmp;
+
+       ks_assert(input);
+       ks_assert(*input);
+       ks_assert(arg);
+
+       tmp = *input;
+       *arg = tmp;
+
+       while (*tmp && *tmp != terminator) ++tmp;
+       if (*tmp == terminator) {
+               *tmp = '\0';
+               ++tmp;
+       }
+       *input = tmp;
+}
+
+void process_console_input(blade_handle_t *bh, char *line)
+{
+       char *args = line;
+       char *cmd = NULL;
+       ks_bool_t found = KS_FALSE;
+
+       parse_argument(&args, &cmd, ' ');
+
+       ks_log(KS_LOG_DEBUG, "Command: %s, Args: %s\n", cmd, args);
+
+       for (int32_t index = 0; command_defs[index].cmd; ++index) {
+               if (!strcmp(command_defs[index].cmd, cmd)) {
+                       found = KS_TRUE;
+                       command_defs[index].callback(bh, args);
+               }
+       }
+       if (!found) ks_log(KS_LOG_INFO, "Command '%s' unknown.\n", cmd);
+}
+
+void command_quit(blade_handle_t *bh, char *args)
+{
+       //ks_assert(bh);
+       ks_assert(args);
+
+       g_shutdown = KS_TRUE;
+}
+
+void command_locate(blade_handle_t *bh, char *args)
+{
+       ks_assert(bh);
+       ks_assert(args);
+
+       blade_protocol_locate(bh, "test", "mydomain.com", test_locate_response_handler, NULL);
+}
+
+void command_join(blade_handle_t *bh, char *args)
+{
+       cJSON *params = NULL;
+
+       ks_assert(bh);
+       ks_assert(args);
+
+       if (!g_testcon_nodeid) {
+               ks_log(KS_LOG_DEBUG, "Protocol controller has not been located\n");
+               return;
+       }
+
+
+       params = cJSON_CreateObject();
+
+       blade_protocol_execute(bh, g_testcon_nodeid, "test.join", "test", "mydomain.com", params, test_join_response_handler, NULL);
+
+       blade_protocol_subscribe(bh, "test.join", "test", "mydomain.com", KS_FALSE, NULL, NULL, test_join_broadcast_handler, NULL);
+       blade_protocol_subscribe(bh, "test.leave", "test", "mydomain.com", KS_FALSE, NULL, NULL, test_leave_broadcast_handler, NULL);
+       blade_protocol_subscribe(bh, "test.talk", "test", "mydomain.com", KS_FALSE, NULL, NULL, test_talk_broadcast_handler, NULL);
+}
+
+void command_leave(blade_handle_t *bh, char *args)
+{
+       cJSON *params = NULL;
+
+       ks_assert(bh);
+       ks_assert(args);
+
+       if (!g_testcon_nodeid) {
+               ks_log(KS_LOG_DEBUG, "Protocol controller has not been located\n");
+               return;
+       }
+
+       params = cJSON_CreateObject();
+
+       blade_protocol_execute(bh, g_testcon_nodeid, "test.leave", "test", "mydomain.com", params, test_leave_response_handler, NULL);
+
+       blade_protocol_subscribe(bh, "test.join", "test", "mydomain.com", KS_TRUE, NULL, NULL, NULL, NULL);
+       blade_protocol_subscribe(bh, "test.leave", "test", "mydomain.com", KS_TRUE, NULL, NULL, NULL, NULL);
+       blade_protocol_subscribe(bh, "test.talk", "test", "mydomain.com", KS_TRUE, NULL, NULL, NULL, NULL);
+}
+
+void command_talk(blade_handle_t *bh, char *args)
+{
+       cJSON *params = NULL;
+
+       ks_assert(bh);
+       ks_assert(args);
+
+       if (!g_testcon_nodeid) {
+               ks_log(KS_LOG_DEBUG, "Protocol controller has not been located\n");
+               return;
+       }
+       if (!args[0]) {
+               ks_log(KS_LOG_DEBUG, "Syntax: talk <text>\n");
+               return;
+       }
+
+       params = cJSON_CreateObject();
+
+       cJSON_AddStringToObject(params, "text", args);
+
+       blade_protocol_execute(bh, g_testcon_nodeid, "test.talk", "test", "mydomain.com", params, test_talk_response_handler, NULL);
+}
+
+/* For Emacs:
+* Local Variables:
+* mode:c
+* indent-tabs-mode:t
+* tab-width:4
+* c-basic-offset:4
+* End:
+* For VIM:
+* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
+*/
diff --git a/libs/libblade/test/testcli.cfg b/libs/libblade/test/testcli.cfg
new file mode 100644 (file)
index 0000000..6d7e93b
--- /dev/null
@@ -0,0 +1,3 @@
+blade:
+{
+};
diff --git a/libs/libblade/test/testcli.vcxproj b/libs/libblade/test/testcli.vcxproj
new file mode 100644 (file)
index 0000000..b8cb371
--- /dev/null
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{CF89E839-AB50-4BBB-AC34-0D6232E1EBB5}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>testcli</RootNamespace>
+    <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\..\..\w32\openssl-version.props" />
+    <Import Project="..\..\..\w32\sodium.props" />
+    <Import Project="..\..\..\w32\config.props" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\..\..\w32\openssl-version.props" />
+    <Import Project="..\..\..\w32\sodium.props" />
+    <Import Project="..\..\..\w32\config.props" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\..\..\w32\openssl-version.props" />
+    <Import Project="..\..\..\w32\sodium.props" />
+    <Import Project="..\..\..\w32\config.props" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\..\..\w32\openssl-version.props" />
+    <Import Project="..\..\..\w32\sodium.props" />
+    <Import Project="..\..\..\w32\config.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+    <IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
+    <IncludePath>$(SolutionDir);$(SolutionDir)..\libks\src\include;$(SolutionDir)..\libsodium-$(SodiumVersion)\src\libsodium\include;$(SolutionDir)..\libconfig-$(ConfigVersion)\lib;$(SolutionDir)\openssl\include;$(IncludePath)</IncludePath>
+    <LibraryPath>$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+    <IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <IncludePath>$(SolutionDir);$(SolutionDir)..\libks\src\include;$(SolutionDir)..\libsodium-$(SodiumVersion)\src\libsodium\include;$(SolutionDir)..\libconfig-$(ConfigVersion)\lib;$(SolutionDir)\openssl\include64;$(IncludePath)</IncludePath>
+    <LibraryPath>$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+    <IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
+    <IncludePath>$(SolutionDir);$(SolutionDir)..\libks\src\include;$(SolutionDir)..\libsodium-$(SodiumVersion)\src\libsodium\include;$(SolutionDir)..\libconfig-$(ConfigVersion)\lib;$(SolutionDir)\openssl\include;$(IncludePath)</IncludePath>
+    <LibraryPath>$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+    <IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <IncludePath>$(SolutionDir);$(SolutionDir)..\libks\src\include;$(SolutionDir)..\libsodium-$(SodiumVersion)\src\libsodium\include;$(SolutionDir)..\libconfig-$(ConfigVersion)\lib;$(SolutionDir)\openssl\include64;$(IncludePath)</IncludePath>
+    <LibraryPath>$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\win32\openssl\include;$(SolutionDir)..\win32\openssl\include_x86;../src/include;.</AdditionalIncludeDirectories>
+      <DisableSpecificWarnings>4090</DisableSpecificWarnings>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <MinimalRebuild>false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\win32\openssl\include;$(SolutionDir)..\win32\openssl\include_x64;../src/include;.</AdditionalIncludeDirectories>
+      <DisableSpecificWarnings>4090</DisableSpecificWarnings>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <MinimalRebuild>false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\win32\openssl\include;$(SolutionDir)..\win32\openssl\include_x86;../src/include;.</AdditionalIncludeDirectories>
+      <DisableSpecificWarnings>4090</DisableSpecificWarnings>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\win32\openssl\include;$(SolutionDir)..\win32\openssl\include_x64;../src/include;.</AdditionalIncludeDirectories>
+      <DisableSpecificWarnings>4090</DisableSpecificWarnings>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="tap.c" />
+    <ClCompile Include="testcli.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\libks\libks.vcxproj">
+      <Project>{70d178d8-1100-4152-86c0-809a91cff832}</Project>
+    </ProjectReference>
+    <ProjectReference Include="..\..\win32\libconfig\libconfig.2015.vcxproj">
+      <Project>{1a234565-926d-49b2-83e4-d56e0c38c9f2}</Project>
+    </ProjectReference>
+    <ProjectReference Include="..\..\win32\libsodium\libsodium.2015.vcxproj">
+      <Project>{a185b162-6cb6-4502-b03f-b56f7699a8d9}</Project>
+    </ProjectReference>
+    <ProjectReference Include="..\..\win32\openssl\libeay32.2015.vcxproj">
+      <Project>{d331904d-a00a-4694-a5a3-fcff64ab5dbe}</Project>
+    </ProjectReference>
+    <ProjectReference Include="..\..\win32\openssl\ssleay32.2015.vcxproj">
+      <Project>{b4b62169-5ad4-4559-8707-3d933ac5db39}</Project>
+    </ProjectReference>
+    <ProjectReference Include="..\libblade.vcxproj">
+      <Project>{a89d6d18-6203-4149-9051-f8e798e7a3e7}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/libs/libblade/test/testcon.c b/libs/libblade/test/testcon.c
new file mode 100644 (file)
index 0000000..f42796e
--- /dev/null
@@ -0,0 +1,414 @@
+#include "blade.h"
+#include "tap.h"
+
+#define CONSOLE_INPUT_MAX 512
+
+ks_bool_t g_shutdown = KS_FALSE;
+
+void loop(blade_handle_t *bh);
+void process_console_input(blade_handle_t *bh, char *line);
+
+typedef void (*command_callback)(blade_handle_t *bh, char *args);
+
+struct command_def_s {
+       const char *cmd;
+       command_callback callback;
+};
+
+void command_quit(blade_handle_t *bh, char *args);
+
+static const struct command_def_s command_defs[] = {
+       { "quit", command_quit },
+
+       { NULL, NULL }
+};
+
+struct testproto_s {
+       blade_handle_t *handle;
+       ks_pool_t *pool;
+       ks_hash_t *participants;
+};
+typedef struct testproto_s testproto_t;
+
+static void testproto_cleanup(ks_pool_t *pool, void *ptr, void *arg, ks_pool_cleanup_action_t action, ks_pool_cleanup_type_t type)
+{
+       //testproto_t *test = (testproto_t *)ptr;
+
+       //ks_assert(test);
+
+       switch (action) {
+       case KS_MPCL_ANNOUNCE:
+               break;
+       case KS_MPCL_TEARDOWN:
+               break;
+       case KS_MPCL_DESTROY:
+               break;
+       }
+}
+
+ks_status_t testproto_create(testproto_t **testP, blade_handle_t *bh)
+{
+       testproto_t *test = NULL;
+       ks_pool_t *pool = NULL;
+
+       ks_assert(testP);
+       ks_assert(bh);
+
+       ks_pool_open(&pool);
+       ks_assert(pool);
+
+       test = ks_pool_alloc(pool, sizeof(testproto_t));
+       test->handle = bh;
+       test->pool = pool;
+
+       ks_hash_create(&test->participants, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_RWLOCK | KS_HASH_FLAG_DUP_CHECK | KS_HASH_FLAG_FREE_KEY, pool);
+
+       ks_pool_set_cleanup(pool, test, NULL, testproto_cleanup);
+
+       *testP = test;
+
+       return KS_STATUS_SUCCESS;
+}
+
+ks_status_t testproto_destroy(testproto_t **testP)
+{
+       testproto_t *test = NULL;
+       ks_pool_t *pool = NULL;
+
+       ks_assert(testP);
+       ks_assert(*testP);
+
+       test = *testP;
+
+       ks_pool_free(test->pool, testP);
+
+       return KS_STATUS_SUCCESS;
+}
+
+ks_bool_t test_publish_response_handler(blade_rpc_response_t *brpcres, void *data)
+{
+       testproto_t *test = NULL;
+       blade_handle_t *bh = NULL;
+       blade_session_t *bs = NULL;
+
+       ks_assert(brpcres);
+       ks_assert(data);
+
+       test = (testproto_t *)data;
+
+       bh = blade_rpc_response_handle_get(brpcres);
+       ks_assert(bh);
+
+       bs = blade_handle_sessions_lookup(bh, blade_rpc_response_sessionid_get(brpcres));
+       ks_assert(bs);
+
+       ks_log(KS_LOG_DEBUG, "Session (%s) publish response processing\n", blade_session_id_get(bs));
+
+       blade_session_read_unlock(bs);
+
+       return KS_FALSE;
+}
+
+ks_bool_t test_join_request_handler(blade_rpc_request_t *brpcreq, void *data)
+{
+       testproto_t *test = NULL;
+       blade_handle_t *bh = NULL;
+       blade_session_t *bs = NULL;
+       const char *requester_nodeid = NULL;
+       const char *key = NULL;
+       cJSON *params = NULL;
+       cJSON *result = NULL;
+
+       ks_assert(brpcreq);
+       ks_assert(data);
+
+       test = (testproto_t *)data;
+
+       bh = blade_rpc_request_handle_get(brpcreq);
+       ks_assert(bh);
+
+       bs = blade_handle_sessions_lookup(bh, blade_rpc_request_sessionid_get(brpcreq));
+       ks_assert(bs);
+
+       requester_nodeid = blade_protocol_execute_request_requester_nodeid_get(brpcreq);
+       ks_assert(requester_nodeid);
+
+       params = blade_protocol_execute_request_params_get(brpcreq);
+       ks_assert(params);
+
+       ks_log(KS_LOG_DEBUG, "Session (%s) test.join request processing\n", blade_session_id_get(bs));
+
+       key = ks_pstrdup(test->pool, requester_nodeid);
+       ks_assert(key);
+       
+       ks_hash_write_lock(test->participants);
+       ks_hash_insert(test->participants, (void *)key, (void *)KS_TRUE);
+       ks_hash_write_unlock(test->participants);
+
+       blade_session_read_unlock(bs);
+
+       result = cJSON_CreateObject();
+
+       blade_protocol_execute_response_send(brpcreq, result);
+
+       params = cJSON_CreateObject();
+
+       blade_protocol_broadcast(bh, requester_nodeid, "test.join", "test", "mydomain.com", params, NULL, NULL);
+
+       return KS_FALSE;
+}
+
+ks_bool_t test_leave_request_handler(blade_rpc_request_t *brpcreq, void *data)
+{
+       testproto_t *test = NULL;
+       blade_handle_t *bh = NULL;
+       blade_session_t *bs = NULL;
+       const char *requester_nodeid = NULL;
+       const char *key = NULL;
+       cJSON *params = NULL;
+       cJSON *result = NULL;
+
+       ks_assert(brpcreq);
+       ks_assert(data);
+
+       test = (testproto_t *)data;
+
+       bh = blade_rpc_request_handle_get(brpcreq);
+       ks_assert(bh);
+
+       bs = blade_handle_sessions_lookup(bh, blade_rpc_request_sessionid_get(brpcreq));
+       ks_assert(bs);
+
+       requester_nodeid = blade_protocol_execute_request_requester_nodeid_get(brpcreq);
+       ks_assert(requester_nodeid);
+
+       params = blade_protocol_execute_request_params_get(brpcreq);
+       ks_assert(params);
+
+       ks_log(KS_LOG_DEBUG, "Session (%s) test.leave (%s) request processing\n", blade_session_id_get(bs), requester_nodeid);
+
+       ks_hash_write_lock(test->participants);
+       ks_hash_remove(test->participants, (void *)requester_nodeid);
+       ks_hash_write_unlock(test->participants);
+
+       blade_session_read_unlock(bs);
+
+       result = cJSON_CreateObject();
+
+       blade_protocol_execute_response_send(brpcreq, result);
+
+       params = cJSON_CreateObject();
+
+       blade_protocol_broadcast(bh, requester_nodeid, "test.leave", "test", "mydomain.com", params, NULL, NULL);
+
+       return KS_FALSE;
+}
+
+ks_bool_t test_talk_request_handler(blade_rpc_request_t *brpcreq, void *data)
+{
+       testproto_t *test = NULL;
+       blade_handle_t *bh = NULL;
+       blade_session_t *bs = NULL;
+       const char *requester_nodeid = NULL;
+       const char *text = NULL;
+       cJSON *params = NULL;
+       cJSON *result = NULL;
+
+       ks_assert(brpcreq);
+       ks_assert(data);
+
+       test = (testproto_t *)data;
+
+       bh = blade_rpc_request_handle_get(brpcreq);
+       ks_assert(bh);
+
+       bs = blade_handle_sessions_lookup(bh, blade_rpc_request_sessionid_get(brpcreq));
+       ks_assert(bs);
+
+       requester_nodeid = blade_protocol_execute_request_requester_nodeid_get(brpcreq);
+       ks_assert(requester_nodeid);
+
+       params = blade_protocol_execute_request_params_get(brpcreq);
+       ks_assert(params);
+
+       text = cJSON_GetObjectCstr(params, "text");
+       ks_assert(text);
+
+       ks_log(KS_LOG_DEBUG, "Session (%s) test.talk (%s) request processing\n", blade_session_id_get(bs), requester_nodeid);
+
+       blade_session_read_unlock(bs);
+
+       result = cJSON_CreateObject();
+
+       blade_protocol_execute_response_send(brpcreq, result);
+
+       params = cJSON_CreateObject();
+
+       cJSON_AddStringToObject(params, "text", text);
+
+       blade_protocol_broadcast(bh, requester_nodeid, "test.talk", "test", "mydomain.com", params, NULL, NULL);
+
+       return KS_FALSE;
+}
+
+
+int main(int argc, char **argv)
+{
+       blade_handle_t *bh = NULL;
+       ks_pool_t *pool = NULL;
+       config_t config;
+       config_setting_t *config_blade = NULL;
+       const char *cfgpath = "testcon.cfg";
+       const char *autoconnect = NULL;
+       testproto_t *test = NULL;
+
+       ks_global_set_default_logger(KS_LOG_LEVEL_DEBUG);
+
+       blade_init();
+
+       blade_handle_create(&bh);
+       ks_assert(bh);
+
+       pool = blade_handle_pool_get(bh);
+       ks_assert(pool);
+
+       if (argc > 1) autoconnect = argv[1];
+
+       config_init(&config);
+       if (!config_read_file(&config, cfgpath)) {
+               ks_log(KS_LOG_ERROR, "%s:%d - %s\n", config_error_file(&config), config_error_line(&config), config_error_text(&config));
+               config_destroy(&config);
+               return EXIT_FAILURE;
+       }
+       config_blade = config_lookup(&config, "blade");
+       if (!config_blade) {
+               ks_log(KS_LOG_ERROR, "Missing 'blade' config group\n");
+               config_destroy(&config);
+               return EXIT_FAILURE;
+       }
+       if (config_setting_type(config_blade) != CONFIG_TYPE_GROUP) {
+               ks_log(KS_LOG_ERROR, "The 'blade' config setting is not a group\n");
+               return EXIT_FAILURE;
+       }
+
+       if (blade_handle_startup(bh, config_blade) != KS_STATUS_SUCCESS) {
+               ks_log(KS_LOG_ERROR, "Blade startup failed\n");
+               return EXIT_FAILURE;
+       }
+
+       testproto_create(&test, bh);
+
+       if (autoconnect) {
+               blade_connection_t *bc = NULL;
+               blade_identity_t *target = NULL;
+               ks_bool_t connected = KS_FALSE;
+               blade_rpc_t *brpc = NULL;
+
+               blade_identity_create(&target, blade_handle_pool_get(bh));
+
+               if (blade_identity_parse(target, autoconnect) == KS_STATUS_SUCCESS) connected = blade_handle_connect(bh, &bc, target, NULL) == KS_STATUS_SUCCESS;
+
+               blade_identity_destroy(&target);
+
+               if (connected) {
+                       // @todo use session state change callback to know when the session is ready and the realm(s) available from blade.connect, this hack temporarily ensures it's ready before trying to publish upstream
+                       ks_sleep_ms(3000);
+
+                       blade_rpc_create(&brpc, bh, "test.join", "test", "mydomain.com", test_join_request_handler, test);
+                       blade_handle_protocolrpc_register(brpc);
+
+                       blade_rpc_create(&brpc, bh, "test.leave", "test", "mydomain.com", test_leave_request_handler, test);
+                       blade_handle_protocolrpc_register(brpc);
+
+                       blade_rpc_create(&brpc, bh, "test.talk", "test", "mydomain.com", test_talk_request_handler, test);
+                       blade_handle_protocolrpc_register(brpc);
+
+                       blade_protocol_publish(bh, "test", "mydomain.com", test_publish_response_handler, test);
+               }
+       }
+
+       loop(bh);
+
+       blade_handle_destroy(&bh);
+
+       testproto_destroy(&test);
+
+       config_destroy(&config);
+
+       blade_shutdown();
+
+       return 0;
+}
+
+void loop(blade_handle_t *bh)
+{
+       char buf[CONSOLE_INPUT_MAX];
+       while (!g_shutdown) {
+               if (!fgets(buf, CONSOLE_INPUT_MAX, stdin)) break;
+
+               for (int index = 0; buf[index]; ++index) {
+                       if (buf[index] == '\r' || buf[index] == '\n') {
+                               buf[index] = '\0';
+                               break;
+                       }
+               }
+               process_console_input(bh, buf);
+       }
+}
+
+void parse_argument(char **input, char **arg, char terminator)
+{
+       char *tmp;
+
+       ks_assert(input);
+       ks_assert(*input);
+       ks_assert(arg);
+
+       tmp = *input;
+       *arg = tmp;
+
+       while (*tmp && *tmp != terminator) ++tmp;
+       if (*tmp == terminator) {
+               *tmp = '\0';
+               ++tmp;
+       }
+       *input = tmp;
+}
+
+void process_console_input(blade_handle_t *bh, char *line)
+{
+       char *args = line;
+       char *cmd = NULL;
+       ks_bool_t found = KS_FALSE;
+
+       parse_argument(&args, &cmd, ' ');
+
+       ks_log(KS_LOG_DEBUG, "Command: %s, Args: %s\n", cmd, args);
+
+       for (int32_t index = 0; command_defs[index].cmd; ++index) {
+               if (!strcmp(command_defs[index].cmd, cmd)) {
+                       found = KS_TRUE;
+                       command_defs[index].callback(bh, args);
+               }
+       }
+       if (!found) ks_log(KS_LOG_INFO, "Command '%s' unknown.\n", cmd);
+}
+
+void command_quit(blade_handle_t *bh, char *args)
+{
+       ks_assert(bh);
+       ks_assert(args);
+
+       g_shutdown = KS_TRUE;
+}
+
+/* For Emacs:
+* Local Variables:
+* mode:c
+* indent-tabs-mode:t
+* tab-width:4
+* c-basic-offset:4
+* End:
+* For VIM:
+* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
+*/
diff --git a/libs/libblade/test/testcon.cfg b/libs/libblade/test/testcon.cfg
new file mode 100644 (file)
index 0000000..6deb995
--- /dev/null
@@ -0,0 +1,20 @@
+blade:
+{
+       transport:
+       {
+               wss:
+               {
+                       endpoints:
+                       {
+                               ipv4 = ( { address = "0.0.0.0", port = 2101 } );
+                               ipv6 = ( { address = "::", port = 2101 } );
+                               backlog = 128;
+                       };
+                       # SSL group is optional, disabled when absent
+                       ssl:
+                       {
+                               # todo: server SSL stuffs here
+                       };
+               };
+       };
+};
diff --git a/libs/libblade/test/testcon.vcxproj b/libs/libblade/test/testcon.vcxproj
new file mode 100644 (file)
index 0000000..8b208c1
--- /dev/null
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{D67EEF66-B323-4BCF-9E3C-3A640B9949B7}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>testcon</RootNamespace>
+    <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\..\..\w32\openssl-version.props" />
+    <Import Project="..\..\..\w32\sodium.props" />
+    <Import Project="..\..\..\w32\config.props" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\..\..\w32\openssl-version.props" />
+    <Import Project="..\..\..\w32\sodium.props" />
+    <Import Project="..\..\..\w32\config.props" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\..\..\w32\openssl-version.props" />
+    <Import Project="..\..\..\w32\sodium.props" />
+    <Import Project="..\..\..\w32\config.props" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="..\..\..\w32\openssl-version.props" />
+    <Import Project="..\..\..\w32\sodium.props" />
+    <Import Project="..\..\..\w32\config.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+    <IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
+    <IncludePath>$(SolutionDir);$(SolutionDir)..\libks\src\include;$(SolutionDir)..\libsodium-$(SodiumVersion)\src\libsodium\include;$(SolutionDir)..\libconfig-$(ConfigVersion)\lib;$(SolutionDir)\openssl\include;$(IncludePath)</IncludePath>
+    <LibraryPath>$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+    <IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <IncludePath>$(SolutionDir);$(SolutionDir)..\libks\src\include;$(SolutionDir)..\libsodium-$(SodiumVersion)\src\libsodium\include;$(SolutionDir)..\libconfig-$(ConfigVersion)\lib;$(SolutionDir)\openssl\include64;$(IncludePath)</IncludePath>
+    <LibraryPath>$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+    <IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
+    <IncludePath>$(SolutionDir);$(SolutionDir)..\libks\src\include;$(SolutionDir)..\libsodium-$(SodiumVersion)\src\libsodium\include;$(SolutionDir)..\libconfig-$(ConfigVersion)\lib;$(SolutionDir)\openssl\include;$(IncludePath)</IncludePath>
+    <LibraryPath>$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+    <IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
+    <IncludePath>$(SolutionDir);$(SolutionDir)..\libks\src\include;$(SolutionDir)..\libsodium-$(SodiumVersion)\src\libsodium\include;$(SolutionDir)..\libconfig-$(ConfigVersion)\lib;$(SolutionDir)\openssl\include64;$(IncludePath)</IncludePath>
+    <LibraryPath>$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\win32\openssl\include;$(SolutionDir)..\win32\openssl\include_x86;../src/include;.</AdditionalIncludeDirectories>
+      <DisableSpecificWarnings>4090</DisableSpecificWarnings>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <MinimalRebuild>false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\win32\openssl\include;$(SolutionDir)..\win32\openssl\include_x64;../src/include;.</AdditionalIncludeDirectories>
+      <DisableSpecificWarnings>4090</DisableSpecificWarnings>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <MinimalRebuild>false</MinimalRebuild>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\win32\openssl\include;$(SolutionDir)..\win32\openssl\include_x86;../src/include;.</AdditionalIncludeDirectories>
+      <DisableSpecificWarnings>4090</DisableSpecificWarnings>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\win32\openssl\include;$(SolutionDir)..\win32\openssl\include_x64;../src/include;.</AdditionalIncludeDirectories>
+      <DisableSpecificWarnings>4090</DisableSpecificWarnings>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="tap.c" />
+    <ClCompile Include="testcon.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\libks\libks.vcxproj">
+      <Project>{70d178d8-1100-4152-86c0-809a91cff832}</Project>
+    </ProjectReference>
+    <ProjectReference Include="..\..\win32\libconfig\libconfig.2015.vcxproj">
+      <Project>{1a234565-926d-49b2-83e4-d56e0c38c9f2}</Project>
+    </ProjectReference>
+    <ProjectReference Include="..\..\win32\libsodium\libsodium.2015.vcxproj">
+      <Project>{a185b162-6cb6-4502-b03f-b56f7699a8d9}</Project>
+    </ProjectReference>
+    <ProjectReference Include="..\..\win32\openssl\libeay32.2015.vcxproj">
+      <Project>{d331904d-a00a-4694-a5a3-fcff64ab5dbe}</Project>
+    </ProjectReference>
+    <ProjectReference Include="..\..\win32\openssl\ssleay32.2015.vcxproj">
+      <Project>{b4b62169-5ad4-4559-8707-3d933ac5db39}</Project>
+    </ProjectReference>
+    <ProjectReference Include="..\libblade.vcxproj">
+      <Project>{a89d6d18-6203-4149-9051-f8e798e7a3e7}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file