commit 74518207654cf1282305070ace0b28c28e5c172f Author: flashwave Date: Wed Dec 27 01:35:22 2023 +0100 meow diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..176a458 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8c2b82c --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +bin/ +*.o +*.cfg +*.dat diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5cfdc45 --- /dev/null +++ b/Makefile @@ -0,0 +1,48 @@ +TARGET ?= satori +SRCDIR := ./src +INCDIR := ./include +BINDIR := ./bin + +DATE = $(shell date +%Y%m%d) + +C_SRC := $(wildcard $(SRCDIR)/*.c) $(wildcard $(SRCDIR)/**/*.c) +CXX_SRC := $(wildcard $(SRCDIR)/*.cxx) $(wildcard $(SRCDIR)/**/*.cxx) + +OBJ := $(C_SRC:.c=.o) $(CXX_SRC:.cxx=.o) +OUT := $(BINDIR)/$(TARGET) + +LIBNAMES := libcurl openssl lua54 json-c $(LIBNAMES) + +CFLAGS := -Wall -Wextra -g -O2 --std=c11 -DSATORI_VERSION=\"$(DATE)\" -D_POSIX_C_SOURCE=200112L $(CFLAGS) +CXXFLAGS := $(CXXFLAGS) +CPPFLAGS := -I$(INCDIR) $(shell pkg-config --cflags $(LIBNAMES)) $(CPPFLAGS) +LDFLAGS := $(LDFLAGS) +LDLIBS := $(shell pkg-config --libs $(LIBNAMES)) $(LDLIBS) + +all: $(OUT) + +$(OUT): $(OBJ) + mkdir -p $(BINDIR) + $(CC) $(LDFLAGS) $(CPPFLAGS) -o $@ $^ $(LDLIBS) + +.cxx.o: + @echo "CXX> " $@ + $(CC) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< + +.c.o: + @echo "C> " $@ + $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< + +clean: + rm -f $(OBJ) $(OUT) + +rebuild: clean all + +run: all + $(OUT) + +gdb: all + gdb $(OUT) + +.PHONY: all clean rebuild +.SUFFIXES: .c .cxx diff --git a/include/buffer.h b/include/buffer.h new file mode 100644 index 0000000..7ac1733 --- /dev/null +++ b/include/buffer.h @@ -0,0 +1,40 @@ +#ifndef H_SATORI_BUFFER +#define H_SATORI_BUFFER + +#include +#include +#include + +#define SAT_BUFFER_PIECE_SIZE (1024) +#define SAT_BUFFER_PIECE_MAX (SAT_BUFFER_PIECE_SIZE - 1) + +typedef struct _sat_buffer_piece { + struct _sat_buffer_piece *next; + char data[SAT_BUFFER_PIECE_SIZE]; +} sat_buffer_piece, *sat_buffer_piece_ptr; + +typedef struct _sat_buffer { + size_t available; + sat_buffer_piece_ptr start; + + size_t rPos; + sat_buffer_piece_ptr rCurrent; + + size_t wPos; + sat_buffer_piece_ptr wCurrent; +} sat_buffer, *sat_buffer_ptr; + +sat_buffer_ptr sat_buffer_alloc(void); +void sat_buffer_free(sat_buffer_ptr buffer); + +bool sat_buffer_pending(sat_buffer_ptr buffer); +size_t sat_buffer_available(sat_buffer_ptr buffer); +void sat_buffer_tidy(sat_buffer_ptr buffer); + +int sat_buffer_write(sat_buffer_ptr buffer, char *source, size_t count); +void sat_buffer_putc(sat_buffer_ptr buffer, char value); + +int sat_buffer_read(sat_buffer_ptr buffer, char *target, size_t length); +char sat_buffer_getc(sat_buffer_ptr buffer); + +#endif // H_SATORI_BUFFER diff --git a/include/config.h b/include/config.h new file mode 100644 index 0000000..5ef716e --- /dev/null +++ b/include/config.h @@ -0,0 +1,56 @@ +#ifndef H_SATORI_CONFIG +#define H_SATORI_CONFIG + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SAT_CONFIG_TYPE_STREAM (1) +#define SAT_CONFIG_TYPE_SCOPED (2) + +typedef struct _sat_config { + int type; + void *info; + struct _sat_config *(*scopeTo)(struct _sat_config *ctx, char *prefix); + char *(*readValue)(struct _sat_config *ctx, char *name, char *fallback); +} sat_config, *sat_config_ptr; + +typedef struct _sat_config_stream { + FILE *stream; + bool ownsStream; + long offset; + mtx_t *lock; +} sat_config_stream, *sat_config_stream_ptr; + +typedef struct _sat_config_scoped { + sat_config_ptr parent; + char *prefix; +} sat_config_scoped, *sat_config_scoped_ptr; + +sat_config_ptr sat_config_alloc(int type); +sat_config_ptr sat_config_alloc_stream(void); +sat_config_ptr sat_config_alloc_scoped(void); + +void sat_config_free(sat_config_ptr ctx); +void sat_config_free_stream(sat_config_ptr ctx); +void sat_config_free_scoped(sat_config_ptr ctx); + +sat_config_ptr sat_config_scope_to(sat_config_ptr ctx, char *prefix); + +char* sat_config_read_value(sat_config_ptr ctx, char *name, char *fallback); +bool sat_config_read_bool(sat_config_ptr ctx, char *name, bool fallback); +uint64_t sat_config_read_u64(sat_config_ptr ctx, char *name, uint64_t fallback); + +int sat_config_stream_open(sat_config_ptr ctx, FILE *stream, bool ownsStream); +int sat_config_stream_open_file(sat_config_ptr ctx, char *path); + +int sat_config_write_example(FILE *stream); +int sat_config_write_example_file(char *path); + +#endif // H_SATORI_CONFIG diff --git a/include/context.h b/include/context.h new file mode 100644 index 0000000..f9afc8b --- /dev/null +++ b/include/context.h @@ -0,0 +1,21 @@ +#ifndef H_SATORI_CONTEXT +#define H_SATORI_CONTEXT + +#include +#include +#include +#include "config.h" +#include "persist.h" +#include "futami.h" + +typedef struct _satori_ctx { + time_t startup; + sat_config_ptr config; + sat_persist_ptr persist; + sat_futami_ptr futami; +} satori_ctx, *satori_ctx_ptr; + +satori_ctx_ptr satori_ctx_alloc(void); +void satori_ctx_free(satori_ctx_ptr ctx); + +#endif // H_SATORI_CONTEXT diff --git a/include/curl_helper.h b/include/curl_helper.h new file mode 100644 index 0000000..6fc4707 --- /dev/null +++ b/include/curl_helper.h @@ -0,0 +1,36 @@ +#ifndef H_SATORI_CURL_HELPER +#define H_SATORI_CURL_HELPER + +#include +#include +#include +#include +#include + +typedef struct _sat_curl_string { + char *str; + size_t length; +} sat_curl_string, *sat_curl_string_ptr; + +sat_curl_string_ptr sat_curl_string_alloc(void); +void sat_curl_string_free(sat_curl_string_ptr str, bool freeArg); +void sat_curl_string_init(sat_curl_string_ptr str); +size_t sat_curl_string_write(char *ptr, size_t width, size_t count, sat_curl_string_ptr str); + +typedef struct _sat_curl_url { + char *scheme; + char *host; + char *port; + char *path; + int flags; // don't touch this +} sat_curl_url, *sat_curl_url_ptr; + +sat_curl_url_ptr sat_curl_url_alloc(void); +void sat_curl_url_free(sat_curl_url_ptr url, bool freeArg); +int sat_curl_url_parse(sat_curl_url_ptr url, char *str, char *scheme, char *host, char *port, char *path, bool supplyDefaults); +void sat_curl_url_owns_scheme(sat_curl_url_ptr url); +void sat_curl_url_owns_host(sat_curl_url_ptr url); +void sat_curl_url_owns_port(sat_curl_url_ptr url); +void sat_curl_url_owns_path(sat_curl_url_ptr url); + +#endif // H_SATORI_CURL_HELPER diff --git a/include/futami.h b/include/futami.h new file mode 100644 index 0000000..1d8ddbc --- /dev/null +++ b/include/futami.h @@ -0,0 +1,25 @@ +#ifndef H_SATORI_FUTAMI +#define H_SATORI_FUTAMI + +#include +#include +#include +#include +#include "curl_helper.h" +#include "macros.h" + +typedef struct _sat_futami { + int32_t ping; + size_t serversCount; + char **servers; +} sat_futami, *sat_futami_ptr; + +sat_futami_ptr sat_futami_alloc(void); +void sat_futami_free(sat_futami_ptr ctx); + +int sat_futami_load_json(sat_futami_ptr ctx, json_object *obj); +int sat_futami_load_json_file(sat_futami_ptr ctx, char *path); +int sat_futami_load_json_string(sat_futami_ptr ctx, char *str); +int sat_futami_load_json_url(sat_futami_ptr ctx, char *url); + +#endif // H_SATORI_FUTAMI diff --git a/include/macros.h b/include/macros.h new file mode 100644 index 0000000..ce81f6e --- /dev/null +++ b/include/macros.h @@ -0,0 +1,20 @@ +#ifndef H_SATORI_VERSION +#define H_SATORI_VERSION + +#define SATORI_NAME "Satori" + +#ifndef SATORI_VERSION +# define SATORI_VERSION "20130127" +#endif + +#define SATORI_USERAGENT (SATORI_NAME "/" SATORI_VERSION " (+https://fii.moe/satori)") + +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(_WIN64) +# define SATORI_OS_WIN +#elif defined(__APPLE__) +# define SATORI_OS_MAC +#elif defined(__linux) || defined(__linux__) || defined(linux) +# define SATORI_OS_LIN +#endif + +#endif diff --git a/include/pack.h b/include/pack.h new file mode 100644 index 0000000..9968723 --- /dev/null +++ b/include/pack.h @@ -0,0 +1,42 @@ +#ifndef H_SATORI_PACK +#define H_SATORI_PACK + +#include + +void sat_pack_i16be(uint8_t *buffer, int16_t num); +void sat_pack_u16be(uint8_t *buffer, uint16_t num); + +void sat_pack_i32be(uint8_t *buffer, int32_t num); +void sat_pack_u32be(uint8_t *buffer, uint32_t num); + +void sat_pack_i64be(uint8_t *buffer, int64_t num); +void sat_pack_u64be(uint8_t *buffer, uint64_t num); + +void sat_pack_i16le(uint8_t *buffer, int16_t num); +void sat_pack_u16le(uint8_t *buffer, uint16_t num); + +void sat_pack_i32le(uint8_t *buffer, int32_t num); +void sat_pack_u32le(uint8_t *buffer, uint32_t num); + +void sat_pack_i64le(uint8_t *buffer, int64_t num); +void sat_pack_u64le(uint8_t *buffer, uint64_t num); + +int16_t sat_unpack_i16be(uint8_t *buffer); +uint16_t sat_unpack_u16be(uint8_t *buffer); + +int32_t sat_unpack_i32be(uint8_t *buffer); +uint32_t sat_unpack_u32be(uint8_t *buffer); + +int64_t sat_unpack_i64be(uint8_t *buffer); +uint64_t sat_unpack_u64be(uint8_t *buffer); + +int16_t sat_unpack_i16le(uint8_t *buffer); +uint16_t sat_unpack_u16le(uint8_t *buffer); + +int32_t sat_unpack_i32le(uint8_t *buffer); +uint32_t sat_unpack_u32le(uint8_t *buffer); + +int64_t sat_unpack_i64le(uint8_t *buffer); +uint64_t sat_unpack_u64le(uint8_t *buffer); + +#endif // H_SATORI_PACK diff --git a/include/persist.h b/include/persist.h new file mode 100644 index 0000000..4319187 --- /dev/null +++ b/include/persist.h @@ -0,0 +1,34 @@ +#ifndef H_SATORI_PERIST +#define H_SATORI_PERIST + +#include +#include +#include +#include +#include +#include +#include +#include "pack.h" + +typedef struct _sat_persist { + FILE *stream; + bool ownsStream; + long offset; + mtx_t *lock; +} sat_persist, *sat_persist_ptr; + +sat_persist_ptr sat_persist_alloc(void); +void sat_persist_free(sat_persist_ptr ctx); + +int sat_persist_create(sat_persist_ptr ctx, FILE *stream, bool ownsStream); +int sat_persist_create_file(sat_persist_ptr ctx, char *path); +void sat_persist_flush(sat_persist_ptr ctx); +int sat_persist_header_refresh(sat_persist_ptr ctx); + +uint64_t sat_persist_get_fii_forum_last_post_id(sat_persist_ptr ctx); +void sat_persist_set_fii_forum_last_post_id(sat_persist_ptr ctx, uint64_t postId); + +uint64_t sat_persist_get_fii_user_last_register_id(sat_persist_ptr ctx); +void sat_persist_set_fii_user_last_register_id(sat_persist_ptr ctx, uint64_t userId); + +#endif // H_SATORI_PERIST diff --git a/include/satori.h b/include/satori.h new file mode 100644 index 0000000..df0ec61 --- /dev/null +++ b/include/satori.h @@ -0,0 +1,15 @@ +#ifndef H_SATORI +#define H_SATORI + +#include "macros.h" +#include "buffer.h" +#include "pack.h" +#include "context.h" +#include "config.h" +#include "persist.h" +#include "futami.h" +#include "sock.h" +#include "websock.h" +#include "curl_helper.h" + +#endif // H_SATORI diff --git a/include/sock.h b/include/sock.h new file mode 100644 index 0000000..9fdb9c6 --- /dev/null +++ b/include/sock.h @@ -0,0 +1,33 @@ +#ifndef H_SATORI_SOCK +#define H_SATORI_SOCK + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct _sat_sock { + int sock; +} sat_sock, *sat_sock_ptr; + +sat_sock_ptr sat_sock_alloc(void); +void sat_sock_free(sat_sock_ptr ctx); +int sat_sock_connect(sat_sock_ptr ctx, int addrFamily, int sockType, char *host, char *port); +int sat_sock_connect_tcp(sat_sock_ptr ctx, char *host, char *port); +void sat_sock_close(sat_sock_ptr ctx); +bool sat_sock_get_blocking(sat_sock_ptr ctx); +void sat_sock_set_blocking(sat_sock_ptr ctx, bool blocking); +int sat_sock_recv(sat_sock_ptr ctx, uint8_t *buffer, size_t size); +int sat_sock_send(sat_sock_ptr ctx, uint8_t *buffer, size_t count); +int sat_sock_select(sat_sock_ptr ctx, int timeout); + +#endif // H_SATORI_SOCK diff --git a/include/websock.h b/include/websock.h new file mode 100644 index 0000000..a410efd --- /dev/null +++ b/include/websock.h @@ -0,0 +1,80 @@ +#ifndef H_SATORI_WEBSOCK +#define H_SATORI_WEBSOCK + +#include +#include +#include +#include +#include +#include "buffer.h" +#include "sock.h" + +typedef struct _sat_websock_close_info { + uint16_t code; + char *reason; +} sat_websock_close_info, *sat_websock_close_info_ptr; + +typedef struct _sat_websock_ctx { + sat_sock_ptr sock; + sat_websock_close_info_ptr closeInfo; + sat_buffer_ptr buffer; +} sat_websock, *sat_websock_ptr; + +sat_websock_ptr sat_websock_alloc(void); +void sat_websock_free(sat_websock_ptr ws); +void sat_websock_init(sat_websock_ptr ws, sat_sock_ptr sock); +void sat_websock_tidy(sat_websock_ptr ws); +void sat_websock_close(sat_websock_ptr ws, int closeCode, char *closeReason); +bool sat_websock_is_closed(sat_websock_ptr ws); +int sat_websock_buffer(sat_websock_ptr ws, size_t want); +int sat_websock_recv(sat_websock_ptr ws, uint8_t *buffer, size_t size); +int sat_websock_send(sat_websock_ptr ws, uint8_t *buffer, size_t count); + +typedef enum _sat_websock_opcode { + // non-control + SAT_WEBSOCK_CONTINUE = 0, + SAT_WEBSOCK_TEXT, + SAT_WEBSOCK_BINARY, + // control + SAT_WEBSOCK_CLOSE = 8, + SAT_WEBSOCK_PING, + SAT_WEBSOCK_PONG, +} sat_websock_opcode; + +typedef struct _sat_websock_frame_header { + bool isFinal; + sat_websock_opcode opcode; + bool isMasked; + char mask[4]; + uint64_t length; // this max size is so ridiculous, should 127 just tell the server to fuck off? + uint64_t offset; // keeps track of data sent for masking +} sat_websock_frame_header, *sat_websock_frame_header_ptr; + +void sat_websock_frame_recv_header(sat_websock_ptr ws, sat_websock_frame_header_ptr header); +int sat_websock_frame_recv(sat_websock_ptr ws, sat_websock_frame_header_ptr header, uint8_t *buffer, size_t size); + +void sat_websock_frame_send_header(sat_websock_ptr ws, sat_websock_frame_header_ptr header); +int sat_websock_frame_send(sat_websock_ptr ws, sat_websock_frame_header_ptr header, uint8_t *buffer, size_t count); + +typedef struct _sat_websock_handshake { + sat_websock_ptr websock; + + char *host; + char *path; + char *origin; + char *protocol; // want to switch this to char** but i need to make a glue func first + char key[16]; + + bool completed; + bool upgraded; + uint16_t statusCode; + char *statusReason; +} sat_websock_handshake, *sat_websock_handshake_ptr; + +sat_websock_handshake_ptr sat_websock_handshake_alloc(void); +void sat_websock_handshake_free(sat_websock_handshake_ptr hs, bool freeArg); +int sat_websock_handshake_gen_key(sat_websock_handshake_ptr hs); +int sat_websock_handshake_send(sat_websock_handshake_ptr hs); +int sat_websock_handshake_recv(sat_websock_handshake_ptr hs); + +#endif // H_SATORI_WEBSOCK diff --git a/src/buffer.c b/src/buffer.c new file mode 100644 index 0000000..28161a2 --- /dev/null +++ b/src/buffer.c @@ -0,0 +1,179 @@ +#include "buffer.h" + +inline sat_buffer_piece_ptr sat_buffer_piece_alloc(void) { + sat_buffer_piece_ptr piece = malloc(sizeof(sat_buffer_piece)); + piece->next = NULL; + return piece; +} + +inline void sat_buffer_piece_free(sat_buffer_piece_ptr piece) { + if(piece != NULL) + free(piece); +} + +sat_buffer_ptr sat_buffer_alloc(void) { + sat_buffer_ptr buffer = malloc(sizeof(sat_buffer)); + buffer->available = buffer->rPos = buffer->wPos = 0; + buffer->wCurrent = buffer->rCurrent = buffer->start = sat_buffer_piece_alloc(); + return buffer; +} + +void sat_buffer_free(sat_buffer_ptr buffer) { + if(buffer == NULL) return; + + sat_buffer_piece_ptr curr = buffer->start; + while(curr != NULL) { + buffer->start = curr->next; + free(curr); + curr = buffer->start; + } + + free(buffer); +} + +bool sat_buffer_pending(sat_buffer_ptr buffer) { + return buffer->available > 0; +} + +size_t sat_buffer_available(sat_buffer_ptr buffer) { + return buffer->available; +} + +void sat_buffer_tidy(sat_buffer_ptr buffer) { + // wCurrent should always be the same or ahead of rCurrent + + sat_buffer_piece_ptr curr = buffer->start; + while(curr != NULL && curr != buffer->rCurrent) { + buffer->start = curr->next; + free(curr); + curr = buffer->start; + } +} + +int sat_buffer_write(sat_buffer_ptr buffer, char *source, size_t count) { + if(buffer == NULL) return -1; + if(source == NULL) return -2; + + if(count == 0) + return 0; + + if(buffer->wCurrent == NULL) { + buffer->wCurrent = buffer->start; + buffer->wPos = 0; + } + + int offset = buffer->wPos; + sat_buffer_piece_ptr piece = buffer->wCurrent; + int written = 0; + + int shove; + while(count > 0) { + if(count + offset < SAT_BUFFER_PIECE_SIZE) + shove = count; + else if(offset < SAT_BUFFER_PIECE_SIZE) + shove = SAT_BUFFER_PIECE_SIZE - offset; + else { + offset = 0; + piece = (piece->next = sat_buffer_piece_alloc()); + continue; + } + + memcpy(&piece->data[offset], source, shove); + + offset += shove; + written += shove; + count -= shove; + } + + buffer->wPos = offset; + buffer->wCurrent = piece; + buffer->available += written; + + return written; +} + +void sat_buffer_putc(sat_buffer_ptr buffer, char value) { + if(buffer == NULL) return; + + if(buffer->wCurrent == NULL) { + buffer->wCurrent = buffer->start; + buffer->wPos = 0; + } else if(buffer->wPos >= SAT_BUFFER_PIECE_MAX) { + buffer->wCurrent = (buffer->wCurrent->next = sat_buffer_piece_alloc()); + buffer->wPos = 0; + } + + buffer->wCurrent->data[buffer->wPos++] = value; + ++buffer->available; +} + +int sat_buffer_read(sat_buffer_ptr buffer, char *target, size_t length) { + if(buffer == NULL) return -1; + if(target == NULL) return -2; + if(buffer->rCurrent == NULL) return -3; + + if(length == 0) + return 0; + + if(buffer->rCurrent == NULL) { + buffer->rCurrent = buffer->start; + buffer->rPos = 0; + } + + int remaining = length > buffer->available ? buffer->available : length; + if(remaining < 1) + return 0; + + int offset = buffer->rPos; + sat_buffer_piece_ptr piece = buffer->rCurrent; + int read = 0; + + int take; + while(remaining > 0) { + if(remaining + offset < SAT_BUFFER_PIECE_SIZE) + take = remaining; + else if(offset < SAT_BUFFER_PIECE_SIZE) + take = SAT_BUFFER_PIECE_SIZE - offset; + else { + offset = 0; + piece = piece->next; + + // this shouldn't happen, maybe make this an assert? + if(piece == NULL) + break; + continue; + } + + memcpy(target, &piece->data[offset], take); + + offset += take; + read += take; + remaining -= take; + } + + buffer->rPos = offset; + buffer->rCurrent = piece; + buffer->available -= read; + + return read; +} + +char sat_buffer_getc(sat_buffer_ptr buffer) { + if(buffer == NULL || buffer->rCurrent == NULL || buffer->available < 1) + return '\0'; + + if(buffer->rCurrent == NULL) { + buffer->rCurrent = buffer->start; + buffer->rPos = 0; + } else if(buffer->rPos >= SAT_BUFFER_PIECE_MAX) { + buffer->rCurrent = buffer->rCurrent->next; + buffer->rPos = 0; + } + + if(buffer->rCurrent == NULL) + return '\0'; + + --buffer->available; + + return buffer->rCurrent->data[buffer->rPos++]; +} diff --git a/src/config/config.c b/src/config/config.c new file mode 100644 index 0000000..3f4e841 --- /dev/null +++ b/src/config/config.c @@ -0,0 +1,59 @@ +#include "config.h" + +#define SAT_CONFIG_LOCK_TIMEOUT (10) +#define SAT_CONFIG_CACHE_LIFETIME (900) + +sat_config_ptr sat_config_alloc(int type) { + if(type == SAT_CONFIG_TYPE_STREAM) + return sat_config_alloc_stream(); + if(type == SAT_CONFIG_TYPE_SCOPED) + return sat_config_alloc_scoped(); + return NULL; +} + +void sat_config_free(sat_config_ptr ctx) { + if(!ctx || !ctx->info) return; + + if(ctx->type == SAT_CONFIG_TYPE_STREAM) + sat_config_free_stream(ctx); + else if(ctx->type == SAT_CONFIG_TYPE_SCOPED) + sat_config_free_scoped(ctx); +} + +sat_config_ptr sat_config_scope_to(sat_config_ptr ctx, char *prefix) { + return ctx->scopeTo(ctx, prefix); +} + +char* sat_config_read_value(sat_config_ptr ctx, char *name, char *fallback) { + return ctx->readValue(ctx, name, fallback); +} + +bool sat_config_read_bool(sat_config_ptr ctx, char *name, bool fallback) { + char *raw = sat_config_read_value(ctx, name, NULL); + if(raw == NULL) + return fallback; + + for(char *p = raw; *p; ++p) + *p = tolower(*p); + + bool value = strcmp(raw, "0") != 0 + && strcmp(raw, "no") != 0 + && strcmp(raw, "false") != 0; + + free(raw); + + return value; +} + +uint64_t sat_config_read_u64(sat_config_ptr ctx, char *name, uint64_t fallback) { + char *raw = sat_config_read_value(ctx, name, NULL); + if(raw == NULL) + return fallback; + + char *end = NULL; + uint64_t value = strtoull(raw, &end, 10); + + free(raw); + + return value; +} diff --git a/src/config/config_example.c b/src/config/config_example.c new file mode 100644 index 0000000..5536ccd --- /dev/null +++ b/src/config/config_example.c @@ -0,0 +1,65 @@ +#include "config.h" + +int sat_config_write_example(FILE *stream) { + if(stream == NULL) + return -1; + + char buff[70]; + time_t curr = time(NULL); + struct tm dt = *localtime(&curr); + strftime(buff, sizeof buff, "%F %T", &dt); + + fprintf(stream, "# Satori configuration\r\n"); + fprintf(stream, "# Created on %s\r\n", buff); + fprintf(stream, "\r\n"); + + fprintf(stream, "# Sock Chat connection settings\r\n"); + fprintf(stream, "sockchat:host ws://example.com/sockchat\r\n"); + fprintf(stream, "sockchat:token AAAAAAAAAAAAAAAAAAAAAAAAA\r\n"); + fprintf(stream, "\r\n"); + + fprintf(stream, "# Prefix character for commands, only the first character will be used\r\n"); + fprintf(stream, "commands:prefix !\r\n"); + fprintf(stream, "\r\n"); + + fprintf(stream, "# Booru settings\r\n"); + fprintf(stream, "booru:prefix ~\r\n"); + fprintf(stream, ";booru:danbooru:token username:token\r\n"); + fprintf(stream, "\r\n"); + + fprintf(stream, "# Worknik API token\r\n"); + fprintf(stream, ";wordnik:token tokengoeshere\r\n"); + fprintf(stream, "\r\n"); + + fprintf(stream, "# Uiharu URL lookup settings\r\n"); + fprintf(stream, "uiharu:enable true\r\n"); + fprintf(stream, "\r\n"); + + fprintf(stream, "# Flashii broadcast settings\r\n"); + fprintf(stream, "flashii:broadcasts:enable true\r\n"); + fprintf(stream, "\r\n"); + + fprintf(stream, "# This option is here to ensure you actually did the configuration.\r\n"); + fprintf(stream, "# You must remove it entirely, or set it to false if you're a fuckhead, or the program won't start.\r\n"); + fprintf(stream, "exampleCfg:unedited true\r\n"); + + return 0; +} + +int sat_config_write_example_file(char *path) { + if(path == NULL) + return -2; + + FILE *stream = fopen(path, "wb"); + + int err = sat_config_write_example(stream); + if(err) { + if(stream) fclose(stream); + return err; + } + + fflush(stream); + fclose(stream); + + return 0; +} diff --git a/src/config/config_scoped.c b/src/config/config_scoped.c new file mode 100644 index 0000000..99f64f4 --- /dev/null +++ b/src/config/config_scoped.c @@ -0,0 +1,64 @@ +#include "config.h" + +sat_config_ptr sat_config_scoped_scope_to(sat_config_ptr ctx, char *prefix) { + sat_config_scoped_ptr info = (sat_config_scoped_ptr)ctx->info; + + int pfxLength = strlen(prefix); + bool appendColon = prefix[pfxLength - 1] != ':'; + + int length = strlen(info->prefix) + pfxLength + 1; + if(appendColon) length += 1; + + char *newPrefix = malloc(length); + memset(newPrefix, 0, length); + strcat(newPrefix, info->prefix); + strcat(newPrefix, prefix); + if(appendColon) strcat(newPrefix, ":"); + + sat_config_ptr scoped = sat_config_alloc_scoped(); + sat_config_scoped_ptr scopedInfo = (sat_config_scoped_ptr)scoped->info; + scopedInfo->parent = info->parent; + scopedInfo->prefix = newPrefix; + + return scoped; +} + +char* sat_config_scoped_read_value(sat_config_ptr ctx, char *name, char *fallback) { + sat_config_scoped_ptr info = (sat_config_scoped_ptr)ctx->info; + + int length = strlen(info->prefix) + strlen(name) + 1; + char *realName = malloc(strlen(info->prefix) + strlen(name) + 1); + memset(realName, 0, length); + strcat(realName, info->prefix); + strcat(realName, name); + + char *value = sat_config_read_value(info->parent, realName, fallback); + + free(realName); + + return value; +} + +sat_config_ptr sat_config_alloc_scoped(void) { + sat_config_ptr ctx = malloc(sizeof(sat_config)); + memset(ctx, 0, sizeof(sat_config)); + ctx->type = SAT_CONFIG_TYPE_SCOPED; + ctx->scopeTo = sat_config_scoped_scope_to; + ctx->readValue = sat_config_scoped_read_value; + ctx->info = malloc(sizeof(sat_config_scoped)); + memset(ctx->info, 0, sizeof(sat_config_scoped)); + return ctx; +} + +void sat_config_free_scoped(sat_config_ptr ctx) { + if(ctx == NULL || ctx->info == NULL || ctx->type != SAT_CONFIG_TYPE_SCOPED) return; + + // freeing parent will cause explosions beyond your imagination + sat_config_scoped_ptr info = (sat_config_scoped_ptr)ctx->info; + + if(info->prefix != NULL) + free(info->prefix); + + free(ctx->info); + free(ctx); +} diff --git a/src/config/config_stream.c b/src/config/config_stream.c new file mode 100644 index 0000000..169cbe4 --- /dev/null +++ b/src/config/config_stream.c @@ -0,0 +1,159 @@ +#include "config.h" + +sat_config_ptr sat_config_stream_scope_to(sat_config_ptr ctx, char *prefix) { + int pfxLength = strlen(prefix); + bool appendColon = prefix[pfxLength - 1] != ':'; + + int length = pfxLength + 1; + if(appendColon) length += 1; + + char *newPrefix = malloc(length); + memset(newPrefix, 0, length); + strcat(newPrefix, prefix); + if(appendColon) strcat(newPrefix, ":"); + + sat_config_ptr scoped = sat_config_alloc_scoped(); + sat_config_scoped_ptr scopedInfo = (sat_config_scoped_ptr)scoped->info; + scopedInfo->parent = ctx; + scopedInfo->prefix = newPrefix; + + return scoped; +} + +char* sat_config_stream_read_value(sat_config_ptr ctx, char *name, char *fallback) { + sat_config_stream_ptr info = (sat_config_stream_ptr)ctx->info; + + // this buffer is reasonable, you are not + char buffer[1024] = {0}; + char *value = NULL; + + // this buffer is also reasonable, you still aren't + // given this name is only used in strcmp and the index of the ending would be known + // there's honestly very little reason to not just replace the first trailing space + // with a \0 character and just passing buffer to strcmp + char cName[256] = {0}; + int cNameLength; + + // should retry for like 10 seconds + mtx_lock(info->lock); + + fseek(info->stream, info->offset, SEEK_SET); + + // probably do trimming later + while(fgets(buffer, sizeof buffer, info->stream) != NULL) { + if(buffer[0] == '\0' || buffer[0] == '\r' || buffer[0] == '\n' + || buffer[0] == ' ' || buffer[0] == '#' || buffer[0] == ';') + continue; + + // skip ridiculous lines too + if(buffer[(sizeof buffer) - 1] != '\0') { + memset(buffer, 0, sizeof buffer); + continue; + } + + cNameLength = 0; + while(buffer[cNameLength] != ' ' && buffer[cNameLength] != '\0') + ++cNameLength; + + cName[cNameLength] = '\0'; + memcpy(cName, buffer, cNameLength); + + if(strcmp(cName, name) == 0) { + int valueStart = cNameLength; + while(buffer[valueStart] == ' ') + ++valueStart; + + int valueEnd = valueStart; + while(buffer[valueEnd] != '\0' && buffer[valueEnd] != '\r' && buffer[valueEnd] != '\n') + ++valueEnd; + + int valueLength = valueEnd - valueStart; + if(valueLength > 0) { + value = malloc(valueLength + 1); + value[valueLength] = '\0'; + memcpy(value, &buffer[valueStart], valueLength); + } + break; + } + } + + mtx_unlock(info->lock); + + if(value == NULL && fallback != NULL) { + int length = strlen(fallback) + 1; + value = malloc(length); + memset(value, 0, length); + strcat(value, fallback); + } + + return value; +} + +sat_config_ptr sat_config_alloc_stream(void) { + sat_config_ptr ctx = malloc(sizeof(sat_config)); + memset(ctx, 0, sizeof(sat_config)); + ctx->type = SAT_CONFIG_TYPE_STREAM; + ctx->scopeTo = sat_config_stream_scope_to; + ctx->readValue = sat_config_stream_read_value; + + sat_config_stream_ptr info = malloc(sizeof(sat_config_stream)); + memset(info, 0, sizeof(sat_config_stream)); + ctx->info = (void*)info; + info->lock = malloc(sizeof(mtx_t)); + + return ctx; +} + +void sat_config_free_stream(sat_config_ptr ctx) { + if(ctx == NULL || ctx->info == NULL || ctx->type != SAT_CONFIG_TYPE_STREAM) return; + + sat_config_stream_ptr info = (sat_config_stream_ptr)ctx->info; + + if(info->lock != NULL) { + mtx_destroy(info->lock); + free(info->lock); + } + + if(info->ownsStream && info->stream != NULL) + free(info->stream); + + free(ctx->info); + free(ctx); +} + +int sat_config_stream_open(sat_config_ptr ctx, FILE *stream, bool ownsStream) { + if(ctx == NULL) return -10; // TODO: No. + if(ctx->type != SAT_CONFIG_TYPE_STREAM) return -11; + if(ctx->info == NULL) return -12; + + sat_config_stream_ptr info = (sat_config_stream_ptr)ctx->info; + + int err = mtx_init(info->lock, mtx_timed); + if(err == thrd_error) return -11; + + info->stream = stream; + info->ownsStream = ownsStream; + + // check for BOM + uint8_t buffer[3] = {0}; + int read = fread(buffer, sizeof(uint8_t), sizeof buffer, stream); + if(buffer[0] != 0xEF || buffer[1] != 0xBB || buffer[2] != 0xBF) + fseek(stream, -read, SEEK_CUR); + + info->offset = ftell(stream); + + return 0; +} + +int sat_config_stream_open_file(sat_config_ptr ctx, char *path) { + if(ctx == NULL) return -10; // TODO: No! + if(ctx->type != SAT_CONFIG_TYPE_STREAM) return -11; + if(ctx->info == NULL) return -12; + + FILE *stream = fopen(path, "rb"); + if(stream == NULL) return errno; + + fseek(stream, 0, SEEK_SET); + + return sat_config_stream_open(ctx, stream, true); +} diff --git a/src/context.c b/src/context.c new file mode 100644 index 0000000..16ab6a1 --- /dev/null +++ b/src/context.c @@ -0,0 +1,22 @@ +#include "context.h" + +satori_ctx_ptr satori_ctx_alloc(void) { + satori_ctx_ptr ctx = malloc(sizeof(satori_ctx)); + memset(ctx, 0, sizeof(satori_ctx)); + return ctx; +} + +void satori_ctx_free(satori_ctx_ptr ctx) { + if(ctx == NULL) return; + + if(ctx->futami != NULL) + sat_futami_free(ctx->futami); + + if(ctx->persist != NULL) + sat_persist_free(ctx->persist); + + if(ctx->config != NULL) + sat_config_free(ctx->config); + + free(ctx); +} diff --git a/src/curl_helper.c b/src/curl_helper.c new file mode 100644 index 0000000..5492177 --- /dev/null +++ b/src/curl_helper.c @@ -0,0 +1,163 @@ +#include "curl_helper.h" + +sat_curl_string_ptr sat_curl_string_alloc(void) { + sat_curl_string_ptr str = malloc(sizeof(sat_curl_string)); + sat_curl_string_init(str); + return str; +} + +void sat_curl_string_free(sat_curl_string_ptr str, bool freeArg) { + if(str == NULL) return; + + if(str->str) + free(str->str); + + if(freeArg) + free(str); +} + +void sat_curl_string_init(sat_curl_string_ptr str) { + str->length = 0; + str->str = malloc(sizeof(char)); + str->str[0] = '\0'; +} + +size_t sat_curl_string_write(char *ptr, size_t width, size_t count, sat_curl_string_ptr str) { + size_t newLength = width * count; + size_t totalLength = str->length + newLength; + + str->str = realloc(str->str, totalLength + 1); + memcpy(str->str + str->length, ptr, newLength); + str->str[totalLength] = '\0'; + + str->length = totalLength; + + return newLength; +} + +#define SAT_CURL_URL_SCHEME_FREE (0x01) +#define SAT_CURL_URL_SCHEME_CURLFREE (0x02) +#define SAT_CURL_URL_HOST_FREE (0x04) +#define SAT_CURL_URL_HOST_CURLFREE (0x08) +#define SAT_CURL_URL_PORT_FREE (0x10) +#define SAT_CURL_URL_PORT_CURLFREE (0x20) +#define SAT_CURL_URL_PATH_FREE (0x40) +#define SAT_CURL_URL_PATH_CURLFREE (0x80) + +sat_curl_url_ptr sat_curl_url_alloc(void) { + sat_curl_url_ptr url = malloc(sizeof(sat_curl_url)); + memset(url, 0, sizeof(sat_curl_url)); + return url; +} + +void sat_curl_url_free(sat_curl_url_ptr url, bool freeArg) { + if(url == NULL) return; + + if(url->scheme) { + if(url->flags & SAT_CURL_URL_SCHEME_CURLFREE) + curl_free(url->scheme); + else if(url->flags & SAT_CURL_URL_SCHEME_FREE) + free(url->scheme); + } + + if(url->host) { + if(url->flags & SAT_CURL_URL_HOST_CURLFREE) + curl_free(url->host); + else if(url->flags & SAT_CURL_URL_HOST_FREE) + free(url->host); + } + + if(url->port) { + if(url->flags & SAT_CURL_URL_PORT_CURLFREE) + curl_free(url->port); + else if(url->flags & SAT_CURL_URL_PORT_FREE) + free(url->port); + } + + if(url->path) { + if(url->flags & SAT_CURL_URL_PATH_CURLFREE) + curl_free(url->path); + else if(url->flags & SAT_CURL_URL_PATH_FREE) + free(url->path); + } + + if(freeArg) + free(url); +} + +// winapi eat your heart out +int sat_curl_url_parse(sat_curl_url_ptr url, char *str, char *scheme, char *host, char *port, char *path, bool supplyDefaults) { + if(str == NULL) return -1; + if(strlen(str) < 3) return -2; + + CURLU *curl = curl_url(); + if(!curl) return -3; + + bool skipScheme = false; + CURLUcode err = curl_url_set(curl, CURLUPART_URL, str, CURLU_NON_SUPPORT_SCHEME); + if(err == CURLUE_BAD_SCHEME) { + // compensating for browsers interpreting //host as no scheme but curl not + if(str[0] == '/' && str[1] == '/') { + str += 2; + skipScheme = true; + } else return err; + + err = curl_url_set(curl, CURLUPART_URL, str, CURLU_DEFAULT_SCHEME | CURLU_NON_SUPPORT_SCHEME); + } + + if(err) + return err; + + if(skipScheme || curl_url_get(curl, CURLUPART_SCHEME, &url->scheme, 0)) + url->scheme = scheme; + else + url->flags |= SAT_CURL_URL_SCHEME_CURLFREE; + + if(curl_url_get(curl, CURLUPART_HOST, &url->host, 0)) + url->host = host; + else + url->flags |= SAT_CURL_URL_HOST_CURLFREE; + + if(curl_url_get(curl, CURLUPART_PORT, &url->port, 0)) + url->port = port; + else + url->flags |= SAT_CURL_URL_PORT_CURLFREE; + + if(curl_url_get(curl, CURLUPART_PATH, &url->path, 0)) + path = path; + else + url->flags |= SAT_CURL_URL_PATH_CURLFREE; + + curl_url_cleanup(curl); + + if(supplyDefaults) { + if(url->port == NULL && url->scheme != NULL) { + if(!strcmp(url->scheme, "https") || !strcmp(url->scheme, "wss")) + url->port = "443"; + else if(!strcmp(url->scheme, "http") || !strcmp(url->scheme, "ws")) + url->port = "80"; + } + } + + return 0; +} + +void sat_curl_url_owns_scheme(sat_curl_url_ptr url) { + if(!(url->flags & SAT_CURL_URL_SCHEME_CURLFREE)) + url->flags |= SAT_CURL_URL_SCHEME_FREE; +} + +void sat_curl_url_owns_host(sat_curl_url_ptr url) { + if(!(url->flags & SAT_CURL_URL_HOST_CURLFREE)) + url->flags |= SAT_CURL_URL_HOST_FREE; +} + +void sat_curl_url_owns_port(sat_curl_url_ptr url) { + if(!(url->flags & SAT_CURL_URL_PORT_CURLFREE)) + url->flags |= SAT_CURL_URL_PORT_FREE; +} + +void sat_curl_url_owns_path(sat_curl_url_ptr url) { + if(!(url->flags & SAT_CURL_URL_PATH_CURLFREE)) + url->flags |= SAT_CURL_URL_PATH_FREE; +} diff --git a/src/futami.c b/src/futami.c new file mode 100644 index 0000000..75ffe4f --- /dev/null +++ b/src/futami.c @@ -0,0 +1,124 @@ +#include "futami.h" + +sat_futami_ptr sat_futami_alloc(void) { + return malloc(sizeof(sat_futami)); +} + +void sat_futami_free(sat_futami_ptr ctx) { + if(ctx == NULL) return; + + if(ctx->servers != NULL) { + while(ctx->serversCount > 0) { + --ctx->serversCount; + + if(ctx->servers[ctx->serversCount] != NULL) + free(ctx->servers[ctx->serversCount]); + } + + free(ctx->servers); + } + + + free(ctx); +} + +int sat_futami_load_json(sat_futami_ptr ctx, json_object *obj) { + if(!obj) return -1; + + json_object *value; + ctx->ping = json_object_object_get_ex(obj, "ping", &value) + ? json_object_get_int(value) + : 69; // change default to 30, testing parsage + + if(json_object_object_get_ex(obj, "servers", &value)) { + int serversCount = json_object_array_length(value); + ctx->serversCount = serversCount; + + char **servers = calloc(serversCount, sizeof(char*)); + ctx->servers = servers; + + json_object *arrValue; + int arrStrLen; + const char* arrStr; + for(int i = 0; i < serversCount; ++i) { + arrValue = json_object_array_get_idx(value, i); + if(arrValue == NULL) + continue; // ??? + + arrStrLen = json_object_get_string_len(arrValue); + servers[i] = malloc(arrStrLen + 1); + servers[i][arrStrLen] = '\0'; + + if(arrStrLen > 0) { + arrStr = json_object_get_string(arrValue); + memcpy(servers[i], arrStr, arrStrLen); + } + } + } else { + ctx->serversCount = 0; + ctx->servers = NULL; + } + + return 0; +} + +int sat_futami_load_json_file(sat_futami_ptr ctx, char *path) { + json_object *obj = json_object_from_file(path); + if(!obj) return -11; + + int err = sat_futami_load_json(ctx, obj); + + json_object_put(obj); + + return err; +} + +int sat_futami_load_json_string(sat_futami_ptr ctx, char *str) { + if(!str) return -21; + + json_object *obj = json_tokener_parse(str); + + int err = sat_futami_load_json(ctx, obj); + + json_object_put(obj); + + return err; +} + +// this only runs once during startup and is required to be completed before +// any connection is set up or module is loaded, but this should really be +// redone to be threaded or some shit rather than just block until done lol +int sat_futami_load_json_url(sat_futami_ptr ctx, char *url) { + if(!url) return -31; + + CURL *curl; + CURLcode res; + + curl = curl_easy_init(); + if(!curl) + return -32; + + sat_curl_string str; + sat_curl_string_init(&str); + + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 2L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, sat_curl_string_write); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &str); + curl_easy_setopt(curl, CURLOPT_USERAGENT, SATORI_USERAGENT); + + res = curl_easy_perform(curl); + if(res != CURLE_OK) // obv this random print shouldn't remain here + fprintf(stderr, "curl_easy_failed(): %s\r\n", curl_easy_strerror(res)); + + curl_easy_cleanup(curl); + + if(res != CURLE_OK) + return res; + + int err = sat_futami_load_json_string(ctx, str.str); + + sat_curl_string_free(&str, false); + + return err; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..2698be6 --- /dev/null +++ b/src/main.c @@ -0,0 +1,154 @@ +#include +#include +#include "satori.h" + +int main(void) { + puts(" _____ __ _"); + puts(" / ___/____ _/ /_____ _____(_)"); + puts(" \\__ \\/ __ `/ __/ __ \\/ ___/ /"); + puts(" ___/ / /_/ / /_/ /_/ / / / /"); + puts("/____/\\__,_/\\__/\\____/_/ /_/"); + puts(""); + puts("Satori Testing Hole"); + + puts("Initialising Libraries..."); + if(curl_global_init(CURL_GLOBAL_ALL)) { + puts("Failed to initialise cURL."); + return 0; + } + + + int err; + satori_ctx_ptr satori = satori_ctx_alloc(); + satori->startup = time(NULL); + + + satori->config = sat_config_alloc_stream(); + err = sat_config_stream_open_file(satori->config, "Satori.cfg"); + + if(err) { + printf("sat_config_stream_open_file: %d\r\n", err); + //return; + } else { + if(sat_config_read_bool(satori->config, "exampleCfg:unedited", false)) { + puts("Please review the configuration file before starting again."); + //return; + } + + char *test = sat_config_read_value(satori->config, "test", NULL); + printf("sat_config_read_value: %s\r\n", test); + free(test); + + + sat_config_ptr scoped = sat_config_scope_to(satori->config, "sockchat"); + + test = sat_config_read_value(scoped, "host", NULL); + printf("sat_config_read_value: %s\r\n", test); + free(test); + + sat_config_free(scoped); + + + sat_config_ptr scoped1 = sat_config_scope_to(satori->config, "flashii"); + sat_config_ptr scoped2 = sat_config_scope_to(scoped1, "broadcasts"); + + sat_config_free(scoped1); + + bool testb = sat_config_read_bool(scoped2, "enable", false); + printf("sat_config_read_bool: %s\r\n", testb ? "YES" : "NO"); + + sat_config_free(scoped2); + } + + + satori->persist = sat_persist_alloc(); + err = sat_persist_create_file(satori->persist, "Persist.dat"); + + if(err) { + printf("sat_persist_create_file: %d\r\n", err); + //return; + } else { + if(sat_persist_get_fii_forum_last_post_id(satori->persist) < 1) { + puts("Importing Flashii last forum post id from legacy configuration..."); + sat_persist_set_fii_forum_last_post_id( + satori->persist, + sat_config_read_u64(satori->config, "legacy:flashii:broadcasts:lastForumPostId", 0) + ); + sat_persist_flush(satori->persist); + } + + if(sat_persist_get_fii_user_last_register_id(satori->persist) < 1) { + puts("Importing Flashii last joined user id from legacy configuration..."); + sat_persist_set_fii_user_last_register_id( + satori->persist, + sat_config_read_u64(satori->config, "legacy:flashii:broadcasts:lastJoinUserId", 0) + ); + sat_persist_flush(satori->persist); + } + } + + + satori->futami = sat_futami_alloc(); + + char *futamiUrl = sat_config_read_value(satori->config, "futami:common", NULL); + if(futamiUrl == NULL) { + puts("Missing Futami URL from config, cannot continue startup."); + //return; + } + + err = sat_futami_load_json_url(satori->futami, futamiUrl); + free(futamiUrl); + + if(err) { + printf("sat_futami_load_json_url: %d\r\n", err); + //return; + } else { + printf("Ping Interval: %d seconds, Server count: %ld\r\n", satori->futami->ping, satori->futami->serversCount); + + for(size_t i = 0; i < satori->futami->serversCount; ++i) + printf(" - %s\r\n", satori->futami->servers[i]); + + printf("\r\n"); + } + + + if(satori->futami->serversCount < 1) { + puts("There are no available servers."); + //return; + } + + sat_curl_url url = {0}; + sat_curl_url_parse(&url, satori->futami->servers[0], "ws", NULL, NULL, "/", true); + + sat_sock_ptr sock = sat_sock_alloc(); + err = sat_sock_connect_tcp(sock, url.host, url.port); + sat_sock_set_blocking(sock, true); + + sat_websock_ptr websock = sat_websock_alloc(); + sat_websock_init(websock, sock); + + sat_websock_handshake wshs = {0}; + wshs.websock = websock; + wshs.host = url.host; + wshs.path = url.path; + sat_websock_handshake_gen_key(&wshs); + + err = sat_websock_handshake_send(&wshs); + printf("sat_websock_handshake_send: %d\r\n", err); + + + err = sat_websock_handshake_recv(&wshs); + printf("sat_websock_handshake_recv: %d\r\n", err); + + + + sat_sock_close(sock); + sat_sock_free(sock); + + satori_ctx_free(satori); + + puts("Cleaning up Libraries..."); + curl_global_cleanup(); + + return 0; +} diff --git a/src/pack.c b/src/pack.c new file mode 100644 index 0000000..28119f2 --- /dev/null +++ b/src/pack.c @@ -0,0 +1,158 @@ +#include "pack.h" + +static inline void sat_pack_16be(uint8_t *buffer, uint16_t num) { + *buffer++ = num >> 8; + *buffer++ = num; +} +static inline void sat_pack_16le(uint8_t *buffer, uint16_t num) { + *buffer++ = num; + *buffer++ = num >> 8; +} + +static inline void sat_pack_32be(uint8_t *buffer, uint32_t num) { + *buffer++ = num >> 24; + *buffer++ = num >> 16; + *buffer++ = num >> 8; + *buffer++ = num; +} +static inline void sat_pack_32le(uint8_t *buffer, uint32_t num) { + *buffer++ = num; + *buffer++ = num >> 8; + *buffer++ = num >> 16; + *buffer++ = num >> 24; +} + +static inline void sat_pack_64be(uint8_t *buffer, uint64_t num) { + *buffer++ = num >> 56; + *buffer++ = num >> 48; + *buffer++ = num >> 40; + *buffer++ = num >> 32; + *buffer++ = num >> 24; + *buffer++ = num >> 16; + *buffer++ = num >> 8; + *buffer++ = num; +} +static inline void sat_pack_64le(uint8_t *buffer, uint64_t num) { + *buffer++ = num; + *buffer++ = num >> 8; + *buffer++ = num >> 16; + *buffer++ = num >> 24; + *buffer++ = num >> 32; + *buffer++ = num >> 40; + *buffer++ = num >> 48; + *buffer++ = num >> 56; +} + + +void sat_pack_i16be(uint8_t *buffer, int16_t num) { sat_pack_16be(buffer, num); } +void sat_pack_u16be(uint8_t *buffer, uint16_t num) { sat_pack_16be(buffer, num); } + +void sat_pack_i32be(uint8_t *buffer, int32_t num) { sat_pack_32be(buffer, num); } +void sat_pack_u32be(uint8_t *buffer, uint32_t num) { sat_pack_32be(buffer, num); } + +void sat_pack_i64be(uint8_t *buffer, int64_t num) { sat_pack_64be(buffer, num); } +void sat_pack_u64be(uint8_t *buffer, uint64_t num) { sat_pack_64be(buffer, num); } + +void sat_pack_i16le(uint8_t *buffer, int16_t num) { sat_pack_16le(buffer, num); } +void sat_pack_u16le(uint8_t *buffer, uint16_t num) { sat_pack_16le(buffer, num); } + +void sat_pack_i32le(uint8_t *buffer, int32_t num) { sat_pack_32le(buffer, num); } +void sat_pack_u32le(uint8_t *buffer, uint32_t num) { sat_pack_32le(buffer, num); } + +void sat_pack_i64le(uint8_t *buffer, int64_t num) { sat_pack_64le(buffer, num); } +void sat_pack_u64le(uint8_t *buffer, uint64_t num) { sat_pack_64le(buffer, num); } + + +static inline uint16_t sat_unpack_16be(uint8_t *buffer) { + return ((uint16_t)buffer[0] << 8) | buffer[1]; +} +static inline uint16_t sat_unpack_16le(uint8_t *buffer) { + return ((uint16_t)buffer[1] << 8) | buffer[0]; +} + +static inline uint32_t sat_unpack_32be(uint8_t *buffer) { + return ((uint32_t)buffer[0] << 24) | ((uint32_t)buffer[1] << 16) + | ((uint32_t)buffer[2] << 8) | buffer[3]; +} +static inline uint32_t sat_unpack_32le(uint8_t *buffer) { + return ((uint32_t)buffer[3] << 24) | ((uint32_t)buffer[2] << 16) + | ((uint32_t)buffer[1] << 8) | buffer[0]; +} + +static inline uint64_t sat_unpack_64be(uint8_t *buffer) { + return ((uint64_t)buffer[0] << 56) | ((uint64_t)buffer[1] << 48) + | ((uint64_t)buffer[2] << 40) | ((uint64_t)buffer[3] << 32) + | ((uint64_t)buffer[4] << 24) | ((uint64_t)buffer[5] << 16) + | ((uint64_t)buffer[6] << 8) | buffer[7]; +} +static inline uint64_t sat_unpack_64le(uint8_t *buffer) { + return ((uint64_t)buffer[7] << 56) | ((uint64_t)buffer[6] << 48) + | ((uint64_t)buffer[5] << 40) | ((uint64_t)buffer[4] << 32) + | ((uint64_t)buffer[3] << 24) | ((uint64_t)buffer[2] << 16) + | ((uint64_t)buffer[1] << 8) | buffer[0]; +} + + +static inline int16_t sat_sign_i16(uint16_t num) { + if(num <= 0x7FFFu) + return num; + + return -1 - (int16_t)(0xFFFF - num); +} + +static inline int32_t sat_sign_i32(uint32_t num) { + if(num <= 0x7FFFFFFFu) + return num; + + return -1 - (int32_t)(0xFFFFFFFFu - num); +} + +static inline int64_t sat_sign_i64(uint64_t num) { + if(num <= 0x7FFFFFFFFFFFFFFFu) + return num; + + return -1 - (int64_t)(0xFFFFFFFFFFFFFFFFu - num); +} + + +int16_t sat_unpack_i16be(uint8_t *buffer) { + return sat_sign_i16(sat_unpack_16be(buffer)); +} +uint16_t sat_unpack_u16be(uint8_t *buffer) { + return sat_unpack_16be(buffer); +} + +int32_t sat_unpack_i32be(uint8_t *buffer) { + return sat_sign_i32(sat_unpack_32be(buffer)); +} +uint32_t sat_unpack_u32be(uint8_t *buffer) { + return sat_unpack_32be(buffer); +} + +int64_t sat_unpack_i64be(uint8_t *buffer) { + return sat_sign_i64(sat_unpack_64be(buffer)); +} +uint64_t sat_unpack_u64be(uint8_t *buffer) { + return sat_unpack_64be(buffer); +} + +int16_t sat_unpack_i16le(uint8_t *buffer) { + return sat_sign_i16(sat_unpack_16le(buffer)); +} +uint16_t sat_unpack_u16le(uint8_t *buffer) { + return sat_unpack_16le(buffer); +} + +int32_t sat_unpack_i32le(uint8_t *buffer) { + return sat_sign_i32(sat_unpack_32le(buffer)); +} +uint32_t sat_unpack_u32le(uint8_t *buffer) { + return sat_unpack_32le(buffer); +} + +int64_t sat_unpack_i64le(uint8_t *buffer) { + return sat_sign_i64(sat_unpack_64le(buffer)); +} +uint64_t sat_unpack_u64le(uint8_t *buffer) { + return sat_unpack_64le(buffer); +} diff --git a/src/persist.c b/src/persist.c new file mode 100644 index 0000000..e85bc21 --- /dev/null +++ b/src/persist.c @@ -0,0 +1,147 @@ +#include "persist.h" + +#define SAT_PERSIST_MAGIC (0xDEAFB00B) +#define SAT_PERSIST_VERSION (0x01) +#define SAT_PERSIST_HEADER (0x10) +#define SAT_PERSIST_FII_FORUM_LAST_POST_ID (0x10) +#define SAT_PERSIST_FII_USER_LAST_REGISTER_ID (0x18) + +// that version number is going to come in useful cuz i want to expand the Persist.dat format +// v2 should add the ability to define a field by name +// i'm avoiding using the word "table" because in my eyes that implies a single block of data +// rather the table should be a linked list with the first entry pointed at from the header +// this way the existing fields in the v1 format don't have to be moved elsewhere during conversion +// essentially we got [DEAFB00B 02000000 20000000 00000000] in the header, +// the pointer to the first entry is at offset 0x08 in the header and should be interpreted as a U32 +// why not a U64? cuz if we're gonna need more than the size a 32-bit integer offers we have worse problems than offset overflows... +// each entry will have a variable size, starting with a null terminated ascii string, followed by a byte for the type, +// followed by a U32 pointing to the location of the data, followed by a final U32 pointing to the next entry +// !!! this would remove the "backwards compatibility" of not having to move the data, but wouldn't it make more sense to +// !!! [NAME TYPE PNEXT VALUE] rather than [NAME TYPE PNEXT PVALUE]? +// i do not know how i'd go about values of variable length, perhaps the function that allocates a space in the file can review +// empty space or some kind of index can be kept, perhaps just add an asterisk saying "do not variable length you will regret" +// perhaps leave variable length shit for a v3 format + +sat_persist_ptr sat_persist_alloc(void) { + sat_persist_ptr ctx = malloc(sizeof(sat_persist)); + memset(ctx, 0, sizeof(sat_persist)); + ctx->lock = malloc(sizeof(mtx_t)); + return ctx; +} + +void sat_persist_free(sat_persist_ptr ctx) { + if(!ctx) return; + + if(ctx->lock != NULL) { + mtx_destroy(ctx->lock); + free(ctx->lock); + } + + if(ctx->ownsStream && ctx->stream) + free(ctx->stream); + + free(ctx); +} + +int sat_persist_create(sat_persist_ptr ctx, FILE *stream, bool ownsStream) { + if(ctx == NULL) return -10; // TODO: No. + + int err; + err = mtx_init(ctx->lock, mtx_timed); + if(err == thrd_error) return -11; + + ctx->stream = stream; + ctx->ownsStream = ownsStream; + ctx->offset = ftell(stream); + + err = sat_persist_header_refresh(ctx); + if(err) return err; + + return 0; +} + +int sat_persist_create_file(sat_persist_ptr ctx, char *path) { + if(ctx == NULL) return -10; // TODO: No! + + FILE *stream = fopen(path, "rb+"); + if(stream == NULL) stream = fopen(path, "wb+"); + if(stream == NULL) return errno; + + fseek(stream, 0, SEEK_SET); + + return sat_persist_create(ctx, stream, true); +} + +void sat_persist_flush(sat_persist_ptr ctx) { + if(ctx != NULL && ctx->stream != NULL) + fflush(ctx->stream); +} + +int sat_persist_header_refresh(sat_persist_ptr ctx) { + uint8_t buffer[SAT_PERSIST_HEADER] = {0}; + + uint32_t magic; + + mtx_lock(ctx->lock); + size_t read = fread(buffer, sizeof(uint8_t), SAT_PERSIST_HEADER, ctx->stream); + mtx_unlock(ctx->lock); + + if(read == 0) { + sat_pack_u32be(buffer, SAT_PERSIST_MAGIC); + + buffer[4] = SAT_PERSIST_VERSION; + + mtx_lock(ctx->lock); + fseek(ctx->stream, ctx->offset, SEEK_SET); + fwrite(buffer, sizeof(uint8_t), SAT_PERSIST_HEADER, ctx->stream); + fflush(ctx->stream); + mtx_unlock(ctx->lock); + } else if(read < SAT_PERSIST_HEADER) { + return -2; // TODO: also no + } else { + magic = sat_unpack_u32be(buffer); + + if(magic != SAT_PERSIST_MAGIC) + return -3; // TODO: still no + + if(buffer[4] < 1 || buffer[4] > SAT_PERSIST_VERSION) + return -4; // TODO: again, no + } + + return 0; +} + +inline uint64_t sat_persist_read_u64(sat_persist_ptr ctx, uint32_t offset) { + uint8_t buffer[sizeof(uint64_t)] = {0}; + + mtx_lock(ctx->lock); + fseek(ctx->stream, ctx->offset + offset, SEEK_SET); + int read = fread(buffer, sizeof(uint8_t), sizeof(uint64_t), ctx->stream); // today we assume things will just work + mtx_unlock(ctx->lock); + if(read < 1) return 0; + + return sat_unpack_u64le(buffer); +} +inline void sat_persist_write_u64(sat_persist_ptr ctx, uint32_t offset, uint64_t value) { + uint8_t buffer[sizeof(uint64_t)] = {0}; + sat_pack_u64le(buffer, value); + + mtx_lock(ctx->lock); + fseek(ctx->stream, ctx->offset + offset, SEEK_SET); + fwrite(buffer, sizeof(uint8_t), sizeof(uint64_t), ctx->stream); + mtx_unlock(ctx->lock); +} + +uint64_t sat_persist_get_fii_forum_last_post_id(sat_persist_ptr ctx) { + return sat_persist_read_u64(ctx, SAT_PERSIST_FII_FORUM_LAST_POST_ID); +} +void sat_persist_set_fii_forum_last_post_id(sat_persist_ptr ctx, uint64_t postId) { + sat_persist_write_u64(ctx, SAT_PERSIST_FII_FORUM_LAST_POST_ID, postId); +} + +uint64_t sat_persist_get_fii_user_last_register_id(sat_persist_ptr ctx) { + return sat_persist_read_u64(ctx, SAT_PERSIST_FII_USER_LAST_REGISTER_ID); +} +void sat_persist_set_fii_user_last_register_id(sat_persist_ptr ctx, uint64_t userId) { + sat_persist_write_u64(ctx, SAT_PERSIST_FII_USER_LAST_REGISTER_ID, userId); +} diff --git a/src/sock/sock_common.c b/src/sock/sock_common.c new file mode 100644 index 0000000..69e756c --- /dev/null +++ b/src/sock/sock_common.c @@ -0,0 +1,104 @@ +#include "sock.h" + +sat_sock_ptr sat_sock_alloc(void) { + sat_sock_ptr ctx = malloc(sizeof(sat_sock)); + memset(ctx, 0, sizeof(sat_sock)); + return ctx; +} + +void sat_sock_free(sat_sock_ptr ctx) { + if(ctx == NULL) return; + + free(ctx); +} + +int sat_sock_connect(sat_sock_ptr ctx, int addrFamily, int sockType, char *host, char *port) { + struct addrinfo hints; + memset(&hints, 0, sizeof hints); + hints.ai_family = addrFamily; + hints.ai_socktype = sockType; + + struct addrinfo *servInfo; + + int err = getaddrinfo(host, port, &hints, &servInfo); + if(err) { + fprintf(stderr, "getaddrinfo(): %s\r\n", gai_strerror(err)); + return err; + } + + int sock = -1; + struct addrinfo *p; + for(p = servInfo; p != NULL; p = p->ai_next) { + if((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { + perror("socket():"); + continue; + } + + if(connect(sock, p->ai_addr, p->ai_addrlen) == -1) { + close(sock); + perror("connect():"); + sock = -1; + continue; + } + + break; + } + + freeaddrinfo(servInfo); + + if(sock != -1) + ctx->sock = sock; + + return sock; +} + +int sat_sock_connect_tcp(sat_sock_ptr ctx, char *host, char *port) { + return sat_sock_connect(ctx, AF_UNSPEC, SOCK_STREAM, host, port); +} + +void sat_sock_close(sat_sock_ptr ctx) { + if(ctx->sock != -1) { + close(ctx->sock); + } +} + +int sat_sock_recv(sat_sock_ptr ctx, uint8_t *buffer, size_t size) { + return recv(ctx->sock, buffer, size, 0); +} + +int sat_sock_send(sat_sock_ptr ctx, uint8_t *buffer, size_t count) { + return send(ctx->sock, (char*)buffer, count, 0); +} + +bool sat_sock_get_blocking(sat_sock_ptr ctx) { + return (fcntl(ctx->sock, F_GETFL) & O_NONBLOCK) > 0; +} + +void sat_sock_set_blocking(sat_sock_ptr ctx, bool blocking) { + int flags = fcntl(ctx->sock, F_GETFL); + if(blocking) + flags |= O_NONBLOCK; + else + flags &= ~O_NONBLOCK; + + fcntl(ctx->sock, F_SETFL, flags); +} + +int sat_sock_select(sat_sock_ptr ctx, int timeout) { + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(ctx->sock, &rfds); + + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + + int state = select(ctx->sock + 1, &rfds, NULL, NULL, &tv); + if(state == -1 || state == 0) + return state; + + if(!FD_ISSET(ctx->sock, &rfds)) + return -2; + + return state; +} diff --git a/src/websock/websock.c b/src/websock/websock.c new file mode 100644 index 0000000..d3dc3d3 --- /dev/null +++ b/src/websock/websock.c @@ -0,0 +1,88 @@ +#include "websock.h" + +sat_websock_ptr sat_websock_alloc(void) { + sat_websock_ptr websock = malloc(sizeof(sat_websock)); + memset(websock, 0, sizeof(sat_websock)); + return websock; +} + +void sat_websock_free(sat_websock_ptr ws) { + if(ws == NULL) return; + + if(ws->closeInfo != NULL) + free(ws->closeInfo); + + if(ws->buffer != NULL) + free(ws->buffer); + + free(ws); +} + +void sat_websock_init(sat_websock_ptr ws, sat_sock_ptr sock) { + ws->sock = sock; + ws->buffer = sat_buffer_alloc(); +} + +void sat_websock_tidy(sat_websock_ptr ws) { + sat_buffer_tidy(ws->buffer); +} + +void sat_websock_close(sat_websock_ptr ws, int closeCode, char *closeReason) { + // send close frame + + // should the sock be nuked right away? + sat_sock_close(ws->sock); +} + +bool sat_websock_is_closed(sat_websock_ptr ws) { + return ws != NULL && ws->closeInfo == NULL; +} + +int sat_websock_buffer(sat_websock_ptr ws, size_t want) { + size_t total = sat_buffer_available(ws->buffer); + if(total > want) + return total; + + want -= total; + + int read; + char buffer[1024] = {0}; + while(total < want) { + if(sat_sock_select(ws->sock, 2) > 0) { + read = sat_sock_recv(ws->sock, (uint8_t*)buffer, 1024); + if(read == -1) + return read; + + total += read; + } + } + + return total; +} + +int sat_websock_recv(sat_websock_ptr ws, uint8_t *buffer, size_t size) { + size_t buffered = sat_buffer_available(ws->buffer); + if(buffered >= size) + return sat_buffer_read(ws->buffer, (char*)buffer, size); + + size -= buffered; + + size_t total = 0; + int read; + while(total < size) { + if(sat_sock_select(ws->sock, 2) > 0) { + read = sat_sock_recv(ws->sock, buffer, size); + if(read == -1) + return read; + + buffer += read; + total += read; + } + } + + return total; +} + +int sat_websock_send(sat_websock_ptr ws, uint8_t *buffer, size_t count) { + return sat_sock_send(ws->sock, buffer, count); +} diff --git a/src/websock/websock_handshake.c b/src/websock/websock_handshake.c new file mode 100644 index 0000000..f9c0464 --- /dev/null +++ b/src/websock/websock_handshake.c @@ -0,0 +1,71 @@ +#include +#include "websock.h" + +#define SAT_WEBSOCK_HANDSHAKE_UNIQUE "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" +#define SAT_WEBSOCK_HANDSHAKE_VERSION_10 "HTTP/1.0 " +#define SAT_WEBSOCK_HANDSHAKE_VERSION_11 "HTTP/1.1 " +#define SAT_WEBSOCK_HANDSHAKE_LINE_END (0x0D0A) + +sat_websock_handshake_ptr sat_websock_handshake_alloc(void) { + sat_websock_handshake_ptr hs = malloc(sizeof(sat_websock_handshake)); + memset(hs, 0, sizeof(sat_websock_handshake)); + return hs; +} + +void sat_websock_handshake_free(sat_websock_handshake_ptr hs, bool freeArg) { + if(hs == NULL) return; + + if(freeArg) free(hs); +} + +int sat_websock_handshake_gen_key(sat_websock_handshake_ptr hs) { + return getrandom(hs->key, 16, 0); +} + +int sat_websock_handshake_send(sat_websock_handshake_ptr hs) { + char buffer[1024] = {0}; + size_t count = 0; + + count += sprintf(buffer + count, "GET %s HTTP/1.1\r\n", hs->path); + count += sprintf(buffer + count, "Host: %s\r\n", hs->host); + count += sprintf(buffer + count, "Upgrade: websocket\r\n"); + count += sprintf(buffer + count, "Connection: Upgrade\r\n"); + count += sprintf(buffer + count, "Sec-WebSocket-Version: 13\r\n"); + count += sprintf(buffer + count, "Sec-WebSocket-Key: %s\r\n", hs->key); + if(hs->origin != NULL) count += sprintf(buffer + count, "Origin: %s\r\n", hs->origin); + if(hs->protocol != NULL) count += sprintf(buffer + count, "Sec-WebSocket-Protocol: %s\r\n", hs->protocol); + count += sprintf(buffer + count, "\r\n"); + + return sat_websock_send(hs->websock, (uint8_t*)buffer, count); +} + +int sat_websock_handshake_recv(sat_websock_handshake_ptr hs) { + char buffer[1024] = {0}; + + sat_websock_buffer(hs->websock, 100); + + int expect = strlen(SAT_WEBSOCK_HANDSHAKE_VERSION_11); + int read = sat_websock_recv(hs->websock, (uint8_t*)buffer, expect); + if(read != expect) return -1; + if(strcmp(SAT_WEBSOCK_HANDSHAKE_VERSION_11, buffer) != 0 + && strcmp(SAT_WEBSOCK_HANDSHAKE_VERSION_10, buffer) != 0) + return -2; + + uint16_t checkCrLf = 0; + + int i; + for(i = 0; i < sizeof buffer && checkCrLf != SAT_WEBSOCK_HANDSHAKE_LINE_END; ++i) { + buffer[i] = sat_buffer_getc(hs->websock->buffer); + checkCrLf <<= 8; + checkCrLf |= buffer[i]; + } + buffer[i] = '\0'; + + if(sscanf(buffer, "%hd %s", &hs->statusCode, hs->statusReason) != 2) + return -3; + + hs->completed = true; + hs->upgraded = hs->statusCode == 101; + + return 0; +} diff --git a/win-amd64.sh b/win-amd64.sh new file mode 100644 index 0000000..686a542 --- /dev/null +++ b/win-amd64.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +TARGET=satori-amd64.exe \ +CC=x86_64-w64-mingw32-gcc \ +CXX=x86_64-w64-mingw32-g++ \ +LDFLAGS="-static-libgcc -static-libstdc++" \ +make rebuild diff --git a/win-ia32.sh b/win-ia32.sh new file mode 100644 index 0000000..ece5171 --- /dev/null +++ b/win-ia32.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +TARGET=satori-ia32.exe \ +CC=i686-w64-mingw32-gcc \ +CXX=i686-w64-mingw32-g++ \ +LDFLAGS="-static-libgcc -static-libstdc++" \ +make rebuild