aboutsummaryrefslogtreecommitdiff
path: root/shared/protocol.c
diff options
context:
space:
mode:
Diffstat (limited to 'shared/protocol.c')
-rw-r--r--shared/protocol.c137
1 files changed, 137 insertions, 0 deletions
diff --git a/shared/protocol.c b/shared/protocol.c
new file mode 100644
index 0000000..c6e5ddd
--- /dev/null
+++ b/shared/protocol.c
@@ -0,0 +1,137 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "protocol.h"
+
+#define WS_CMD_MAP(parsed_cmd, name, code) \
+ if (strlen(parsed_cmd->argv[0]) == strlen(name) && strncmp(parsed_cmd->argv[0], name, strlen(name)) == 0) return code;
+
+static ws_e_protocol_cmd ws_protocol_get_req_cmd_code(ws_s_protocol_parsed_req_cmd* parsed_cmd) {
+ if (parsed_cmd == NULL) return WS_PROTOCOL_CMD_UNKNOWN; // invalid command
+ WS_CMD_MAP(parsed_cmd, "last-records", WS_PROTOCOL_CMD_LAST_RECORDS);
+
+ return WS_PROTOCOL_CMD_UNKNOWN;
+}
+
+void ws_protocol_parse_req_byte(ws_s_protocol_req_parser_state* state, char input) {
+ switch(input) {
+ case WS_PROTOCOL_C_EOL: {
+ break;
+ }
+
+ case WS_PROTOCOL_C_SPACE: {
+ if (!state->valid) return;
+ state->arg_len++;
+ return;
+ }
+
+ case WS_PROTOCOL_C_NULL: {
+ state->valid = false;
+ return;
+ }
+
+ default: {
+ if (!state->valid) return;
+ state->cmd[state->cmd_len++] = input;
+ state->args_len[state->arg_len] += 1;
+ if (state->cmd_len == WS_PROTOCOL_CMD_BUFFER_LEN) state->valid = false;
+ return;
+ }
+ }
+ // arg_len is used as an index while parsing, so add 1 to get length
+ state->arg_len++;
+
+ // parse cmd into argc and argv
+ if (state->valid) ws_protocol_req_cmd_init(state);
+ // create response
+ ws_s_protocol_res* response = ws_protocol_parse_req_finished(state->target);
+
+ // send response
+ char response_first_line[16];
+ sprintf(response_first_line, "%s,%x\n", response->success == WS_PROTOCOL_CMD_RETURN_OK ? "ok" : "error", response->msg->bytes);
+ ws_protocol_send_data(response_first_line, strlen(response_first_line));
+ if (!response->csh) ws_protocol_send_data((char*) response->msg->data, response->msg->bytes);
+ else (*g_ws_protocol_res_handlers[response->cmd_code])(state->target, response, true);
+
+ // free response data containers
+ free(response->msg);
+ free(response);
+
+ // reset parser
+ ws_protocol_req_parser_reset(state);
+
+ return;
+}
+
+ws_s_protocol_res* ws_protocol_parse_req_finished(ws_s_protocol_parsed_req_cmd* parsed_cmd) {
+ ws_s_protocol_res* response = malloc(sizeof(ws_s_protocol_res));
+ response->success = WS_PROTOCOL_CMD_RETURN_ERROR;
+ response->csh = false;
+ response->msg = NULL;
+ response->cmd_code = ws_protocol_get_req_cmd_code(parsed_cmd);
+
+ if (response->cmd_code == WS_PROTOCOL_CMD_UNKNOWN) goto ws_protocol_parse_exit;
+ if (response->cmd_code >= WS_PROTOCOL_CMD_AMOUNT) goto ws_protocol_parse_exit;
+
+ ws_protocol_res_handler_t* ws_protocol_res_handler = g_ws_protocol_res_handlers[response->cmd_code];
+ if (ws_protocol_res_handler == NULL) goto ws_protocol_parse_exit;
+ (*ws_protocol_res_handler)(parsed_cmd, response, false);
+
+ws_protocol_parse_exit:
+
+ if (response->msg == NULL) response->msg = ws_bin_s_alloc(0);
+ return response;
+}
+
+void ws_protocol_parse_req_bytes(ws_s_protocol_req_parser_state* state, char* input, unsigned int length) {
+ for (unsigned int i = 0; i < length; i++) ws_protocol_parse_req_byte(state, input[i]);
+}
+
+ws_s_protocol_req_parser_state* ws_protocol_req_parser_alloc() {
+ ws_s_protocol_req_parser_state* parser_state = malloc(sizeof(ws_s_protocol_req_parser_state) + sizeof(uint16_t) * WS_PROTOCOL_CMD_MAX_ARGUMENTS);
+ parser_state->cmd = malloc(sizeof(char) * WS_PROTOCOL_CMD_BUFFER_LEN);
+ parser_state->target = NULL;
+ ws_protocol_req_parser_reset(parser_state);
+ return parser_state;
+}
+
+void ws_protocol_req_cmd_init(ws_s_protocol_req_parser_state* state) {
+ state->target = malloc(sizeof(ws_s_protocol_parsed_req_cmd) + sizeof(char*) * state->arg_len);
+ for (unsigned int i = 0; i < state->arg_len; i++)
+ state->target->argv[i] = malloc(sizeof(char) * (state->args_len[i] + 1));
+
+ state->target->argc = state->arg_len;
+
+ unsigned int head = 0;
+ for (unsigned int i = 0; i < state->arg_len; i++) {
+ strncpy(state->target->argv[i], &state->cmd[head], state->args_len[i]);
+ state->target->argv[i][state->args_len[i]] = 0x00; // terminate argument with null byte
+ head += state->args_len[i];
+ }
+}
+
+void ws_protocol_req_parser_free(ws_s_protocol_req_parser_state* state) {
+ if (state == NULL) return;
+ if (state->target != NULL) ws_protocol_req_cmd_free(state->target);
+ state->target = NULL;
+ free(state->cmd);
+ free(state);
+ return;
+}
+
+void ws_protocol_req_parser_reset(ws_s_protocol_req_parser_state* state) {
+ if (state->target != NULL) ws_protocol_req_cmd_free(state->target);
+ state->target = NULL;
+ state->valid = true;
+ state->cmd_len = 0;
+ state->arg_len = 0;
+ memset(state->args_len, 0, sizeof(uint16_t) * WS_PROTOCOL_CMD_MAX_ARGUMENTS);
+}
+
+void ws_protocol_req_cmd_free(ws_s_protocol_parsed_req_cmd* cmd) {
+ for (int i = 0; i < cmd->argc; i++)
+ free(cmd->argv[i]);
+ free(cmd);
+ return;
+}