From 1a9fcdaaf3efc3d7b07627268998752e873abbd5 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Sun, 13 Feb 2022 20:38:11 -0600 Subject: [PATCH] File transfer code in. Still being debugged. --- .gitignore | 1 + LICENSE | 13 ++- client/assets/menu.xcf | 4 +- client/client.pro | 2 + client/src/file.c | 188 ++++++++++++++++++++++++++++++++++++++ client/src/file.h | 34 +++++++ client/src/main.c | 5 +- client/src/menu.c | 30 ++++-- client/src/system/cache.h | 2 +- kpmpgsmkii.pro | 4 +- server/server.pro | 6 +- server/src/client.h | 2 + server/src/client/file.c | 154 +++++++++++++++++++------------ server/src/client/file.h | 3 + server/src/database.c | 96 +++++++++++++++++++ server/src/database.h | 2 + server/src/main.c | 26 ++---- server/src/settings.c | 30 ++++++ server/src/settings.h | 37 ++++++++ shared/packets.h | 20 +++- 20 files changed, 560 insertions(+), 99 deletions(-) create mode 100644 client/src/file.c create mode 100644 client/src/file.h create mode 100644 server/src/settings.c create mode 100644 server/src/settings.h diff --git a/.gitignore b/.gitignore index f81878b..d93ba10 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ bin/ obj/ retired/ test/ +doors/ */out/ diff --git a/LICENSE b/LICENSE index 0e77ed4..284f6bb 100644 --- a/LICENSE +++ b/LICENSE @@ -19,6 +19,7 @@ along with this program. If not, see . Licenses Used By: + Client ====== @@ -42,16 +43,16 @@ MemWatch http://www.linkdata.se/sourcecode/memwatch/ GPL2 -minicoro --------- -https://github.com/edubart/minicoro -Public Domain or MIT No Attribution - SDL2 ---- https://www.libsdl.org/ BSD 3-Clause +SDL2_image +---------- +https://www.libsdl.org/ +BSD 3-Clause + SHA256 ------ https://github.com/ilvn/SHA256 @@ -78,6 +79,7 @@ https://github.com/bonybrown/tiny-AES128-C Unlicense + Font Converter ============== @@ -92,6 +94,7 @@ https://github.com/nothings/stb Public Domain + Server ====== diff --git a/client/assets/menu.xcf b/client/assets/menu.xcf index 74ce554..88d575d 100644 --- a/client/assets/menu.xcf +++ b/client/assets/menu.xcf @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a96e8aa41d87c8a7aed6e5ba1236bd8285742fdfdca256a1333bd8b29ce9583f -size 21301 +oid sha256:58e69ccb53b34298d69737cb3160f5bb7fafaa87747c1474b3331a6a8e1fe862 +size 25417 diff --git a/client/client.pro b/client/client.pro index 1c9bfe4..06d1b94 100644 --- a/client/client.pro +++ b/client/client.pro @@ -71,6 +71,7 @@ HEADERS = \ $$SHARED/packets.h \ src/config.h \ $$SHARED/util.h \ + src/file.h \ src/gui/msgbox.h \ src/gui/timer.h \ src/hangup.h \ @@ -123,6 +124,7 @@ SOURCES = \ $$SHARED/thirdparty/tiny-AES-c/aes.c \ $$SHARED/thirdparty/tiny-AES128-C/pkcs7_padding.c \ $$SHARED/memory.c \ + src/file.c \ src/gui/msgbox.c \ src/gui/timer.c \ src/hangup.c \ diff --git a/client/src/file.c b/client/src/file.c new file mode 100644 index 0000000..c7edd04 --- /dev/null +++ b/client/src/file.c @@ -0,0 +1,188 @@ +/* + * Kangaroo Punch MultiPlayer Game Server Mark II + * Copyright (C) 2020-2021 Scott Duensing + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +#include "network.h" +#include "packet.h" + +#include "taglist.h" +#include "window.h" +#include "label.h" + +#include "file.h" + + +static uint8_t _channel = 0; +static uint8_t _dialogVisible = 0; +static int16_t _index = -1; +static char **_fileList = NULL; +static FILE *_handle = NULL; +static fileCallback _done = NULL; +static WindowT *_winFile = NULL; +static LabelT *_lblFile = NULL; + + +static void fileCheckNext(void); +static void fileShowDialog(void); +static void fileShowError(char *message); +static void packetHandler(PacketDecodeDataT *packet); + + +void fileCacheCheck(fileCallback callback, char *vpaths[]) { + _done = callback; + _fileList = vpaths; + _index = -1; + _dialogVisible = 0; + _channel = netChannelGet(packetHandler); + + fileCheckNext(); +} + + +static void fileCheckNext(void) { + PacketEncodeDataT encoded = { 0 }; + char *packetData = NULL; + uint16_t length = 0; + uint32_t temp = 0; + char *shaPointer = NULL; + static char *badSHA = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; + + _index++; + + if (_fileList[_index] == NULL) { + // End of list! + if (_dialogVisible) guiDelete(D(_winFile)); + netChannelRelease(_channel); + _done(); + return; + } + + // Check next file. + temp = FILE_REQUEST_CHECK; + shaPointer = cacheSha256Get(_fileList[_index]); + if (!shaPointer) shaPointer = badSHA; + packetData = packetContentPack(&length, "iss", temp, shaPointer, _fileList[_index]); + logWrite("Checking [%d] [%s] [%s]\n", temp, shaPointer, _fileList[_index]); + encoded.control = PACKET_CONTROL_DAT; + encoded.packetType = PACKET_TYPE_FILE_REQUEST; + encoded.channel = _channel; + encoded.encrypt = 0; + packetEncode(__packetThreadData, &encoded, packetData, length); + packetSend(__packetThreadData, &encoded); + DEL(packetData); +} + + +static void fileShowDialog(void) { + TagItemT uiFile[] = { + T_START, + T_WINDOW, O(_winFile), + T_TITLE, P("Updating Cache"), + T_WIDTH, 200, T_HEIGHT, 100, + T_LABEL, O(_lblFile), + T_X, 41, T_Y, 25, + T_TITLE, P("Downloading..."), + T_LABEL, T_DONE, + T_WINDOW, T_DONE, + T_END + }; + + tagListRun(uiFile); + _dialogVisible = 1; +} + + +static void fileShowError(char *message) { + // ***TODO*** Handle error. This is fatal. + if (_dialogVisible) guiDelete(D(_winFile)); + netChannelRelease(_channel); +} + + +static void packetHandler(PacketDecodeDataT *packet) { + FileResponseT response = 0; + uint32_t fileLength = 0; + PacketEncodeDataT encoded = { 0 }; + char *packetData = NULL; + uint16_t length = 0; + uint32_t temp = 0; + + logWrite("Got packet %d\n", packet->packetType); + + if (packet->packetType == PACKET_TYPE_FILE_RESPONSE) { + // Extract the response type. We get more data later. + packetContentUnpack(packet->data, "i", &response); + logWrite("Got response %d [%d bytes]\n", response, sizeof(response)); + switch (response) { + case FILE_RESPONSE_UNKNOWN: + logWrite("Unknown file transfer response.\n"); + fileShowError("Unknown file transfer response."); + break; + + case FILE_RESPONSE_OKAY: + // This file is already up-to-date, move on to next. + logWrite("Got FILE_RESPONSE_OKAY\n"); + fileCheckNext(); + break; + + case FILE_RESPONSE_SEND: + // Get file size. + logWrite("Got FILE_RESPONSE_SEND\n"); + packetContentUnpack(packet->data, "ii", &response, &fileLength); + // Receive new file data. + if (!_dialogVisible) fileShowDialog(); + // Are we starting a new file? + if (_handle == NULL) { + logWrite("Opening [%s]\n", _fileList[_index]); + _handle = cacheFOpen(_fileList[_index], "wb"); + if (!_handle) { + fileShowError("Unable to write to cache."); + break; + } + } + // Write data to file, skipping first two integers stored in packet. + logWrite("Writing %d bytes to [%s]\n", packet->length - 8, _fileList[_index]); + fwrite(&packet->data[8], packet->length - 8, 1, _handle); + // Is this file complete? + if (ftell(_handle) >= fileLength) { + logWrite("Closing [%s]\n", _fileList[_index]); + // Close this file. + cacheFClose(_handle); + _handle = NULL; + // Next file! + fileCheckNext(); + } else { + logWrite("Sending FILE_REQUEST_NEXT\n"); + // Tell the server we got this bit of data, send the next. + temp = FILE_REQUEST_NEXT; + packetData = packetContentPack(&length, "i", temp); + encoded.control = PACKET_CONTROL_DAT; + encoded.packetType = PACKET_TYPE_FILE_REQUEST; + encoded.channel = _channel; + encoded.encrypt = 0; + packetEncode(__packetThreadData, &encoded, packetData, length); + packetSend(__packetThreadData, &encoded); + DEL(packetData); + } + break; + } + } + + packetDecodeDataDestroy(&packet); +} diff --git a/client/src/file.h b/client/src/file.h new file mode 100644 index 0000000..27c66ab --- /dev/null +++ b/client/src/file.h @@ -0,0 +1,34 @@ +/* + * Kangaroo Punch MultiPlayer Game Server Mark II + * Copyright (C) 2020-2021 Scott Duensing + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +#ifndef FILE_H +#define FILE_H + + +#include "os.h" + + +typedef void (*fileCallback)(void); + + +void fileCacheCheck(fileCallback callback, char *vpaths[]); + + +#endif // FILE_H diff --git a/client/src/main.c b/client/src/main.c index fde7705..071d145 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -292,8 +292,6 @@ static void tableSave(void) { } -void menuShow(void); - int main(int argc, char *argv[]) { if (startup(argc, argv)) return 1; @@ -301,8 +299,7 @@ int main(int argc, char *argv[]) { // Perform "first run" setup tasks or start the client? if (hasValidSettings()) { // We have what we need, start the client. - //welcomeShow(); - menuShow(); + welcomeShow(); } else { // Run the setup. settingsShow(checkSettings); diff --git a/client/src/menu.c b/client/src/menu.c index 40d0b3c..e972c9b 100644 --- a/client/src/menu.c +++ b/client/src/menu.c @@ -30,11 +30,13 @@ #include "menu.h" #include "hangup.h" #include "welcome.h" +#include "file.h" static void btnLogoffClick(WidgetT *widget); static void btnOptionsClick(WidgetT *widget); static void btnMsgBoxLogoff(MsgBoxButtonT button); +static void menuFilesReady(void); static void picDoorClick(WidgetT *widget); static void setButtons(uint8_t enabled); @@ -67,8 +69,6 @@ static void btnMsgBoxLogoff(MsgBoxButtonT button) { } - - static void btnOptionsClick(WidgetT *widget) { (void)widget; // ***TODO*** @@ -76,7 +76,10 @@ static void btnOptionsClick(WidgetT *widget) { } -void menuShow(void) { +static void menuFilesReady(void) { + char *dos = strdup(cacheFilenameGet("menu:dosgames.png")); + char *door = strdup(cacheFilenameGet("menu:doorgames.png")); + char *fiction = strdup(cacheFilenameGet("menu:interactivefiction.png")); TagItemT uiMenu[] = { T_START, @@ -86,18 +89,18 @@ void menuShow(void) { T_WIDTH, 350, T_HEIGHT, 275, T_PICTURE, O(_picDOS), - T_FILENAME, P("dos.png"), + T_FILENAME, P(dos), T_X, 18, T_Y, 18, T_PICTURE, T_DONE, T_PICTURE, O(_picDoor), - T_FILENAME, P("door.png"), + T_FILENAME, P(door), T_X, 18, T_Y, 77, T_CLICK, P(picDoorClick), T_PICTURE, T_DONE, T_PICTURE, O(_picIF), - T_FILENAME, P("if.png"), + T_FILENAME, P(fiction), T_X, 18, T_Y, 136, T_PICTURE, T_DONE, @@ -118,6 +121,21 @@ void menuShow(void) { }; tagListRun(uiMenu); + + DEL(fiction); + DEL(door); + DEL(dos); +} + + +void menuShow(void) { + char *fileList[] = { + "menu:dosgames.png", + "menu:doorgames.png", + "menu:interactivefiction.png", + NULL + }; + fileCacheCheck(menuFilesReady, fileList); } diff --git a/client/src/system/cache.h b/client/src/system/cache.h index 5410eb8..f6685bb 100644 --- a/client/src/system/cache.h +++ b/client/src/system/cache.h @@ -25,7 +25,7 @@ #include "os.h" -#define CACHE_VIRTUAL_PATH_MAX 512 +#define CACHE_VIRTUAL_PATH_MAX 512 // Should match setting in the server's client/file.h typedef struct CachePreMakeListS { diff --git a/kpmpgsmkii.pro b/kpmpgsmkii.pro index 01169bb..f82183f 100644 --- a/kpmpgsmkii.pro +++ b/kpmpgsmkii.pro @@ -21,8 +21,8 @@ TEMPLATE = subdirs CONFIG *= ORDERED SUBDIRS = \ - client -# server + client \ + server # precache # font # primes diff --git a/server/server.pro b/server/server.pro index c86a751..3fb3a35 100644 --- a/server/server.pro +++ b/server/server.pro @@ -61,7 +61,8 @@ HEADERS = \ src/os.h \ src/rest.h \ src/database.h \ - src/server.h + src/server.h \ + src/settings.h SOURCES = \ $$SHARED/thirdparty/memwatch/memwatch.c \ @@ -85,7 +86,8 @@ SOURCES = \ src/network.c \ src/rest.c \ src/database.c \ - src/server.c + src/server.c \ + src/settings.c LIBS = \ -L/usr/lib/x86_64-linux-gnu/ \ diff --git a/server/src/client.h b/server/src/client.h index df1dbe9..6849667 100644 --- a/server/src/client.h +++ b/server/src/client.h @@ -52,6 +52,8 @@ typedef struct ClientThreadS { void *peer; // User State Stuff. uint8_t authenticated; + FILE *handle; // ***TODO*** Needs to support more than one file transfer at a time. + uint32_t fileSize; // Length of current file being transferred. } ClientThreadT; diff --git a/server/src/client/file.c b/server/src/client/file.c index 49890ac..1ded8f9 100644 --- a/server/src/client/file.c +++ b/server/src/client/file.c @@ -19,14 +19,13 @@ #include "database.h" +#include "settings.h" #include "file.h" -static void clientApiFileRequestCacheCheck(ClientThreadT *client, PacketDecodeDataT *data); -static void clientApiFileRequestClose(ClientThreadT *client, PacketDecodeDataT *data); -static void clientApiFileRequestOpen(ClientThreadT *client, PacketDecodeDataT *data); -static void clientApiFileRequestRead(ClientThreadT *client, PacketDecodeDataT *data); +static void clientApiFileRequestCheck(ClientThreadT *client, PacketDecodeDataT *data); +static void clientApiFileRequestNext(ClientThreadT *client, PacketDecodeDataT *data); void clientApiFileRequest(ClientThreadT *client, PacketDecodeDataT *data) { @@ -36,30 +35,23 @@ void clientApiFileRequest(ClientThreadT *client, PacketDecodeDataT *data) { uint16_t length = 0; // Must be logged in to do file operations. - if (!client->authenticated) { + if (client->authenticated) { // Extract the request type. We get more data later. packetContentUnpack(data->data, "i", &request); switch (request) { - case FILE_REQUEST_CACHE_CHECK: - clientApiFileRequestCacheCheck(client, data); + case FILE_REQUEST_CHECK: + clientApiFileRequestCheck(client, data); break; - case FILE_REQUEST_OPEN: - clientApiFileRequestOpen(client, data); - break; - - case FILE_REQUEST_READ: - clientApiFileRequestRead(client, data); - break; - - case FILE_REQUEST_CLOSE: - clientApiFileRequestClose(client, data); + case FILE_REQUEST_NEXT: + clientApiFileRequestNext(client, data); break; default: - // No idea what they want. First value is 0 for fail, 1 for success. - packetData = packetContentPack(&length, "i", 0); + logWrite("Got FILE_REQUEST_UNKNOWN [%d] %d\n\r", request, data->length); + // No idea what they want. + packetData = packetContentPack(&length, "i", FILE_RESPONSE_UNKNOWN); // Build packet. encoded.control = PACKET_CONTROL_DAT; encoded.packetType = PACKET_TYPE_FILE_RESPONSE; @@ -76,51 +68,99 @@ void clientApiFileRequest(ClientThreadT *client, PacketDecodeDataT *data) { } -static void clientApiFileRequestCacheCheck(ClientThreadT *client, PacketDecodeDataT *data) { - FileRequestsT request = 0; - char *path = NULL; - char *sha256 = NULL; - PacketEncodeDataT encoded = { 0 }; - char *packetData = NULL; - uint16_t length = 0; +static void clientApiFileRequestCheck(ClientThreadT *client, PacketDecodeDataT *data) { + FileRequestsT request = 0; + char *path = NULL; + char *sha256 = NULL; + PacketEncodeDataT encoded = { 0 }; + char *packetData = NULL; + uint16_t length = 0; + char buffer[1024] = { 0 }; + char buffer2[1024] = { 0 }; + uint32_t temp = 0; + + logWrite("Got FILE_REQUEST_CHECK\n\r"); // Extract the request. - packetContentUnpack(data->data, "i", &request, &path, &sha256); - - // Look up the entry and compare SHA. + packetContentUnpack(data->data, "iss", &request, &sha256, &path); + logWrite("[%s] [%s]\n\r", sha256, path); + // Look up the entry and compare SHA256. + dbFileSha256Get(path, buffer, FILE_VIRTUAL_PATH_MAX); + if (strcasecmp(sha256, buffer) == 0) { + logWrite("File is current. Sending FILE_RESPONSE_OKAY.\n\r"); + // File is already current on client. + temp = FILE_RESPONSE_OKAY; + packetData = packetContentPack(&length, "i", temp); + encoded.control = PACKET_CONTROL_DAT; + encoded.packetType = PACKET_TYPE_FILE_RESPONSE; + encoded.channel = data->channel; + encoded.encrypt = 0; + packetEncode(client->packetThreadData, &encoded, packetData, length); + packetSend(client->packetThreadData, &encoded); + DEL(packetData); + } else { + logWrite("File needs updated.\n\r"); + // Get real path. + dbFileRealPathGet(path, buffer, FILE_VIRTUAL_PATH_MAX); + snprintf(buffer2, 1024, "%s%s", __settingsFile, buffer); + // Open file & get file size. + client->handle = fopen(buffer2, "rb"); + if (!client->handle) { + // ***TODO*** Handle error + } + fseek(client->handle, 0, SEEK_END); + client->fileSize = ftell(client->handle); + fseek(client->handle, 0, SEEK_SET); + // Start sending new file to client. + packetData = (char *)malloc(PACKET_MAX); + if (!packetData) { + // ***TODO*** Handle error + } + logWrite("Size is %d\n\r", client->fileSize); + clientApiFileRequestNext(client, data); + } } -static void clientApiFileRequestClose(ClientThreadT *client, PacketDecodeDataT *data) { - FileRequestsT request = 0; +static void clientApiFileRequestNext(ClientThreadT *client, PacketDecodeDataT *data) { + uint32_t temp = 0; PacketEncodeDataT encoded = { 0 }; - char *packetData = NULL; - uint16_t length = 0; - -} - - -static void clientApiFileRequestOpen(ClientThreadT *client, PacketDecodeDataT *data) { - FileRequestsT request = 0; - char *path = NULL; - char *sha256 = NULL; - PacketEncodeDataT encoded = { 0 }; - char *packetData = NULL; - uint16_t length = 0; - - // Extract the request. - packetContentUnpack(data->data, "i", &request, &path); - - // Look up the entry and fetch SHA. - -} - - -static void clientApiFileRequestRead(ClientThreadT *client, PacketDecodeDataT *data) { - FileRequestsT request = 0; - PacketEncodeDataT encoded = { 0 }; - char *packetData = NULL; uint16_t length = 0; + uint16_t dataSize = 0; + char packetData[PACKET_MAX]; + // Do we have an open file? + if (client->handle) { + logWrite("Sending FILE_RESPONSE_SEND.\n\r"); + // Add response type. + temp = FILE_RESPONSE_SEND; + memcpy(packetData, &temp, sizeof(int32_t)); + + // Add file length. + memcpy(&packetData[4], &client->fileSize, sizeof(int32_t)); + + // Add file data. + dataSize = client->fileSize - ftell(client->handle); + if (dataSize > PACKET_MAX - 8) { + // File is larger than a packet size. + dataSize = PACKET_MAX - 8; // 8 for two integers of response data. + fread(&packetData[8], dataSize, 1, client->handle); + } else { + // File remains will fit in this packet. + fread(&packetData[8], dataSize, 1, client->handle); + fclose(client->handle); + client->handle = NULL; + } + logWrite("Sending %d bytes\n\r", dataSize); + + // Build packet. + encoded.control = PACKET_CONTROL_DAT; + encoded.packetType = PACKET_TYPE_FILE_RESPONSE; + encoded.channel = data->channel; + encoded.encrypt = 0; + packetEncode(client->packetThreadData, &encoded, packetData, length); + // Send it. + packetSend(client->packetThreadData, &encoded); + } } diff --git a/server/src/client/file.h b/server/src/client/file.h index 1fe1d76..fd73c1a 100644 --- a/server/src/client/file.h +++ b/server/src/client/file.h @@ -26,6 +26,9 @@ #include "client.h" +#define FILE_VIRTUAL_PATH_MAX 512 // Should match setting in the client's cache.h + + void clientApiFileRequest(ClientThreadT *client, PacketDecodeDataT *data); diff --git a/server/src/database.c b/server/src/database.c index d50e807..bc9bd79 100644 --- a/server/src/database.c +++ b/server/src/database.c @@ -65,6 +65,102 @@ uint8_t dbDisconnect(void) { } +uint8_t dbFileRealPathGet(char *vpath, char *value, uint32_t max) { + char statement[STATEMENT_MAX]; + char *p = statement; + MYSQL_RES *result = NULL; + MYSQL_ROW row; + int count; + + pthread_mutex_lock(&_mutex); + + if (!_sql) { + pthread_mutex_unlock(&_mutex); + return FAIL; + } + + p += sprintf(p, "SELECT realPath FROM files WHERE virtualPath='"); + p += mysql_real_escape_string(_sql, p, vpath, strlen(vpath)); + p += sprintf(p, "'"); + if (mysql_real_query(_sql, statement, p - statement) != 0) { + logWrite("dbFileRealPathGet: %s\n", mysql_error(_sql)); + pthread_mutex_unlock(&_mutex); + return FAIL; + } + + result = mysql_store_result(_sql); + count = mysql_num_rows(result); + if (count != 1) { + logWrite("dbFileRealPathGet: Wrong number of rows returned: %d.\n", count); + mysql_free_result(result); + pthread_mutex_unlock(&_mutex); + return FAIL; + } + + if ((row = mysql_fetch_row(result)) == NULL) { + logWrite("dbFileRealPathGet: %s\n", mysql_error(_sql)); + pthread_mutex_unlock(&_mutex); + return FAIL; + } + + strncpy(value, row[0], max); + + mysql_free_result(result); + + pthread_mutex_unlock(&_mutex); + + return SUCCESS; +} + + +uint8_t dbFileSha256Get(char *vpath, char *value, uint32_t max) { + char statement[STATEMENT_MAX]; + char *p = statement; + MYSQL_RES *result = NULL; + MYSQL_ROW row; + int count; + + pthread_mutex_lock(&_mutex); + + if (!_sql) { + pthread_mutex_unlock(&_mutex); + return FAIL; + } + + p += sprintf(p, "SELECT sha256 FROM files WHERE virtualPath='"); + p += mysql_real_escape_string(_sql, p, vpath, strlen(vpath)); + p += sprintf(p, "'"); + if (mysql_real_query(_sql, statement, p - statement) != 0) { + logWrite("dbFileSha256Get: %s\n", mysql_error(_sql)); + pthread_mutex_unlock(&_mutex); + return FAIL; + } + + result = mysql_store_result(_sql); + count = mysql_num_rows(result); + if (count != 1) { + logWrite("dbFileSha256Get: Wrong number of rows returned: %d.\n", count); + mysql_free_result(result); + pthread_mutex_unlock(&_mutex); + return FAIL; + } + + if ((row = mysql_fetch_row(result)) == NULL) { + logWrite("dbFileSha256Get: %s\n", mysql_error(_sql)); + pthread_mutex_unlock(&_mutex); + return FAIL; + } + + strncpy(value, row[0], max); + + mysql_free_result(result); + + pthread_mutex_unlock(&_mutex); + + return SUCCESS; +} + + uint8_t dbSettingsStringGet(char *host, char *key, char *value, uint32_t max) { char statement[STATEMENT_MAX]; char *p = statement; diff --git a/server/src/database.h b/server/src/database.h index 80a74a7..ebb45fb 100644 --- a/server/src/database.h +++ b/server/src/database.h @@ -36,6 +36,8 @@ typedef struct DBTableS { uint8_t dbConnect(char *host, uint16_t port, char *database, char *user, char *password); uint8_t dbDisconnect(void); +uint8_t dbFileRealPathGet(char *vpath, char *value, uint32_t max); +uint8_t dbFileSha256Get(char *vpath, char *value, uint32_t max); uint8_t dbSettingsStringGet(char *host, char *key, char *value, uint32_t max); uint8_t dbSettingsValueGet(char *host, char *key, int32_t *value); uint8_t dbTableGet(char *which, DBTableT ***table); diff --git a/server/src/main.c b/server/src/main.c index 508d6cf..b398f2a 100644 --- a/server/src/main.c +++ b/server/src/main.c @@ -24,6 +24,7 @@ #include "server.h" #include "rest.h" #include "database.h" +#include "settings.h" #include "thirdparty/ini/src/ini.h" @@ -102,13 +103,6 @@ int main(int argc, char *argv[]) { char *configFile = NULL; char hostname[256] = { 0 }; - char settingsFile[DB_CONFIG_ITEM_SIZE] = { 0 }; - char settingsRest[DB_CONFIG_ITEM_SIZE] = { 0 }; - char settingsUser[DB_CONFIG_ITEM_SIZE] = { 0 }; - char settingsPass[DB_CONFIG_ITEM_SIZE] = { 0 }; - int64_t settingsMaxClients = 0; - int64_t settingsPortNumber = 0; - int64_t settingsClientVersion = 0; (void)argc; @@ -130,22 +124,22 @@ int main(int argc, char *argv[]) { } // Fetch settings needed to start server. - if (!dbSettingsValueGet(hostname, "maxClients", (int32_t *)&settingsMaxClients)) utilDie("Unable to load maxClients.\n"); - if (!dbSettingsValueGet(hostname, "portNumber", (int32_t *)&settingsPortNumber)) utilDie("Unable to load portNumber.\n"); - if (!dbSettingsValueGet(hostname, "clientVersion", (int32_t *)&settingsClientVersion)) utilDie("Unable to load clientVersion.\n"); - if (!dbSettingsStringGet(hostname, "fileLocation", settingsFile, DB_CONFIG_ITEM_SIZE)) utilDie("Unable to load file location.\n"); - if (!dbSettingsStringGet(hostname, "restEndpoint", settingsRest, DB_CONFIG_ITEM_SIZE)) utilDie("Unable to load REST URL.\n"); - if (!dbSettingsStringGet(hostname, "restUser", settingsUser, DB_CONFIG_ITEM_SIZE)) utilDie("Unable to load REST user.\n"); - if (!dbSettingsStringGet(hostname, "restPassword", settingsPass, DB_CONFIG_ITEM_SIZE)) utilDie("Unable to load REST password.\n"); + if (!dbSettingsValueGet(hostname, "maxClients", (int32_t *)&__settingsMaxClients)) utilDie("Unable to load maxClients.\n"); + if (!dbSettingsValueGet(hostname, "portNumber", (int32_t *)&__settingsPortNumber)) utilDie("Unable to load portNumber.\n"); + if (!dbSettingsValueGet(hostname, "clientVersion", (int32_t *)&__settingsClientVersion)) utilDie("Unable to load clientVersion.\n"); + if (!dbSettingsStringGet(hostname, "fileLocation", __settingsFile, DB_CONFIG_ITEM_SIZE)) utilDie("Unable to load file location.\n"); + if (!dbSettingsStringGet(hostname, "restEndpoint", __settingsRest, DB_CONFIG_ITEM_SIZE)) utilDie("Unable to load REST URL.\n"); + if (!dbSettingsStringGet(hostname, "restUser", __settingsUser, DB_CONFIG_ITEM_SIZE)) utilDie("Unable to load REST user.\n"); + if (!dbSettingsStringGet(hostname, "restPassword", __settingsPass, DB_CONFIG_ITEM_SIZE)) utilDie("Unable to load REST password.\n"); // Start up REST. - if (!restStartup(settingsRest, settingsUser, settingsPass)) { + if (!restStartup(__settingsRest, __settingsUser, __settingsPass)) { logWrite("Unable to locate REST endpoint. Web site integration disabled.\n"); __restAvailable = 0; } clientStartup(); - serverStartup(settingsPortNumber, settingsMaxClients); + serverStartup(__settingsPortNumber, __settingsMaxClients); logWrite("Server online.\n"); // Run Console. diff --git a/server/src/settings.c b/server/src/settings.c new file mode 100644 index 0000000..60cec6a --- /dev/null +++ b/server/src/settings.c @@ -0,0 +1,30 @@ +/* + * Kangaroo Punch MultiPlayer Game Server Mark II + * Copyright (C) 2020-2021 Scott Duensing + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +#include "settings.h" + + +char __settingsFile[DB_CONFIG_ITEM_SIZE] = { 0 }; +char __settingsRest[DB_CONFIG_ITEM_SIZE] = { 0 }; +char __settingsUser[DB_CONFIG_ITEM_SIZE] = { 0 }; +char __settingsPass[DB_CONFIG_ITEM_SIZE] = { 0 }; +int64_t __settingsMaxClients = 0; +int64_t __settingsPortNumber = 0; +int64_t __settingsClientVersion = 0; diff --git a/server/src/settings.h b/server/src/settings.h new file mode 100644 index 0000000..d60fa9c --- /dev/null +++ b/server/src/settings.h @@ -0,0 +1,37 @@ +/* + * Kangaroo Punch MultiPlayer Game Server Mark II + * Copyright (C) 2020-2021 Scott Duensing + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +#ifndef SETTINGS_H +#define SETTINGS_H + +#include "os.h" +#include "database.h" + + +extern char __settingsFile[DB_CONFIG_ITEM_SIZE]; +extern char __settingsRest[DB_CONFIG_ITEM_SIZE]; +extern char __settingsUser[DB_CONFIG_ITEM_SIZE]; +extern char __settingsPass[DB_CONFIG_ITEM_SIZE]; +extern int64_t __settingsMaxClients; +extern int64_t __settingsPortNumber; +extern int64_t __settingsClientVersion; + + +#endif // SETTINGS_H diff --git a/shared/packets.h b/shared/packets.h index 650156c..83c69e6 100644 --- a/shared/packets.h +++ b/shared/packets.h @@ -60,13 +60,25 @@ typedef enum PacketTypeE { PACKET_TYPE_COUNT } PacketTypeT; +/* + * Client -> PACKET_TYPE_FILE_REQUEST FILE_REQUEST_CHECK SHA256 VPATH + * Server -> PACKET_TYPE_FILE_RESPONSE FILE_RESPONSE_OKAY + * - or - + * Server -> PACKET_TYPE_FILE_RESPONSE FILE_RESPONSE_SEND (4 bytes length) (data...) + * Client -> PACKET_TYPE_FILE_REQUEST FILE_REQUEST_NEXT + * (Repeat until received data matches the length.) + */ typedef enum FileRequestsE { FILE_REQUEST_UNKNOWN = 0, - FILE_REQUEST_CACHE_CHECK, - FILE_REQUEST_OPEN, - FILE_REQUEST_READ, - FILE_REQUEST_CLOSE + FILE_REQUEST_CHECK, + FILE_REQUEST_NEXT } FileRequestsT; +typedef enum FileResponseE { + FILE_RESPONSE_UNKNOWN = 0, + FILE_RESPONSE_OKAY, + FILE_RESPONSE_SEND +} FileResponseT; + #endif // PACKETS_H