diff --git a/client/src/main.c b/client/src/main.c index 14993d1..075e194 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -253,12 +253,33 @@ int main(int argc, char *argv[]) { __packetThreadData = packetThreadDataCreate(NULL); packetSenderRegister(comPacketSender); + __runtimeData.integers = NULL; + __runtimeData.strings = NULL; + __runtimeData.protocolVersion = 0; + //taskCreate(taskComDebugLoop, NULL); taskCreate(taskWelcome, NULL); taskCreate(taskGuiEventLoop, NULL); taskRun(); + // Free integer table. + if (__runtimeData.integers) { + while (shlen(__runtimeData.integers) > 0) { + shdel(__runtimeData.integers, __runtimeData.integers[0].key); + } + shfree(__runtimeData.integers); + } + + // Free string table. + if (__runtimeData.strings) { + while (shlen(__runtimeData.strings) > 0) { + DEL(__runtimeData.strings[0].value); + shdel(__runtimeData.strings, __runtimeData.strings[0].key); + } + shfree(__runtimeData.strings); + } + packetThreadDataDestroy(&__packetThreadData); taskShutdown(); diff --git a/client/src/runtime.h b/client/src/runtime.h index 358d89b..33be6db 100644 --- a/client/src/runtime.h +++ b/client/src/runtime.h @@ -25,8 +25,21 @@ #include "os.h" +typedef struct StringMapS { + char *key; + char *value; +} StringMapT; + +typedef struct IntegerMapS { + char *key; + int32_t value; +} IntegerMapT; + + typedef struct RuntimeDataS { - uint32_t protocolVersion; + uint32_t protocolVersion; + StringMapT *strings; + IntegerMapT *integers; } RuntimeDataT; diff --git a/client/src/welcome.c b/client/src/welcome.c index d2d154d..b64a47a 100644 --- a/client/src/welcome.c +++ b/client/src/welcome.c @@ -82,7 +82,9 @@ static void taskConnectClick(void *data) { int32_t r = 0; char buffer[1024] = { 0 }; PacketDecodeDataT *decoded = NULL; + PacketEncodeDataT encoded = { 0 }; int16_t timeout = 0; + uint8_t waiting = 1; (void)data; @@ -153,20 +155,62 @@ static void taskConnectClick(void *data) { return; } - // Wait for version packet to arrive. + // Wait for version and table packets to arrive. timeout = 10; do { decoded = netGetPacket(0); if (decoded) { - if (decoded->packetType == PACKET_TYPE_VERSION) { - __runtimeData.protocolVersion = *(uint32_t *)decoded->data; - packetDecodeDataDestroy(&decoded); - break; + switch (decoded->packetType) { + case PACKET_TYPE_NUMBER: + // Store in number table. + logWrite("Added integer: [%s] = [%d]\n", &decoded->data[4], (int32_t)decoded->data[0]); + shput(__runtimeData.integers, &decoded->data[4], (int32_t)decoded->data[0]); + // Reset timeout. + timeout = 10; + break; + + case PACKET_TYPE_PROCEED: + waiting = 0; + break; + + case PACKET_TYPE_STRING: + // Store in string table. + logWrite("Added string: [%s] = [%s]\n", decoded->data, &decoded->data[strlen(decoded->data) + 1]); + shput(__runtimeData.strings, decoded->data, strdup(&decoded->data[strlen(decoded->data) + 1])); + // Reset timeout. + timeout = 10; + break; + + case PACKET_TYPE_VERSION: + __runtimeData.protocolVersion = *(uint32_t *)decoded->data; + packetDecodeDataDestroy(&decoded); + // Do we need to update? + if (PACKET_PROTOCOL_VERSION == __runtimeData.protocolVersion) { + // Nope, we're good. + encoded.packetType = PACKET_TYPE_VERSION_OKAY; + } else { + // Version mismatch - upgrade time! + encoded.packetType = PACKET_TYPE_VERSION_BAD; + //***TODO*** + } + encoded.control = PACKET_CONTROL_DAT; + encoded.channel = 0; + encoded.encrypt = 0; + packetEncode(__packetThreadData, &encoded, NULL, 0); + // Send GOOD or BAD. + packetSend(__packetThreadData, &encoded); + // Reset timeout. + timeout = 10; + break; + + default: + logWrite("Unexpected packet received: %d\n", decoded->packetType); + break; } } taskYield(); if (__timerSecondTick) timeout--; - } while (!guiHasStopped() && timeout >= 0); + } while (!guiHasStopped() && timeout >= 0 && waiting); if (timeout < 0) { comClose(__configData.serialCom - 1); msgBoxOne("Negotiation Error", MSGBOX_ICON_INFORMATION, "Unable to fetch client settings!", "Okay", btnMsgBox); diff --git a/kanga.world/site/config/config.php.example b/kanga.world/site/config/config.php.example index 071a226..4e0ed71 100644 --- a/kanga.world/site/config/config.php.example +++ b/kanga.world/site/config/config.php.example @@ -6,6 +6,17 @@ return [ 'extra' => false ], + + 'kangaroopunch.kangaworld-integration' => [ + 'sql' => [ + 'host' => 'mysql', + 'port' => 3306, + 'data' => 'dosThing', + 'user' => 'dosThing', + 'pass' => 'password' + ] + ], + // For REST APIs. 'api' => [ 'basicAuth' => true, diff --git a/kanga.world/site/plugins/kangaworld-integration/api/config.php b/kanga.world/site/plugins/kangaworld-integration/api/config.php index 762ef7e..3f25ec6 100644 --- a/kanga.world/site/plugins/kangaworld-integration/api/config.php +++ b/kanga.world/site/plugins/kangaworld-integration/api/config.php @@ -6,4 +6,18 @@ function kpApiConfigGetConfig(&$response) { $response['reason'] = 'Configuration entries returned.'; } + +function kpApiConfigGetNumbers(&$response) { + $response['config'] = collection('kwconfig')->filterBy('type', 'number'); + $response['result'] = 'true'; + $response['reason'] = 'Number entries returned.'; +} + + +function kpApiConfigGetStrings(&$response) { + $response['config'] = collection('kwconfig')->filterBy('type', 'string'); + $response['result'] = 'true'; + $response['reason'] = 'String entries returned.'; +} + ?> diff --git a/kanga.world/site/plugins/kangaworld-integration/api/test.php b/kanga.world/site/plugins/kangaworld-integration/api/test.php new file mode 100644 index 0000000..ba0a14a --- /dev/null +++ b/kanga.world/site/plugins/kangaworld-integration/api/test.php @@ -0,0 +1,11 @@ + diff --git a/kanga.world/site/plugins/kangaworld-integration/areas.php b/kanga.world/site/plugins/kangaworld-integration/areas.php deleted file mode 100644 index bd86b07..0000000 --- a/kanga.world/site/plugins/kangaworld-integration/areas.php +++ /dev/null @@ -1,32 +0,0 @@ - [ - 'label' => 'Kanga World Configuration', - 'icon' => 'globe', - 'menu' => true, - // update and delete dialogs - 'dialogs' => [ - require __DIR__ . '/dialogs/create.php', - require __DIR__ . '/dialogs/update.php', - require __DIR__ . '/dialogs/delete.php' - ], - // dropdown with edit and delete buttons - 'dropdowns' => [ - require __DIR__ . '/dropdowns/kwconfig.php' - ], - // search for settings - 'searches' => [ - 'kwconfig' => require __DIR__ . '/searches/kwconfig.php' - ], - // view route - 'views' => [ - require __DIR__ . '/views/kwconfig.php', - require __DIR__ . '/views/kwentry.php' - ] - ] - -]; - -?> diff --git a/kanga.world/site/plugins/kangaworld-integration/classes/KPunch.php b/kanga.world/site/plugins/kangaworld-integration/classes/KPunch.php new file mode 100644 index 0000000..8119569 --- /dev/null +++ b/kanga.world/site/plugins/kangaworld-integration/classes/KPunch.php @@ -0,0 +1,46 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } catch(PDOException $ex) { + $db = null; + } + + return $db; + } + + + public static function databaseQueryToJSON($db, $query, $options = null) { + + $result = null; + + if ($db) { + $statement = $db->prepare($query); + $statement->execute($options); + $resultset = array(); + while ($row = $statement->fetch(PDO::FETCH_ASSOC)) { + $resultset[] = $row; + } + $result = json_encode($resultset); + } + + return $result; + } + +} + +?> diff --git a/kanga.world/site/plugins/kangaworld-integration/classes/KwStrings.php b/kanga.world/site/plugins/kangaworld-integration/classes/KwStrings.php new file mode 100644 index 0000000..0bb2d52 --- /dev/null +++ b/kanga.world/site/plugins/kangaworld-integration/classes/KwStrings.php @@ -0,0 +1,90 @@ + diff --git a/kanga.world/site/plugins/kangaworld-integration/api.php b/kanga.world/site/plugins/kangaworld-integration/features/api.php similarity index 71% rename from kanga.world/site/plugins/kangaworld-integration/api.php rename to kanga.world/site/plugins/kangaworld-integration/features/api.php index 719c7c2..3dcd9b7 100644 --- a/kanga.world/site/plugins/kangaworld-integration/api.php +++ b/kanga.world/site/plugins/kangaworld-integration/features/api.php @@ -1,7 +1,8 @@ [ + 'label' => 'Kanga World Configuration', + 'icon' => 'globe', + 'menu' => true, + // update and delete dialogs + 'dialogs' => [ + require __DIR__ . '/../dialogs/create.php', + require __DIR__ . '/../dialogs/update.php', + require __DIR__ . '/../dialogs/delete.php' + ], + // dropdown with edit and delete buttons + 'dropdowns' => [ + require __DIR__ . '/../dropdowns/kwconfig.php' + ], + // search for settings + 'searches' => [ + 'kwconfig' => require __DIR__ . '/../searches/kwconfig.php' + ], + // view route + 'views' => [ + require __DIR__ . '/../views/kwconfig.php', + require __DIR__ . '/../views/kwentry.php' + ], + ] + +]; + +?> diff --git a/kanga.world/site/plugins/kangaworld-integration/collections.php b/kanga.world/site/plugins/kangaworld-integration/features/collections.php similarity index 52% rename from kanga.world/site/plugins/kangaworld-integration/collections.php rename to kanga.world/site/plugins/kangaworld-integration/features/collections.php index 6d782ed..091f932 100644 --- a/kanga.world/site/plugins/kangaworld-integration/collections.php +++ b/kanga.world/site/plugins/kangaworld-integration/features/collections.php @@ -1,11 +1,16 @@ function($site) { return new Collection(KwConfig::list()); - } + }, + + 'kwstrings' => function($site) { + return new Collection(KwStrings::list()); + } ]; ?> diff --git a/kanga.world/site/plugins/kangaworld-integration/features/options.php b/kanga.world/site/plugins/kangaworld-integration/features/options.php new file mode 100644 index 0000000..980ceb2 --- /dev/null +++ b/kanga.world/site/plugins/kangaworld-integration/features/options.php @@ -0,0 +1,13 @@ + [ + 'host' => 'mysql', + 'port' => 3306, + 'data' => 'dosThing', + 'user' => 'dosThing', + 'pass' => 'password' + ] +]; + +?> diff --git a/kanga.world/site/plugins/kangaworld-integration/index.php b/kanga.world/site/plugins/kangaworld-integration/index.php index 250f073..ac69637 100644 --- a/kanga.world/site/plugins/kangaworld-integration/index.php +++ b/kanga.world/site/plugins/kangaworld-integration/index.php @@ -1,15 +1,26 @@ __DIR__ . '/classes/KwConfig.php' + 'KangarooPunch\KPunch ' => __DIR__ . '/classes/KPunch.php', + 'KangarooPunch\KwConfig' => __DIR__ . '/classes/KwConfig.php', + 'KangarooPunch\KwStrings' => __DIR__ . '/classes/KwStrings.php' ]); +*/ + +load([ + 'KangarooPunch\KPunch ' => 'KPunch.php', + 'KangarooPunch\KwConfig' => 'KwConfig.php', + 'KangarooPunch\KwStrings' => 'KwStrings.php' +], __DIR__ . '/classes'); Kirby::plugin('kangaroopunch/kangaworld-integration', [ - 'api' => require __DIR__ . '/api.php', - 'areas' => require __DIR__ . '/areas.php', - 'collections' => require __DIR__ . '/collections.php' + 'api' => require __DIR__ . '/features/api.php', +// 'areas' => require __DIR__ . '/features/areas.php', + 'collections' => require __DIR__ . '/features/collections.php', + 'options' => require __DIR__ . '/features/options.php' ]); diff --git a/kpmpgsmkii.pro b/kpmpgsmkii.pro index 197a241..00ff9fd 100644 --- a/kpmpgsmkii.pro +++ b/kpmpgsmkii.pro @@ -20,7 +20,7 @@ TEMPLATE = subdirs CONFIG *= ORDERED SUBDIRS = \ -# client \ + client \ server # font \ # primes diff --git a/server/src/client.c b/server/src/client.c index e3c97f3..fbf18f0 100644 --- a/server/src/client.c +++ b/server/src/client.c @@ -24,6 +24,7 @@ #include "console.h" #include "packet.h" #include "server.h" +#include "rest.h" static uint8_t clientDequeuePacket(ClientThreadT *client); @@ -63,7 +64,15 @@ static uint8_t clientDequeuePacket(ClientThreadT *client) { static void clientProcessPacket(ClientThreadT *client, PacketDecodeDataT *data) { - PacketEncodeDataT encoded = { 0 }; + uint64_t i = 0; + uint64_t x = 0; + uint32_t y = 0; + uint64_t length = 0; + char *buffer = NULL; + PacketEncodeDataT encoded = { 0 }; + json_object *response = NULL; + RestStringMapT *strings = NULL; + RestIntegerMapT *integers = NULL; switch (data->packetType) { case PACKET_TYPE_CLIENT_SHUTDOWN: @@ -86,6 +95,98 @@ static void clientProcessPacket(ClientThreadT *client, PacketDecodeDataT *data) logWrite("Got PING, sent PONG\n\r"); break; + case PACKET_TYPE_VERSION_BAD: + //***TODO*** + break; + + case PACKET_TYPE_VERSION_OKAY: + logWrite("Got VERSION_OK.\n\r"); + // Fetch string table from REST. + response = restRequest("CONFIG_GET_STRINGS", NULL); + if (!response) { + logWrite("Unable to fetch strings!\n\r"); + break; + } + strings = restHelperConfigStringMapGet(response); + if (!strings) { + logWrite("Unable to map strings!\n\r"); + break; + } + restRelease(response); + // Send string table to client. + logWrite("Sending strings.\n\r"); + for (i=0; i<(unsigned)shlen(strings); i++) { + // Strings are encoded in a single buffer as: KEY\0DATA\0 + x = strlen(strings[i].key); + y = strlen(strings[i].value); + length = x + y + 2; + buffer = (char *)malloc(length); + if (!buffer) { + logWrite("Unable to allocate buffer for string packet!\n\r"); + break; + } + memcpy(buffer, strings[i].key, x + 1); + memcpy(&buffer[x + 1], strings[i].value, y + 1); + // Build packet. + encoded.control = PACKET_CONTROL_DAT; + encoded.packetType = PACKET_TYPE_STRING; + encoded.channel = 0; + encoded.encrypt = 0; + packetEncode(client->packetThreadData, &encoded, buffer, length); + // Send it. + packetSend(client->packetThreadData, &encoded); + DEL(buffer); + } + restHelperConfigStringMapRelease(strings); + // Fetch number table from REST. + response = restRequest("CONFIG_GET_NUMBERS", NULL); + if (!response) { + logWrite("Unable to fetch numbers!\n\r"); + break; + } + integers = restHelperConfigIntegerMapGet(response); + if (!integers) { + logWrite("Unable to map numbers!\n\r"); + break; + } + restRelease(response); + // Send number table to client. + logWrite("Sending numbers.\n\r"); + for (i=0; i<(unsigned)shlen(integers); i++) { + // Integers are encoded in a single buffer as: 1234DATA\0 + // Integers are 64 bit until sent to the client when they are truncated to 32. + x = strlen(integers[i].key); + y = integers[i].value; // 64->32 + length = x + 5; + buffer = (char *)malloc(length); + if (!buffer) { + logWrite("Unable to allocate buffer for number packet!\n\r"); + break; + } + memcpy(buffer, &y, 4); + memcpy(&buffer[4], integers[i].key, x + 1); + // Build packet. + encoded.control = PACKET_CONTROL_DAT; + encoded.packetType = PACKET_TYPE_NUMBER; + encoded.channel = 0; + encoded.encrypt = 0; + packetEncode(client->packetThreadData, &encoded, buffer, length); + // Send it. + packetSend(client->packetThreadData, &encoded); + DEL(buffer); + } + restHelperConfigIntegerMapRelease(integers); + // Build PROCEED packet. + encoded.control = PACKET_CONTROL_DAT; + encoded.packetType = PACKET_TYPE_PROCEED; + encoded.channel = 0; + encoded.encrypt = 0; + packetEncode(client->packetThreadData, &encoded, NULL, 0); + // Send it. + packetSend(client->packetThreadData, &encoded); + logWrite("Sending proceed.\n\r"); + break; + default: consoleMessageQueue("%ld: Channel %d Unknown Packet %d\n", client->threadIndex, data->channel, data->packetType); break; @@ -136,6 +237,7 @@ void *clientThread(void *data) { // Start communications with client as soon as encryption channel is ready. if (!versionSent) { if (packetEncryptionReady()) { + // Send required protocol version. version.version = PACKET_PROTOCOL_VERSION; encoded.control = PACKET_CONTROL_DAT; encoded.packetType = PACKET_TYPE_VERSION; diff --git a/server/src/rest.c b/server/src/rest.c index 0a9d2c1..20be626 100644 --- a/server/src/rest.c +++ b/server/src/rest.c @@ -24,6 +24,7 @@ #include "os.h" #include "rest.h" +#include "array.h" typedef struct RestResponseS { @@ -52,14 +53,13 @@ int64_t restHelperConfigIntegerGet(json_object *object, char *name, int64_t defa int64_t result = defaultValue; config = json_object_object_get(object, "config"); - //logWrite("Config: %d %s\n", json_object_get_type(config), json_object_to_json_string_ext(config, JSON_C_TO_STRING_PRETTY)); if (config) { data = json_object_object_get(config, "data"); - //logWrite("Data: %d %s\n", json_object_get_type(data), json_object_to_json_string_ext(data, JSON_C_TO_STRING_PRETTY)); if (data) { for (i=0; i 0) { + shdel(map, map[0].key); + } + shfree(map); +} + + +/* +char *restHelperConfigStringGet(json_object *object, char *name, char *defaultValue) { + uint64_t i = 0; + json_object *config = NULL; + json_object *data = NULL; + json_object *item = NULL; + char *result = strdup(defaultValue); + + config = json_object_object_get(object, "config"); + if (config) { + data = json_object_object_get(config, "data"); + if (data) { + for (i=0; i 0) { + DEL(map[0].value); + shdel(map, map[0].key); + } + shfree(map); +} + + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" // It's used, but the compiler isn't picking it up because it's via a callback. static void restMutexLocker(int mode, int n, const char *file, int line) { @@ -119,10 +208,10 @@ json_object *restRequest(char *command, char *format, ...) { } va_end(args); - //logWrite("Request: %s\n", json_object_to_json_string_ext(request, JSON_C_TO_STRING_PRETTY)); + logWrite("Request: %s\n", json_object_to_json_string_ext(request, JSON_C_TO_STRING_PRETTY)); response = restUrlPost(request); json_object_put(request); - //logWrite("Response: %s\n", json_object_to_json_string_ext(response, JSON_C_TO_STRING_PRETTY)); + logWrite("Response: %s\n", json_object_to_json_string_ext(response, JSON_C_TO_STRING_PRETTY)); if (response) { if (json_object_get_boolean(json_object_object_get(response, "result")) != TRUE) { diff --git a/server/src/rest.h b/server/src/rest.h index 4252ad0..5750e4f 100644 --- a/server/src/rest.h +++ b/server/src/rest.h @@ -25,11 +25,27 @@ #include -int64_t restHelperConfigIntegerGet(json_object *object, char *name, int64_t defaultValue); -void restRelease(json_object *object); -json_object *restRequest(char *command, char *format, ...); -void restShutdown(void); -uint8_t restStartup(char *url, char *user, char *password); +typedef struct RestStringMapS { + char *key; + char *value; +} RestStringMapT; + +typedef struct RestIntegerMapS { + char *key; + int64_t value; +} RestIntegerMapT; + + +int64_t restHelperConfigIntegerGet(json_object *object, char *name, int64_t defaultValue); +RestIntegerMapT *restHelperConfigIntegerMapGet(json_object *object); +void restHelperConfigIntegerMapRelease(RestIntegerMapT *map); +//char *restHelperConfigStringGet(json_object *object, char *name, char *defaultValue); +RestStringMapT *restHelperConfigStringMapGet(json_object *object); +void restHelperConfigStringMapRelease(RestStringMapT *map); +void restRelease(json_object *object); +json_object *restRequest(char *command, char *format, ...); +void restShutdown(void); +uint8_t restStartup(char *url, char *user, char *password); #endif // REST_H diff --git a/shared/packets.h b/shared/packets.h index 29400ad..af71fdd 100644 --- a/shared/packets.h +++ b/shared/packets.h @@ -41,6 +41,11 @@ typedef enum PacketTypeE { PACKET_TYPE_DH_REQUEST, PACKET_TYPE_DH_RESPONSE, PACKET_TYPE_VERSION, + PACKET_TYPE_VERSION_OKAY, + PACKET_TYPE_VERSION_BAD, + PACKET_TYPE_STRING, + PACKET_TYPE_NUMBER, + PACKET_TYPE_PROCEED, PACKET_TYPE_LOGIN, PACKET_TYPE_COUNT } PacketTypeT;