File transfer code in. Still being debugged.

This commit is contained in:
Scott Duensing 2022-02-13 20:38:11 -06:00
parent 029a37d87d
commit 1a9fcdaaf3
20 changed files with 560 additions and 99 deletions

1
.gitignore vendored
View file

@ -9,6 +9,7 @@ bin/
obj/ obj/
retired/ retired/
test/ test/
doors/
*/out/ */out/

13
LICENSE
View file

@ -19,6 +19,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
Licenses Used By: Licenses Used By:
Client Client
====== ======
@ -42,16 +43,16 @@ MemWatch
http://www.linkdata.se/sourcecode/memwatch/ http://www.linkdata.se/sourcecode/memwatch/
GPL2 GPL2
minicoro
--------
https://github.com/edubart/minicoro
Public Domain or MIT No Attribution
SDL2 SDL2
---- ----
https://www.libsdl.org/ https://www.libsdl.org/
BSD 3-Clause BSD 3-Clause
SDL2_image
----------
https://www.libsdl.org/
BSD 3-Clause
SHA256 SHA256
------ ------
https://github.com/ilvn/SHA256 https://github.com/ilvn/SHA256
@ -78,6 +79,7 @@ https://github.com/bonybrown/tiny-AES128-C
Unlicense Unlicense
Font Converter Font Converter
============== ==============
@ -92,6 +94,7 @@ https://github.com/nothings/stb
Public Domain Public Domain
Server Server
====== ======

BIN
client/assets/menu.xcf (Stored with Git LFS)

Binary file not shown.

View file

@ -71,6 +71,7 @@ HEADERS = \
$$SHARED/packets.h \ $$SHARED/packets.h \
src/config.h \ src/config.h \
$$SHARED/util.h \ $$SHARED/util.h \
src/file.h \
src/gui/msgbox.h \ src/gui/msgbox.h \
src/gui/timer.h \ src/gui/timer.h \
src/hangup.h \ src/hangup.h \
@ -123,6 +124,7 @@ SOURCES = \
$$SHARED/thirdparty/tiny-AES-c/aes.c \ $$SHARED/thirdparty/tiny-AES-c/aes.c \
$$SHARED/thirdparty/tiny-AES128-C/pkcs7_padding.c \ $$SHARED/thirdparty/tiny-AES128-C/pkcs7_padding.c \
$$SHARED/memory.c \ $$SHARED/memory.c \
src/file.c \
src/gui/msgbox.c \ src/gui/msgbox.c \
src/gui/timer.c \ src/gui/timer.c \
src/hangup.c \ src/hangup.c \

188
client/src/file.c Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*
*/
#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);
}

34
client/src/file.h Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*
*/
#ifndef FILE_H
#define FILE_H
#include "os.h"
typedef void (*fileCallback)(void);
void fileCacheCheck(fileCallback callback, char *vpaths[]);
#endif // FILE_H

View file

@ -292,8 +292,6 @@ static void tableSave(void) {
} }
void menuShow(void);
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
if (startup(argc, argv)) return 1; 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? // Perform "first run" setup tasks or start the client?
if (hasValidSettings()) { if (hasValidSettings()) {
// We have what we need, start the client. // We have what we need, start the client.
//welcomeShow(); welcomeShow();
menuShow();
} else { } else {
// Run the setup. // Run the setup.
settingsShow(checkSettings); settingsShow(checkSettings);

View file

@ -30,11 +30,13 @@
#include "menu.h" #include "menu.h"
#include "hangup.h" #include "hangup.h"
#include "welcome.h" #include "welcome.h"
#include "file.h"
static void btnLogoffClick(WidgetT *widget); static void btnLogoffClick(WidgetT *widget);
static void btnOptionsClick(WidgetT *widget); static void btnOptionsClick(WidgetT *widget);
static void btnMsgBoxLogoff(MsgBoxButtonT button); static void btnMsgBoxLogoff(MsgBoxButtonT button);
static void menuFilesReady(void);
static void picDoorClick(WidgetT *widget); static void picDoorClick(WidgetT *widget);
static void setButtons(uint8_t enabled); static void setButtons(uint8_t enabled);
@ -67,8 +69,6 @@ static void btnMsgBoxLogoff(MsgBoxButtonT button) {
} }
static void btnOptionsClick(WidgetT *widget) { static void btnOptionsClick(WidgetT *widget) {
(void)widget; (void)widget;
// ***TODO*** // ***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[] = { TagItemT uiMenu[] = {
T_START, T_START,
@ -86,18 +89,18 @@ void menuShow(void) {
T_WIDTH, 350, T_HEIGHT, 275, T_WIDTH, 350, T_HEIGHT, 275,
T_PICTURE, O(_picDOS), T_PICTURE, O(_picDOS),
T_FILENAME, P("dos.png"), T_FILENAME, P(dos),
T_X, 18, T_Y, 18, T_X, 18, T_Y, 18,
T_PICTURE, T_DONE, T_PICTURE, T_DONE,
T_PICTURE, O(_picDoor), T_PICTURE, O(_picDoor),
T_FILENAME, P("door.png"), T_FILENAME, P(door),
T_X, 18, T_Y, 77, T_X, 18, T_Y, 77,
T_CLICK, P(picDoorClick), T_CLICK, P(picDoorClick),
T_PICTURE, T_DONE, T_PICTURE, T_DONE,
T_PICTURE, O(_picIF), T_PICTURE, O(_picIF),
T_FILENAME, P("if.png"), T_FILENAME, P(fiction),
T_X, 18, T_Y, 136, T_X, 18, T_Y, 136,
T_PICTURE, T_DONE, T_PICTURE, T_DONE,
@ -118,6 +121,21 @@ void menuShow(void) {
}; };
tagListRun(uiMenu); 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);
} }

