Pushing broken PHP in hope for help.

This commit is contained in:
Scott Duensing 2022-01-12 17:28:50 -06:00
parent 597284e28f
commit cb34b340ae
19 changed files with 561 additions and 57 deletions

View file

@ -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();

View file

@ -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;
StringMapT *strings;
IntegerMapT *integers;
} RuntimeDataT;

View file

@ -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) {
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);

View file

@ -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,

View file

@ -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.';
}
?>

View file

@ -0,0 +1,11 @@
<?php
function kpApiTest(&$response) {
$response['payload'] = collection('kwstrings');
$response['result'] = 'true';
$response['reason'] = 'Dataset returned.';
}
?>

View file

@ -1,32 +0,0 @@
<?php
return [
'kwconfig' => [
'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'
]
]
];
?>

View file

@ -0,0 +1,46 @@
<?php
namespace KangarooPunch;
class KPunch {
public static function databaseGet() {
$db = null;
try {
$host = option('kangaroopunch.kangaworld-integration.sql.host');
$port = option('kangaroopunch.kangaworld-integration.sql.port');
$data = option('kangaroopunch.kangaworld-integration.sql.data');
$user = option('kangaroopunch.kangaworld-integration.sql.user');
$pass = option('kangaroopunch.kangaworld-integration.sql.pass');
$db = new PDO("mysql:host=$host;port=$port;dbname=$data", $user, $pass);
$db->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;
}
}
?>

View file

@ -0,0 +1,90 @@
<?php
namespace KangarooPunch;
use KangarooPunch\KPunch;
use Kirby\Exception\InvalidArgumentException;
use Kirby\Exception\NotFoundException;
use Kirby\Toolkit\V;
class KwStrings {
public static function create(array $input): bool {
validate($input);
$db = KPunch::databaseGet();
KPunch::databaseQueryToJSON($db,
'INSERT INTO strings (name, data, description) VALUES (?, ?, ?)',
array(
$input['name'],
$input['data'],
$input['description']
)
);
return true;
}
public static function delete(string $id): bool {
$db = KPunch::databaseGet();
KPunch::databaseQueryToJSON($db, 'DELETE FROM strings WHERE id = ?', array($id));
return true;
}
public static function find(string $id): array {
$db = KPunch::databaseGet();
$result = KPunch::databaseQueryToJSON($db, 'SELECT * FROM strings WHERE id = ?', array($id));
if ($result == null) {
throw new NotFoundException('The entry could not be found');
}
return json_decode($result);
}
public static function list(): array {
$db = KPunch::databaseGet();
return json_decode(KPunch::databaseQueryToJSON($db, 'SELECT * FROM strings'));
}
public static function update(array $input): bool {
validate($input);
$db = KPunch::databaseGet();
KPunch::databaseQueryToJSON($db,
'UPDATE strings SET name = ?, data = ?, description = ? WHERE id = ?',
array(
$input['name'],
$input['data'],
$input['description'],
$input['id']
)
);
return true;
}
private static function validate($input) {
if (V::minlength($input['name'], 1) === false) {
throw new InvalidArgumentException('The name must not be empty');
}
if (V::maxlength($input['name'], 32) === false) {
throw new InvalidArgumentException('The name must not be longer than 32 characters');
}
if (V::minlength($input['data'], 1) === false) {
throw new InvalidArgumentException('The data must not be empty');
}
if (V::maxlength($input['data'], 4096) === false) {
throw new InvalidArgumentException('The data must not be longer than 4096 characters');
}
if (V::maxlength($input['description'], 255) === false) {
throw new InvalidArgumentException('The description must not be longer than 255 characters');
}
}
}
?>

View file

@ -1,7 +1,8 @@
<?php
require __DIR__ . '/api/config.php';
require __DIR__ . '/api/user.php';
require __DIR__ . '/../api/config.php';
require __DIR__ . '/../api/test.php';
require __DIR__ . '/../api/user.php';
return [
@ -22,10 +23,22 @@ return [
switch (get('command')) {
case 'API_TEST':
kpApiTest($response);
break;
case 'CONFIG_GET_CONFIG':
kpApiConfigGetConfig($response);
break;
case 'CONFIG_GET_NUMBERS':
kpApiConfigGetNumbers($response);
break;
case 'CONFIG_GET_STRINGS':
kpApiConfigGetStrings($response);
break;
case 'USER_CREATE':
kpApiUserCreate(get('name'), get('email'), get('password'), $response);
break;

View file

@ -0,0 +1,32 @@
<?php
return [
'kwconfig' => [
'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'
],
]
];
?>

View file

@ -1,10 +1,15 @@
<?php
use KangarooPunch\KwConfig;
use KangarooPunch\KwStrings;
return [
'kwconfig' => function($site) {
return new Collection(KwConfig::list());
},
'kwstrings' => function($site) {
return new Collection(KwStrings::list());
}
];

View file

@ -0,0 +1,13 @@
<?php
return [
'sql' => [
'host' => 'mysql',
'port' => 3306,
'data' => 'dosThing',
'user' => 'dosThing',
'pass' => 'password'
]
];
?>

View file

@ -1,15 +1,26 @@
<?php
/*
load([
'KangarooPunch\KwConfig' => __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'
]);

View file

@ -20,7 +20,7 @@ TEMPLATE = subdirs
CONFIG *= ORDERED
SUBDIRS = \
# client \
client \
server
# font \
# primes

View file

@ -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) {
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;

View file

@ -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<json_object_array_length(data); i++) {
item = json_object_array_get_idx(data, i);
if (strcmp(name, json_object_get_string(json_object_object_get(item, "name"))) == 0) {
// Because of the way Kirby is sending us REST data, everything is a string. We have to convert it to an integer.
result = atol(json_object_get_string(json_object_object_get(item, "data")));
break;
}
@ -71,6 +71,95 @@ int64_t restHelperConfigIntegerGet(json_object *object, char *name, int64_t defa
}
RestIntegerMapT *restHelperConfigIntegerMapGet(json_object *object) {
uint64_t i = 0;
json_object *config = NULL;
json_object *data = NULL;
json_object *item = NULL;
RestIntegerMapT *result = NULL;
config = json_object_object_get(object, "config");
if (config) {
data = json_object_object_get(config, "data");
if (data) {
for (i=0; i<json_object_array_length(data); i++) {
item = json_object_array_get_idx(data, i);
shput(result, json_object_get_string(json_object_object_get(item, "name")), atol(json_object_get_string(json_object_object_get(item, "data"))));
}
}
}
return result;
}
void restHelperConfigIntegerMapRelease(RestIntegerMapT *map) {
while (shlen(map) > 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<json_object_array_length(data); i++) {
item = json_object_array_get_idx(data, i);
if (strcmp(name, json_object_get_string(json_object_object_get(item, "name"))) == 0) {
DEL(result);
result = strdup(json_object_get_string(json_object_object_get(item, "data")));
break;
}
}
}
}
return result;
}
*/
RestStringMapT *restHelperConfigStringMapGet(json_object *object) {
uint64_t i = 0;
json_object *config = NULL;
json_object *data = NULL;
json_object *item = NULL;
RestStringMapT *result = NULL;
config = json_object_object_get(object, "config");
if (config) {
data = json_object_object_get(config, "data");
if (data) {
for (i=0; i<json_object_array_length(data); i++) {
item = json_object_array_get_idx(data, i);
shput(result, json_object_get_string(json_object_object_get(item, "name")), strdup(json_object_get_string(json_object_object_get(item, "data"))));
}
}
}
return result;
}
void restHelperConfigStringMapRelease(RestStringMapT *map) {
while (shlen(map) > 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) {

View file

@ -25,7 +25,23 @@
#include <json-c/json.h>
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);

View file

@ -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;