File transfer code in. Still being debugged.
This commit is contained in:
parent
029a37d87d
commit
1a9fcdaaf3
20 changed files with 560 additions and 99 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -9,6 +9,7 @@ bin/
|
|||
obj/
|
||||
retired/
|
||||
test/
|
||||
doors/
|
||||
|
||||
*/out/
|
||||
|
||||
|
|
13
LICENSE
13
LICENSE
|
@ -19,6 +19,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
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
|
||||
======
|
||||
|
||||
|
|
BIN
client/assets/menu.xcf
(Stored with Git LFS)
BIN
client/assets/menu.xcf
(Stored with Git LFS)
Binary file not shown.
|
@ -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 \
|
||||
|
|
188
client/src/file.c
Normal file
188
client/src/file.c
Normal 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
34
client/src/file.h
Normal 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
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -21,8 +21,8 @@ TEMPLATE = subdirs
|
|||
CONFIG *= ORDERED
|
||||
|
||||
SUBDIRS = \
|
||||
client
|
||||
# server
|
||||
client \
|
||||
server
|
||||
# precache
|
||||
# font
|
||||
# primes
|
||||
|
|
|
@ -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/ \
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
30
server/src/settings.c
Normal file
30
server/src/settings.c
Normal 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
37
server/src/settings.h
Normal 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
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue