From 7174d36eb934ff70a91a2315c512a2c0043ae8c2 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Fri, 14 Jan 2022 19:06:01 -0600 Subject: [PATCH] Login and Sign Up dialogs implemented. --- client/client.pro | 3 + client/data/ddialing.png | 4 +- client/src/gui/textbox.c | 22 ++- client/src/gui/textbox.h | 2 + client/src/gui/widget.c | 1 + client/src/login.c | 126 +++++++++++- client/src/main.c | 200 ++++++------------- client/src/network.c | 7 +- client/src/network.h | 3 +- client/src/signup.c | 384 ++++++++++++++++++++++++++++++++++++ client/src/signup.h | 31 +++ client/src/system/os.c | 23 +++ client/src/system/os.h | 1 + client/src/system/taglist.c | 9 +- client/src/system/taglist.h | 1 + client/src/welcome.c | 10 +- server/src/client.c | 23 ++- server/src/rest.c | 2 + server/src/server.c | 6 +- shared/packet.c | 27 ++- shared/packets.h | 22 ++- 21 files changed, 734 insertions(+), 173 deletions(-) create mode 100644 client/src/signup.c create mode 100644 client/src/signup.h create mode 100644 client/src/system/os.c diff --git a/client/client.pro b/client/client.pro index fee1e68..aba09f4 100644 --- a/client/client.pro +++ b/client/client.pro @@ -72,6 +72,7 @@ HEADERS = \ src/login.h \ src/network.h \ src/runtime.h \ + src/signup.h \ src/thirdparty/minicoro/minicoro.h \ src/system/comport.h \ src/settings.h \ @@ -119,7 +120,9 @@ SOURCES = \ src/login.c \ src/network.c \ src/settings.c \ + src/signup.c \ src/system/comport.c \ + src/system/os.c \ src/system/surface.c \ src/system/taglist.c \ $$SHARED/util.c \ diff --git a/client/data/ddialing.png b/client/data/ddialing.png index c9aef6a..3497667 100644 --- a/client/data/ddialing.png +++ b/client/data/ddialing.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:05b73b4d78f35c17dc9a770b48be09c15737122d40a18ca163d48e98bac06ab5 -size 20525 +oid sha256:88f41ed691c97f8289f600b84f0a58bbe840199e4e9fc0134c88eedd57aa5b2a +size 12587 diff --git a/client/src/gui/textbox.c b/client/src/gui/textbox.c index 788f9b7..c1806e1 100644 --- a/client/src/gui/textbox.c +++ b/client/src/gui/textbox.c @@ -58,6 +58,7 @@ WidgetT *textboxInit(WidgetT *widget, char *title) { t->value = (char *)malloc(t->maxLength); t->caret = 0; t->offset = 0; + t->password = 0; if (!t->value) return NULL; t->value[0] = 0; @@ -76,6 +77,7 @@ static void textboxKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t extende TextboxT *t = (TextboxT *)widget; uint16_t x; + uint16_t y; char *temp; (void)scancode; @@ -167,20 +169,25 @@ static void textboxKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t extende default: // Other keys if (ascii >= 32 && ascii <= 126) { + // Remember length so we can zero terminate this after the edit. + y = strlen(t->value); // Insert character, if room. - if (strlen(t->value) < (size_t)t->maxLength - 1) { + if (y < (size_t)t->maxLength - 1) { // Move existing characters over, if needed. - if (t->caret + t->offset < strlen(t->value)) { - for (x=strlen(t->value) + 1; x>t->caret + t->offset; x--) { + if (t->caret + t->offset < y) { + for (x=y + 1; x>t->caret + t->offset; x--) { t->value[x] = t->value[x-1]; } } // Place typed character at caret. t->value[t->caret + t->offset] = ascii; + y++; + // Fix zero termination. + t->value[y] = 0; // Is the caret on the right edge of the box? if (t->caret == t->visible - 1) { // Can we move the string offset? - if (t->offset < strlen(t->value)) { + if (t->offset < y) { t->offset++; } } else { @@ -267,6 +274,7 @@ static void textboxPaint(WidgetT *widget, uint8_t enabled, RectT pos) { uint16_t textY; char cursor[2] = { 0xb1, 0 }; + if (GUI_GET_FLAG(widget, WIDGET_FLAG_DIRTY) || guiFocusGet() == widget) { labelWidth = (strlen(t->title) * fontWidthGet(_guiFont)) + _guiMetric[METRIC_TEXTBOX_PADDING]; valueWidth = (t->visible * fontWidthGet(_guiFont)) + (_guiMetric[METRIC_TEXTBOX_HORIZONTAL_PADDING] * 2); @@ -288,6 +296,7 @@ static void textboxPaint(WidgetT *widget, uint8_t enabled, RectT pos) { // Draw value. draw = strdup(&t->value[t->offset]); if (strlen(draw) > t->visible) draw[t->visible] = 0; + if (t->password != 0) memset(draw, t->password, strlen(draw)); fontRender(_guiFont, draw, _guiColor[COLOR_TEXTBOX_TEXT], _guiColor[COLOR_TEXTBOX_BACKGROUND], textX, textY); free(draw); @@ -302,6 +311,11 @@ static void textboxPaint(WidgetT *widget, uint8_t enabled, RectT pos) { } +void textboxPasswordCharacterSet(TextboxT *textbox, char c) { + textbox->password = c; +} + + void textboxTitleSet(TextboxT *textbox, char *title) { if (textbox->title) free(textbox->title); textbox->title = strdup(title); diff --git a/client/src/gui/textbox.h b/client/src/gui/textbox.h index 1703e46..cac029a 100644 --- a/client/src/gui/textbox.h +++ b/client/src/gui/textbox.h @@ -30,6 +30,7 @@ typedef struct TextboxS { WidgetT base; // Must be first in every widget char *title; char *value; + char password; // Character to use to mask passwords uint16_t maxLength; // Maximum length of value + 1 uint16_t caret; // Cursor position in control (caret + offset = cursor positition in value) uint16_t offset; // First character position to display in control @@ -40,6 +41,7 @@ typedef struct TextboxS { WidgetT *textboxInit(WidgetT *widget, char *title); void textboxLengthMaxSet(TextboxT *textbox, uint16_t length); TextboxT *textboxNew(uint16_t x, uint16_t y, uint16_t w, char *title); +void textboxPasswordCharacterSet(TextboxT *textbox, char c); void textboxTitleSet(TextboxT *textbox, char *title); char *textboxValueGet(TextboxT *textbox); void textboxValueSet(TextboxT *textbox, char *value); diff --git a/client/src/gui/widget.c b/client/src/gui/widget.c index 4e899a4..ec5f8ed 100644 --- a/client/src/gui/widget.c +++ b/client/src/gui/widget.c @@ -28,6 +28,7 @@ uint8_t widgetEnableGet(WidgetT *widget) { void widgetEnableSet(WidgetT *widget, uint8_t enabled) { + // ***TODO*** Should also set children. // We did "DISABLED" for the flag so that "flags = 0" (the default) would enable widgets. if (enabled) { GUI_CLEAR_FLAG(widget, WIDGET_FLAG_DISABLED); diff --git a/client/src/login.c b/client/src/login.c index c32e106..7baa984 100644 --- a/client/src/login.c +++ b/client/src/login.c @@ -18,13 +18,103 @@ */ -#include "login.h" +#include "textbox.h" +#include "button.h" +#include "msgbox.h" +#include "config.h" +#include "comport.h" +#include "network.h" #include "taglist.h" +#include "timer.h" #include "task.h" +#include "login.h" +#include "signup.h" +#include "welcome.h" + static WindowT *_winLogin = NULL; +static TextboxT *_txtUser = NULL; +static TextboxT *_txtPass = NULL; +static ButtonT *_btnCancel = NULL; +static ButtonT *_btnSignUp = NULL; +static ButtonT *_btnLogin = NULL; + + +static void btnCancelClick(WidgetT *widget); +static void btnSignUpClick(WidgetT *widget); +static void btnLoginClick(WidgetT *widget); +static void btnMsgBoxCancel(MsgBoxButtonT button); +static void setButtons(uint8_t enabled); +static void taskDisconnect(void *data); + + +static void btnCancelClick(WidgetT *widget) { + (void)widget; + + // ***TODO*** This should disable the entire dialog instead of just the buttons. Need to finish the Enable GUI code first. + + setButtons(0); + msgBoxTwo("Cancel?", MSGBOX_ICON_QUESTION, "Cancel login?\n \nThis will disconnect you from the server.", "Okay", btnMsgBoxCancel, "Cancel", btnMsgBoxCancel); +} + + +static void btnSignUpClick(WidgetT *widget) { + (void)widget; + + guiDelete(D(_winLogin)); + taskCreate(taskSignUp, NULL); +} + + +static void btnLoginClick(WidgetT *widget) { + (void)widget; + +} + + +static void btnMsgBoxCancel(MsgBoxButtonT button) { + + if (button == MSGBOX_BUTTON_ONE) { + guiDelete(D(_winLogin)); + taskCreate(taskDisconnect, taskWelcome); + } else { + setButtons(1); + } +} + + +static void setButtons(uint8_t enabled) { + widgetEnableSet(W(_btnCancel), enabled); + widgetEnableSet(W(_btnSignUp), enabled); + widgetEnableSet(W(_btnLogin), enabled); +} + + +static void taskDisconnect(void *nextTask) { + PacketEncodeDataT encoded = { 0 }; + int16_t timeout = 2; + + // Tell server we're disconnecting. + encoded.packetType = PACKET_TYPE_CLIENT_SHUTDOWN; + encoded.control = PACKET_CONTROL_DAT; + encoded.channel = 0; + encoded.encrypt = 0; + packetEncode(__packetThreadData, &encoded, NULL, 0); + packetSend(__packetThreadData, &encoded); + + // Snooze a bit for the packet to send. + while (timeout > 0) { + taskYield(); + if (__timerQuarterSecondTick) timeout--; + } + + // Shut down packet processing & COM port. + netShutdown(); + comClose(__configData.serialCom - 1); + if (nextTask) taskCreate(taskWelcome, nextTask); +} void taskLogin(void *data) { @@ -35,7 +125,39 @@ void taskLogin(void *data) { T_START, T_WINDOW, O(_winLogin), T_TITLE, P("Login"), - T_WIDTH, 500, T_HEIGHT, 225, + T_WIDTH, 300, T_HEIGHT, 155, + + T_TEXTBOX, O(_txtUser), + T_TITLE, P("User Name:"), + T_X, 42, T_Y, 10, + T_WIDTH, 200, + T_LENGTH, 16, + T_TEXTBOX, T_DONE, + + T_TEXTBOX, O(_txtPass), + T_TITLE, P(" Password:"), + T_X, 42, T_Y, 40, + T_WIDTH, 200, + T_LENGTH, 16, + T_MASK, '*', + T_TEXTBOX, T_DONE, + + T_BUTTON, O(_btnCancel), + T_TITLE, P("Cancel"), + T_X, 25, T_Y, 85, + T_CLICK, P(btnCancelClick), + T_BUTTON, T_DONE, + T_BUTTON, O(_btnSignUp), + T_TITLE, P("Sign Up"), + T_X, 110, T_Y, 85, + T_CLICK, P(btnSignUpClick), + T_BUTTON, T_DONE, + T_BUTTON, O(_btnLogin), + T_TITLE, P("Login"), + T_X, 199, T_Y, 85, + T_CLICK, P(btnLoginClick), + T_BUTTON, T_DONE, + T_WINDOW, T_DONE, T_END }; diff --git a/client/src/main.c b/client/src/main.c index 3f1b409..3b742d7 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -47,138 +47,19 @@ #include "gui.h" #include "config.h" #include "runtime.h" +#include "comport.h" #include "welcome.h" +#include "signup.h" PacketThreadDataT *__packetThreadData = NULL; // Exported in os.h RuntimeDataT __runtimeData; // Exported in runtime.h -static void taskComDebugLoop(void *data); static void taskGuiEventLoop(void *data); -#include "comport.h" -static void taskComDebugLoop(void *data) { - int32_t r; - int32_t x; - char buffer[1024]; - - (void)data; - - // Open COM port. - logWrite("Opening COM\n"); - r = comOpen(__configData.serialCom - 1, 57600L, 8, 'n', 1, SER_HANDSHAKING_RTSCTS); - if (r != SER_SUCCESS) { - logWrite("Unable to open COM port! Please check settings.\n"); - guiStop(); - return; - } - // Send a CR to clear anything in the modem. - logWrite("Clearing modem buffer\n"); - snprintf(buffer, 1023, "%c", 13); - comWrite(__configData.serialCom - 1, buffer, strlen(buffer)); - // Wait roughly a second for anything. - comWaitWithTimeout(__configData.serialCom - 1, buffer, 1023, 4, "weExpectNothing"); - // Send actual init - logWrite("Init modem\n"); - snprintf(buffer, 1023, "%s%c", "AT+SOCK1", 13); - comWrite(__configData.serialCom - 1, buffer, strlen(buffer)); - // Wait roughly a second for "OK". - r = comWaitWithTimeout(__configData.serialCom - 1, buffer, 1023, 4, "OK"); - if (r <= 0) { - comClose(__configData.serialCom - 1); - logWrite("Modem does not support ENET! Please check settings.\n"); - guiStop(); - return; - } - logWrite("Modem OK\n"); - // Flush COM port. - timerQuarterSecondsWait(4); - comReceiveBufferFlush(__configData.serialCom - 1); - - // Show dialing, dial service. - logWrite("Dialing\n"); - snprintf(buffer, 1023, "ATDT%s:%d%c", __configData.serverHost, __configData.serverPort, 13); - comWrite(__configData.serialCom - 1, buffer, strlen(buffer)); - // Wait 7 seconds for welcome banner. - r = comWaitWithTimeout(__configData.serialCom - 1, buffer, 1023, 4 * 7, "KPMPGSMKII\r"); - if (r <= 0) { - comClose(__configData.serialCom - 1); - logWrite("Unable to connect to server! Please check settings or try later.\n"); - guiStop(); - return; - } - - // Connected! Show icon and negotiate session. - logWrite("Connected\n"); - - packetEncryptionSetup(__packetThreadData); - - PacketEncodeDataT encoded = { 0 }; - PacketDecodeDataT decoded = { 0 }; - - // Send it 10 times, waiting for a PONG between each. - for (x=0; x<10; x++) { - // Send PING. - logWrite("Sending PING %d\n", x); - encoded.control = PACKET_CONTROL_DAT; - encoded.packetType = PACKET_TYPE_PING; - encoded.channel = 1; - encoded.encrypt = 0; - packetEncode(__packetThreadData, &encoded, NULL, 0); // Must encode each packet - no reusing encoded data. - packetSend(__packetThreadData, &encoded); - // Wait for PONG. - while (!guiHasStopped()) { - r = comRead(__configData.serialCom - 1, buffer, 1); - if (r == 1) { - if (packetDecode(__packetThreadData, &decoded, buffer, 1)) { - if (decoded.packetType == PACKET_TYPE_PONG) { - logWrite("Received PONG\n"); - break; - } else { - logWrite("Unexpected packet type received %d\n", decoded.packetType); - } - } - } else { - taskYield(); - } - } - } - - if (guiHasStopped()) return; - - // Send LOGIN. - logWrite("Sending LOGIN\n"); - PacketTypeLoginT loginData; - strcpy(loginData.user, "Encryption"); - strcpy(loginData.pass, "Works!"); - encoded.control = PACKET_CONTROL_DAT; - encoded.packetType = PACKET_TYPE_LOGIN; - encoded.channel = 1; - encoded.encrypt = 1; - packetEncode(__packetThreadData, &encoded, (char *)&loginData, sizeof(PacketTypeLoginT)); // Must encode each packet - no reusing encoded data. - packetSend(__packetThreadData, &encoded); - - // Send CLIENT_SHUTDOWN. - logWrite("Sending CLIENT_SHUTDOWN\n"); - encoded.control = PACKET_CONTROL_DAT; - encoded.packetType = PACKET_TYPE_CLIENT_SHUTDOWN; - encoded.channel = 1; - encoded.encrypt = 0; - packetEncode(__packetThreadData, &encoded, NULL, 0); - packetSend(__packetThreadData, &encoded); - - logWrite("Sleeping\n"); - timerQuarterSecondsWait(8); - - logWrite("COM closed\n"); - comClose(__configData.serialCom - 1); - guiStop(); -} - - static void taskGuiEventLoop(void *data) { MouseT *mouse = NULL; ImageT *pointer = NULL; @@ -211,6 +92,10 @@ static void taskGuiEventLoop(void *data) { int main(int argc, char *argv[]) { + FILE *cache = NULL; + char *line = NULL; + char *p = NULL; + #ifndef __linux__ // On DOS, display the contets of the log now that we're back in text mode. char *logName = NULL; @@ -260,31 +145,74 @@ int main(int argc, char *argv[]) { sh_new_strdup(__runtimeData.integers); sh_new_strdup(__runtimeData.strings); - // ***TODO*** Load tables from disk cache + // ***TODO*** Default initial tables - //taskCreate(taskComDebugLoop, NULL); + mkdir("cache", 0777); + line = (char *)malloc(4096); + if (line) { + // Load string cache. + cache = fopen("cache/string.dat", "rt"); + if (cache) { + while (fscanf(cache, "%s\n", line) != EOF) { + p = strstr(line, "="); + if (p) { + *p = 0; + p++; + shput(__runtimeData.strings, line, strdup(p)); + } + } + fclose(cache); + } + // Load integer cache. + cache = fopen("cache/integer.dat", "rt"); + if (cache) { + while (fscanf(cache, "%s\n", line) != EOF) { + p = strstr(line, "="); + if (p) { + *p = 0; + p++; + shput(__runtimeData.integers, line, atol(p)); + } + } + fclose(cache); + } + free(line); + line = NULL; + } + +// taskCreate(taskSignUp, NULL); taskCreate(taskWelcome, NULL); taskCreate(taskGuiEventLoop, NULL); taskRun(); - // ***TODO*** Write tables from disk cache - - // Free integer table. - if (__runtimeData.integers) { - while (shlen(__runtimeData.integers) > 0) { - shdel(__runtimeData.integers, __runtimeData.integers[0].key); + // Save & free integer table. + cache = fopen("cache/integer.dat", "wt"); + if (cache) { + if (__runtimeData.integers) { + while (shlen(__runtimeData.integers) > 0) { + //logWrite("[%s]=[%d]\n", __runtimeData.integers[0].key, __runtimeData.integers[0].value); + fprintf(cache, "%s=%d\n", __runtimeData.integers[0].key, __runtimeData.integers[0].value); + shdel(__runtimeData.integers, __runtimeData.integers[0].key); + } + shfree(__runtimeData.integers); } - shfree(__runtimeData.integers); + fclose(cache); } - // Free string table. - if (__runtimeData.strings) { - while (shlen(__runtimeData.strings) > 0) { - DEL(__runtimeData.strings[0].value); - shdel(__runtimeData.strings, __runtimeData.strings[0].key); + // Save & free string table. + cache = fopen("cache/string.dat", "wt"); + if (cache) { + if (__runtimeData.strings) { + while (shlen(__runtimeData.strings) > 0) { + //logWrite("[%s]=[%s]\n", __runtimeData.strings[0].key, __runtimeData.strings[0].value); + fprintf(cache, "%s=%s\n", __runtimeData.strings[0].key, __runtimeData.strings[0].value); + DEL(__runtimeData.strings[0].value); + shdel(__runtimeData.strings, __runtimeData.strings[0].key); + } + shfree(__runtimeData.strings); } - shfree(__runtimeData.strings); + fclose(cache); } packetThreadDataDestroy(&__packetThreadData); diff --git a/client/src/network.c b/client/src/network.c index 9ecc4bc..4ad68ca 100644 --- a/client/src/network.c +++ b/client/src/network.c @@ -66,11 +66,16 @@ PacketDecodeDataT *netGetPacket(uint8_t channel) { } -void netStop(void) { +void netShutdown(void) { _netRunning = 0; } +void netStartup(void) { + taskCreate(taskNetwork, NULL); +} + + void taskNetwork(void *data) { int32_t r = 0; int8_t pingTimeout = 0; diff --git a/client/src/network.h b/client/src/network.h index e10fbdf..333632d 100644 --- a/client/src/network.h +++ b/client/src/network.h @@ -27,7 +27,8 @@ uint8_t netGetChannelFree(void); PacketDecodeDataT *netGetPacket(uint8_t channel); -void netStop(void); +void netShutdown(void); +void netStartup(void); void taskNetwork(void *data); diff --git a/client/src/signup.c b/client/src/signup.c new file mode 100644 index 0000000..95112ff --- /dev/null +++ b/client/src/signup.c @@ -0,0 +1,384 @@ +/* + * 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 "textbox.h" +#include "button.h" +#include "label.h" +#include "msgbox.h" + +#include "network.h" +#include "runtime.h" +#include "taglist.h" +#include "timer.h" +#include "task.h" + +#include "signup.h" +#include "login.h" + + +static WindowT *_winSignUp = NULL; +static LabelT *_lblRequired = NULL; +static TextboxT *_txtEmail = NULL; +static TextboxT *_txtFirst = NULL; +static TextboxT *_txtLast = NULL; +static TextboxT *_txtUser = NULL; +static TextboxT *_txtPass1 = NULL; +static TextboxT *_txtPass2 = NULL; +static ButtonT *_btnCancel = NULL; +static ButtonT *_btnSignUp = NULL; + + +static void btnCancelClick(WidgetT *widget); +static void btnMsgBoxContinue(MsgBoxButtonT button); +static void btnMsgBoxFinish(MsgBoxButtonT button); +static void setButtons(uint8_t enabled); +static void taskSignUpClick(void *data); +static uint8_t validateEmail(char *email); +static uint8_t validateEmailLetter(char c); +static uint8_t validateName(char *username); +static uint8_t validatePassword(char *password); +static uint8_t validateUser(char *username); + + +static void btnCancelClick(WidgetT *widget) { + (void)widget; + + guiDelete(D(_winSignUp)); + taskCreate(taskLogin, NULL); +} + + +static void btnMsgBoxContinue(MsgBoxButtonT button) { + (void)button; + + setButtons(1); +} + + +static void btnMsgBoxFinish(MsgBoxButtonT button) { + (void)button; + + guiDelete(D(_winSignUp)); + taskCreate(taskLogin, NULL); +} + + +static void setButtons(uint8_t enabled) { + widgetEnableSet(W(_btnCancel), enabled); + widgetEnableSet(W(_btnSignUp), enabled); +} + + +void taskSignUp(void *data) { + + (void)data; + + TagItemT uiSignUp[] = { + T_START, + T_WINDOW, O(_winSignUp), + T_TITLE, P("Create New Account"), + T_WIDTH, 400, T_HEIGHT, 340, + + T_LABEL, O(_lblRequired), + T_TITLE, P("All fields are required!"), + T_X, 100, T_Y, 25, + T_COLOR_FOREGROUND, vbeColorMake(255, 255, 0), + T_LABEL, T_DONE, + + T_TEXTBOX, O(_txtUser), + // 123456789012345678 + T_TITLE, P(" User Name:"), + T_X, 40, T_Y, 64, + T_WIDTH, 300, + T_LENGTH, 16, + T_TEXTBOX, T_DONE, + + T_TEXTBOX, O(_txtPass1), + // 123456789012345678 + T_TITLE, P(" Password:"), + T_X, 40, T_Y, 94, + T_WIDTH, 300, + T_LENGTH, 16, + T_MASK, '*', + T_TEXTBOX, T_DONE, + + T_TEXTBOX, O(_txtPass2), + // 123456789012345678 + T_TITLE, P(" Password (again):"), + T_X, 40, T_Y, 124, + T_WIDTH, 300, + T_LENGTH, 16, + T_MASK, '*', + T_TEXTBOX, T_DONE, + + T_TEXTBOX, O(_txtFirst), + // 123456789012345678 + T_TITLE, P(" REAL First Name:"), + T_X, 40, T_Y, 154, + T_WIDTH, 300, + T_LENGTH, 16, + T_MASK, '*', + T_TEXTBOX, T_DONE, + + T_TEXTBOX, O(_txtLast), + // 123456789012345678 + T_TITLE, P(" REAL Last Name:"), + T_X, 40, T_Y, 184, + T_WIDTH, 300, + T_LENGTH, 16, + T_MASK, '*', + T_TEXTBOX, T_DONE, + + T_TEXTBOX, O(_txtEmail), + // 123456789012345678 + T_TITLE, P(" VALID E-Mail:"), + T_X, 40, T_Y, 214, + T_WIDTH, 300, + T_LENGTH, 16, + T_MASK, '*', + T_TEXTBOX, T_DONE, + + T_BUTTON, O(_btnCancel), + T_TITLE, P("Cancel"), + T_X, 25, T_Y, 270, + T_CLICK, P(btnCancelClick), + T_BUTTON, T_DONE, + T_BUTTON, O(_btnSignUp), + T_TITLE, P("Sign Up"), + T_X, 291, T_Y, 270, + T_CLICK, P(taskProxy), + T_USER_DATA, P(taskSignUpClick), + T_BUTTON, T_DONE, + + T_WINDOW, T_DONE, + T_END + }; + + tagListRun(uiSignUp); +} + + +static void taskSignUpClick(void *data) { + PacketEncodeDataT encoded = { 0 }; + PacketDecodeDataT *decoded = NULL; + PacketTypeSignUpT signup = { 0 }; + PacketTypeSignUpResultT *result = { 0 }; + int16_t timeout = 5; + + (void)data; + + // ***TODO*** This should disable the entire dialog instead of just the buttons. Need to finish the Enable GUI code first. + + setButtons(0); + + // Get the data. + memcpy(signup.email, textboxValueGet(_txtEmail), strlen(textboxValueGet(_txtEmail))); + memcpy(signup.first, textboxValueGet(_txtFirst), strlen(textboxValueGet(_txtFirst))); + memcpy(signup.last, textboxValueGet(_txtLast), strlen(textboxValueGet(_txtLast))); + memcpy(signup.pass, textboxValueGet(_txtPass1), strlen(textboxValueGet(_txtPass1))); + memcpy(signup.user, textboxValueGet(_txtUser), strlen(textboxValueGet(_txtUser))); + + // Validate it. ***TODO*** These messages could be a lot better. + if (!validateEmail(signup.email)) { + msgBoxOne("Invalid E-Mail", MSGBOX_ICON_ERROR, "Please enter a valid E-mail address.", "Okay", btnMsgBoxContinue); + return; + } + if (!validateName(signup.first)) { + msgBoxOne("Invalid First Name", MSGBOX_ICON_ERROR, "Please enter a valid first name.", "Okay", btnMsgBoxContinue); + return; + } + if (!validateName(signup.last)) { + msgBoxOne("Invalid Last Name", MSGBOX_ICON_ERROR, "Please enter a valid last name.", "Okay", btnMsgBoxContinue); + return; + } + if (strcmp(textboxValueGet(_txtPass1), textboxValueGet(_txtPass2)) != 0) { + msgBoxOne("Invalid Password", MSGBOX_ICON_ERROR, "Passwords must match.", "Okay", btnMsgBoxContinue); + return; + } + if (!validatePassword(signup.pass)) { + msgBoxOne("Invalid Password", MSGBOX_ICON_ERROR, "Please enter a valid password.", "Okay", btnMsgBoxContinue); + return; + } + if (!validateUser(signup.user)) { + msgBoxOne("Invalid User Name", MSGBOX_ICON_ERROR, "Please enter a valid user name.", "Okay", btnMsgBoxContinue); + return; + } + + // Send signup request. + encoded.packetType = PACKET_TYPE_SIGNUP; + encoded.control = PACKET_CONTROL_DAT; + encoded.channel = 0; + encoded.encrypt = 0; + packetEncode(__packetThreadData, &encoded, (char *)&signup, sizeof(PacketTypeSignUpT)); + packetSend(__packetThreadData, &encoded); + + // Wait for response. + do { + decoded = netGetPacket(0); + if (decoded) { + switch (decoded->packetType) { + case PACKET_TYPE_SIGNUP_RESULT: + result = (PacketTypeSignUpResultT *)decoded->data; + if (result->success) { + msgBoxOne("Success!", MSGBOX_ICON_INFORMATION, result->message, "Okay", btnMsgBoxFinish); + } else { + msgBoxOne("Uh Oh!", MSGBOX_ICON_ERROR, result->message, "Okay", btnMsgBoxContinue); + } + timeout = 0; + break; + + default: + logWrite("Unexpected packet received: %d\n", decoded->packetType); + break; + } + packetDecodeDataDestroy(&decoded); + } + taskYield(); + if (__timerQuarterSecondTick) timeout--; + } while (!guiHasStopped() && timeout > 0); + + setButtons(1); +} + + +static uint8_t validateEmail(char *email) { + + uint16_t x; + uint16_t step = 0; + + // This does a really terrible job of checking if a string is an email address. + // It only has be sorta close. The server will make sure. + + for (x=0; x= '0') && (c <= '9')) return 1; + if ((c >= 'a') && (c <= 'z')) return 1; + if ((c >= 'A') && (c <= 'Z')) return 1; + return 0; +} + + +static uint8_t validateName(char *username) { + uint16_t x; + char *allowed; + char needle[2]; + + if ((int32_t)strlen(username) < shget(__runtimeData.integers, "minName")) return 0; + if ((int32_t)strlen(username) > shget(__runtimeData.integers, "maxName")) return 0; + + allowed = shget(__runtimeData.strings, "nameAllowed"); + needle[1] = 0; + for (x=0; x shget(__runtimeData.integers, "maxPass")) return 0; + + passLower = shget(__runtimeData.strings, "passLower"); + passUpper = shget(__runtimeData.strings, "passUpper"); + passSpecial = shget(__runtimeData.strings, "passSpecial"); + passNumeric = shget(__runtimeData.strings, "passNumeric"); + needle[1] = 0; + for (x=0; x shget(__runtimeData.integers, "maxUser")) return 0; + + allowed = shget(__runtimeData.strings, "userAllowed"); + needle[1] = 0; + for (x=0; x. + * + */ + + +#ifndef SIGNUP_H +#define SIGNUP_H + + +#include "os.h" + + +void taskSignUp(void *data); + + +#endif // SIGNUP_H diff --git a/client/src/system/os.c b/client/src/system/os.c new file mode 100644 index 0000000..1c32230 --- /dev/null +++ b/client/src/system/os.c @@ -0,0 +1,23 @@ +/* + * 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 "os.h" + + diff --git a/client/src/system/os.h b/client/src/system/os.h index 578871d..d659b03 100644 --- a/client/src/system/os.h +++ b/client/src/system/os.h @@ -34,6 +34,7 @@ #include #include #include +#include #ifdef __linux__ diff --git a/client/src/system/taglist.c b/client/src/system/taglist.c index 0b0a754..f310628 100644 --- a/client/src/system/taglist.c +++ b/client/src/system/taglist.c @@ -106,6 +106,7 @@ static void tagListWidgetAttributeHandle(void) { uint8_t hasIndex; uint8_t enabled; uint8_t visible; + char mask; // Process generated lists in reverse. while (arrlen(_widgetList) > 0) { @@ -140,6 +141,7 @@ static void tagListWidgetAttributeHandle(void) { hasIndex = 0; enabled = 1; visible = 1; + mask = 0; // Parse provided attributes. for (i=0; itagList); i+=2) { @@ -196,6 +198,10 @@ static void tagListWidgetAttributeHandle(void) { length = v; break; + case T_MASK: + mask = v; + break; + case T_MAXIMUM: maximum = v; break; @@ -320,8 +326,9 @@ static void tagListWidgetAttributeHandle(void) { case T_TEXTBOX: widget = W(textboxNew(pos.x, pos.y, pos.w, title)); - textboxValueSet((TextboxT *)widget, valueString); + if (hasValue) textboxValueSet((TextboxT *)widget, valueString); if (length > 0) textboxLengthMaxSet((TextboxT *)widget, length); + textboxPasswordCharacterSet((TextboxT *)widget, mask); break; case T_UPDOWN: diff --git a/client/src/system/taglist.h b/client/src/system/taglist.h index 061fa2d..b216ff6 100644 --- a/client/src/system/taglist.h +++ b/client/src/system/taglist.h @@ -71,6 +71,7 @@ enum TagItemsE { T_INDEX, T_ITEM, T_LENGTH, + T_MASK, T_MAXIMUM, T_MINIMUM, T_SELECTED, diff --git a/client/src/welcome.c b/client/src/welcome.c index 3a18990..a3ca60c 100644 --- a/client/src/welcome.c +++ b/client/src/welcome.c @@ -137,7 +137,7 @@ static void taskConnectClick(void *data) { } // Start packet handler and negotiate encryption. - taskCreate(taskNetwork, NULL); + netStartup(); packetEncryptionSetup(__packetThreadData); timeout = 5; do { @@ -161,20 +161,18 @@ static void taskConnectClick(void *data) { 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: - logWrite("Received PACKET_TYPE_PROCEED\n"); 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]); + //logWrite("Storing [%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; @@ -219,7 +217,7 @@ static void taskConnectClick(void *data) { // Connected! Show icon. widgetVisibleSet(W(_picConnect), 1); timeout = 6; // Roughly 1.5 seconds. - while (timeout > 0) { + while (timeout > 0 && !guiHasStopped()) { taskYield(); if (__timerQuarterSecondTick) timeout--; } @@ -244,7 +242,7 @@ static void btnSettingsClick(WidgetT *widget) { } -void setButtons(uint8_t enabled) { +static void setButtons(uint8_t enabled) { widgetEnableSet(W(_btnConnect), (__configData.serialCom > 0 && strlen(__configData.serverHost) > 2) ? enabled : 0); widgetEnableSet(W(_btnSettings), enabled); widgetEnableSet(W(_btnQuit), enabled); diff --git a/server/src/client.c b/server/src/client.c index 72b51d4..0f68dd7 100644 --- a/server/src/client.c +++ b/server/src/client.c @@ -92,7 +92,10 @@ static void clientProcessPacket(ClientThreadT *client, PacketDecodeDataT *data) packetEncode(client->packetThreadData, &encoded, NULL, 0); // Send it. packetSend(client->packetThreadData, &encoded); - logWrite("Got PING, sent PONG\n\r"); + break; + + case PACKET_TYPE_SIGNUP: + //***TODO*** break; case PACKET_TYPE_VERSION_BAD: @@ -100,21 +103,19 @@ static void clientProcessPacket(ClientThreadT *client, PacketDecodeDataT *data) 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"); + consoleMessageQueue("%ld: Unable to fetch strings!\n", client->threadIndex); break; } strings = restHelperConfigStringMapGet(response); if (!strings) { - logWrite("Unable to map strings!\n\r"); + consoleMessageQueue("%ld: Unable to map strings!\n", client->threadIndex); 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); @@ -122,11 +123,12 @@ static void clientProcessPacket(ClientThreadT *client, PacketDecodeDataT *data) length = x + y + 2; buffer = (char *)malloc(length); if (!buffer) { - logWrite("Unable to allocate buffer for string packet!\n\r"); + consoleMessageQueue("%ld: Unable to allocate buffer for string packet!\n", client->threadIndex); break; } memcpy(buffer, strings[i].key, x + 1); memcpy(&buffer[x + 1], strings[i].value, y + 1); + //consoleMessageQueue("[%s]=[%s]\n", strings[i].key, strings[i].value); // Build packet. encoded.control = PACKET_CONTROL_DAT; encoded.packetType = PACKET_TYPE_STRING; @@ -136,23 +138,21 @@ static void clientProcessPacket(ClientThreadT *client, PacketDecodeDataT *data) // Send it. packetSend(client->packetThreadData, &encoded); DEL(buffer); - //logWrite("[%s] = [%s]\r\n", strings[i].key, strings[i].value); } restHelperConfigStringMapRelease(strings); // Fetch number table from REST. response = restRequest("CONFIG_GET_NUMBERS", NULL); if (!response) { - logWrite("Unable to fetch numbers!\n\r"); + consoleMessageQueue("%ld: Unable to fetch numbers!\n", client->threadIndex); break; } integers = restHelperConfigIntegerMapGet(response); if (!integers) { - logWrite("Unable to map numbers!\n\r"); + consoleMessageQueue("%ld: Unable to map numbers!\n", client->threadIndex); 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. @@ -161,7 +161,7 @@ static void clientProcessPacket(ClientThreadT *client, PacketDecodeDataT *data) length = x + 5; buffer = (char *)malloc(length); if (!buffer) { - logWrite("Unable to allocate buffer for number packet!\n\r"); + consoleMessageQueue("%ld: Unable to allocate buffer for number packet!\n\r", client->threadIndex); break; } memcpy(buffer, &y, 4); @@ -186,7 +186,6 @@ static void clientProcessPacket(ClientThreadT *client, PacketDecodeDataT *data) packetEncode(client->packetThreadData, &encoded, NULL, 0); // Send it. packetSend(client->packetThreadData, &encoded); - logWrite("Sending proceed.\n\r"); break; default: diff --git a/server/src/rest.c b/server/src/rest.c index 0ebe7e1..9858a32 100644 --- a/server/src/rest.c +++ b/server/src/rest.c @@ -25,6 +25,7 @@ #include "os.h" #include "rest.h" #include "array.h" +#include "console.h" typedef struct RestResponseS { @@ -147,6 +148,7 @@ RestStringMapT *restHelperConfigStringMapGet(json_object *object) { for (i=0; irunning = 0; - pthread_join(client->threadHandle, &status); // Tell the console. enet_address_get_host_ip(&peer->address, buffer, 2047); consoleMessageQueue("%ld: [%s] disconnected.\n", client->threadIndex, buffer); + // Stop client processing. + client->running = 0; + pthread_join(client->threadHandle, &status); // Hang up. enet_peer_reset(peer); } diff --git a/shared/packet.c b/shared/packet.c index 175e5fc..6803e4f 100644 --- a/shared/packet.c +++ b/shared/packet.c @@ -74,6 +74,23 @@ static void *packetAesContextCreate(uint8_t *key, uint8_t *iv) { static uint8_t packetCRC(char *data, uint16_t length, uint8_t startAt) { uint16_t x = 0; + /* + * ***TODO*** + * + * Something in the data is throwing this off. CRCs fail when there are + * "special characters" in the data, such as these string table fields: + * + * Storing [userAllowed]=[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890`!@#$%^&*()_-+=[]{}\/?<>.,:;"'] + * CRC failed! 192 != 60 + * + * Storing [passSpecial]=[`!@#$%^&*()_-+=[]\{}|:;"',.<>/?] + * CRC failed! 8 != 244 + * + * Like the rest of the reliability code, this is only needed if we have + * RS-232 issues to the softmodem. Documenting for later if needed. + * + */ + for (x=0; xnewPacket = 1; - // Check CRC. - if ((uint8_t)threadData->decodeBuffer[threadData->length - 1] != packetCRC(threadData->decodeBuffer, threadData->length - 1, 0)) continue; - /* + // Check CRC. + x = packetCRC(threadData->decodeBuffer, threadData->length - 1, 0); + if ((uint8_t)threadData->decodeBuffer[threadData->length - 1] != (uint8_t)x) { + //logWrite("CRC failed! %d != %d\n", (uint8_t)threadData->decodeBuffer[threadData->length - 1], (uint8_t)x); + continue; + } + // Get sequence value. sequence = ((uint8_t)threadData->decodeBuffer[0]) & 0x1f; diff --git a/shared/packets.h b/shared/packets.h index af71fdd..147545c 100644 --- a/shared/packets.h +++ b/shared/packets.h @@ -27,8 +27,11 @@ #define PACKET_PROTOCOL_VERSION 1 -#define PACKET_MAX_USER 16 -#define PACKET_MAX_PASS 16 +#define PACKET_MAX_USER 16 // These need to match the configuration in the database. +#define PACKET_MAX_PASS 16 // Or better, use the database values. +#define PACKET_MAX_NAME 32 +#define PACKET_MAX_EMAIL 32 +#define PACKET_MAX_MESSAGE 256 // This enum is treated as BYTES in the code. Do not go over 255 entries. @@ -47,6 +50,8 @@ typedef enum PacketTypeE { PACKET_TYPE_NUMBER, PACKET_TYPE_PROCEED, PACKET_TYPE_LOGIN, + PACKET_TYPE_SIGNUP, + PACKET_TYPE_SIGNUP_RESULT, PACKET_TYPE_COUNT } PacketTypeT; @@ -62,6 +67,19 @@ typedef struct PacketTypeLoginS { char pass[PACKET_MAX_PASS]; } PacketTypeLoginT; +typedef struct PacketTypeSignUpS { + char user[PACKET_MAX_USER]; + char pass[PACKET_MAX_PASS]; + char first[PACKET_MAX_NAME]; + char last[PACKET_MAX_NAME]; + char email[PACKET_MAX_EMAIL]; +} PacketTypeSignUpT; + +typedef struct PacketTypeSignUpResultS { + uint8_t success; + char message[PACKET_MAX_MESSAGE]; +} PacketTypeSignUpResultT; + #pragma pack(pop)