View file

@ -25,7 +25,7 @@
#include "os.h" #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 { typedef struct CachePreMakeListS {

View file

@ -21,8 +21,8 @@ TEMPLATE = subdirs
CONFIG *= ORDERED CONFIG *= ORDERED
SUBDIRS = \ SUBDIRS = \
client client \
# server server
# precache # precache
# font # font
# primes # primes

View file

@ -61,7 +61,8 @@ HEADERS = \
src/os.h \ src/os.h \
src/rest.h \ src/rest.h \
src/database.h \ src/database.h \
src/server.h src/server.h \
src/settings.h
SOURCES = \ SOURCES = \
$$SHARED/thirdparty/memwatch/memwatch.c \ $$SHARED/thirdparty/memwatch/memwatch.c \
@ -85,7 +86,8 @@ SOURCES = \
src/network.c \ src/network.c \
src/rest.c \ src/rest.c \
src/database.c \ src/database.c \
src/server.c src/server.c \
src/settings.c
LIBS = \ LIBS = \
-L/usr/lib/x86_64-linux-gnu/ \ -L/usr/lib/x86_64-linux-gnu/ \

View file

@ -52,6 +52,8 @@ typedef struct ClientThreadS {
void *peer; void *peer;
// User State Stuff. // User State Stuff.
uint8_t authenticated; 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; } ClientThreadT;

View file

@ -19,14 +19,13 @@
#include "database.h" #include "database.h"
#include "settings.h"
#include "file.h" #include "file.h"
static void clientApiFileRequestCacheCheck(ClientThreadT *client, PacketDecodeDataT *data); static void clientApiFileRequestCheck(ClientThreadT *client, PacketDecodeDataT *data);
static void clientApiFileRequestClose(ClientThreadT *client, PacketDecodeDataT *data); static void clientApiFileRequestNext(ClientThreadT *client, PacketDecodeDataT *data);
static void clientApiFileRequestOpen(ClientThreadT *client, PacketDecodeDataT *data);
static void clientApiFileRequestRead(ClientThreadT *client, PacketDecodeDataT *data);
void clientApiFileRequest(ClientThreadT *client, PacketDecodeDataT *data) { void clientApiFileRequest(ClientThreadT *client, PacketDecodeDataT *data) {
@ -36,30 +35,23 @@ void clientApiFileRequest(ClientThreadT *client, PacketDecodeDataT *data) {
uint16_t length = 0; uint16_t length = 0;
// Must be logged in to do file operations. // Must be logged in to do file operations.
if (!client->authenticated) { if (client->authenticated) {
// Extract the request type. We get more data later. // Extract the request type. We get more data later.
packetContentUnpack(data->data, "i", &request); packetContentUnpack(data->data, "i", &request);
switch (request) { switch (request) {
case FILE_REQUEST_CACHE_CHECK: case FILE_REQUEST_CHECK:
clientApiFileRequestCacheCheck(client, data); clientApiFileRequestCheck(client, data);
break; break;
case FILE_REQUEST_OPEN: case FILE_REQUEST_NEXT:
clientApiFileRequestOpen(client, data); clientApiFileRequestNext(client, data);
break;
case FILE_REQUEST_READ:
clientApiFileRequestRead(client, data);
break;
case FILE_REQUEST_CLOSE:
clientApiFileRequestClose(client, data);
break; break;
default: default:
// No idea what they want. First value is 0 for fail, 1 for success. logWrite("Got FILE_REQUEST_UNKNOWN [%d] %d\n\r", request, data->length);
packetData = packetContentPack(&length, "i", 0); // No idea what they want.
packetData = packetContentPack(&length, "i", FILE_RESPONSE_UNKNOWN);
// Build packet. // Build packet.
encoded.control = PACKET_CONTROL_DAT; encoded.control = PACKET_CONTROL_DAT;
encoded.packetType = PACKET_TYPE_FILE_RESPONSE; encoded.packetType = PACKET_TYPE_FILE_RESPONSE;
@ -76,51 +68,99 @@ void clientApiFileRequest(ClientThreadT *client, PacketDecodeDataT *data) {
} }
static void clientApiFileRequestCacheCheck(ClientThreadT *client, PacketDecodeDataT *data) { static void clientApiFileRequestCheck(ClientThreadT *client, PacketDecodeDataT *data) {
FileRequestsT request = 0; FileRequestsT request = 0;
char *path = NULL; char *path = NULL;
char *sha256 = NULL; char *sha256 = NULL;
PacketEncodeDataT encoded = { 0 }; PacketEncodeDataT encoded = { 0 };
char *packetData = NULL; char *packetData = NULL;
uint16_t length = 0; 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. // Extract the request.
packetContentUnpack(data->data, "i", &request, &path, &sha256); packetContentUnpack(data->data, "iss", &request, &sha256, &path);
logWrite("[%s] [%s]\n\r", sha256, path);
// Look up the entry and compare SHA.
// 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) { static void clientApiFileRequestNext(ClientThreadT *client, PacketDecodeDataT *data) {
FileRequestsT request = 0; uint32_t temp = 0;
PacketEncodeDataT encoded = { 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 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);
}
} }

View file

@ -26,6 +26,9 @@
#include "client.h" #include "client.h"
#define FILE_VIRTUAL_PATH_MAX 512 // Should match setting in the client's cache.h
void clientApiFileRequest(ClientThreadT *client, PacketDecodeDataT *data); void clientApiFileRequest(ClientThreadT *client, PacketDecodeDataT *data);

View file

@ -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) { uint8_t dbSettingsStringGet(char *host, char *key, char *value, uint32_t max) {
char statement[STATEMENT_MAX]; char statement[STATEMENT_MAX];
char *p = statement; char *p = statement;

View file

@ -36,6 +36,8 @@ typedef struct DBTableS {
uint8_t dbConnect(char *host, uint16_t port, char *database, char *user, char *password); uint8_t dbConnect(char *host, uint16_t port, char *database, char *user, char *password);
uint8_t dbDisconnect(void); 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 dbSettingsStringGet(char *host, char *key, char *value, uint32_t max);
uint8_t dbSettingsValueGet(char *host, char *key, int32_t *value); uint8_t dbSettingsValueGet(char *host, char *key, int32_t *value);
uint8_t dbTableGet(char *which, DBTableT ***table); uint8_t dbTableGet(char *which, DBTableT ***table);

View file

@ -24,6 +24,7 @@
#include "server.h" #include "server.h"
#include "rest.h" #include "rest.h"
#include "database.h" #include "database.h"
#include "settings.h"
#include "thirdparty/ini/src/ini.h" #include "thirdparty/ini/src/ini.h"
@ -102,13 +103,6 @@ int main(int argc, char *argv[]) {
char *configFile = NULL; char *configFile = NULL;
char hostname[256] = { 0 }; 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; (void)argc;
@ -130,22 +124,22 @@ int main(int argc, char *argv[]) {
} }
// Fetch settings needed to start server. // Fetch settings needed to start server.
if (!dbSettingsValueGet(hostname, "maxClients", (int32_t *)&settingsMaxClients)) utilDie("Unable to load maxClients.\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, "portNumber", (int32_t *)&__settingsPortNumber)) utilDie("Unable to load portNumber.\n");
if (!dbSettingsValueGet(hostname, "clientVersion", (int32_t *)&settingsClientVersion)) utilDie("Unable to load clientVersion.\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, "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, "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, "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 (!dbSettingsStringGet(hostname, "restPassword", __settingsPass, DB_CONFIG_ITEM_SIZE)) utilDie("Unable to load REST password.\n");
// Start up REST. // Start up REST.
if (!restStartup(settingsRest, settingsUser, settingsPass)) { if (!restStartup(__settingsRest, __settingsUser, __settingsPass)) {
logWrite("Unable to locate REST endpoint. Web site integration disabled.\n"); logWrite("Unable to locate REST endpoint. Web site integration disabled.\n");
__restAvailable = 0; __restAvailable = 0;
} }
clientStartup(); clientStartup();
serverStartup(settingsPortNumber, settingsMaxClients); serverStartup(__settingsPortNumber, __settingsMaxClients);
logWrite("Server online.\n"); logWrite("Server online.\n");
// Run Console. // Run Console.

30
server/src/settings.c Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*
*/
#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;

37
server/src/settings.h Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*
*/
#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

View file

@ -60,13 +60,25 @@ typedef enum PacketTypeE {
PACKET_TYPE_COUNT PACKET_TYPE_COUNT
} PacketTypeT; } 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 { typedef enum FileRequestsE {
FILE_REQUEST_UNKNOWN = 0, FILE_REQUEST_UNKNOWN = 0,
FILE_REQUEST_CACHE_CHECK, FILE_REQUEST_CHECK,
FILE_REQUEST_OPEN, FILE_REQUEST_NEXT
FILE_REQUEST_READ,
FILE_REQUEST_CLOSE
} FileRequestsT; } FileRequestsT;
typedef enum FileResponseE {
FILE_RESPONSE_UNKNOWN = 0,
FILE_RESPONSE_OKAY,
FILE_RESPONSE_SEND
} FileResponseT;
#endif // PACKETS_H #endif // PACKETS_H