This commit is contained in:
flash 2023-12-27 01:35:22 +01:00
commit 7451820765
30 changed files with 2026 additions and 0 deletions

1
.gitattributes vendored Normal file
View file

@ -0,0 +1 @@
* text=auto

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
bin/
*.o
*.cfg
*.dat

48
Makefile Normal file
View file

@ -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

40
include/buffer.h Normal file
View file

@ -0,0 +1,40 @@
#ifndef H_SATORI_BUFFER
#define H_SATORI_BUFFER
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#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

56
include/config.h Normal file
View file

@ -0,0 +1,56 @@
#ifndef H_SATORI_CONFIG
#define H_SATORI_CONFIG
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <threads.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <stdint.h>
#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

21
include/context.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef H_SATORI_CONTEXT
#define H_SATORI_CONTEXT
#include <stdlib.h>
#include <string.h>
#include <time.h>
#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

36
include/curl_helper.h Normal file
View file

@ -0,0 +1,36 @@
#ifndef H_SATORI_CURL_HELPER
#define H_SATORI_CURL_HELPER
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <curl/curl.h>
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

25
include/futami.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef H_SATORI_FUTAMI
#define H_SATORI_FUTAMI
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include <json-c/json.h>
#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

20
include/macros.h Normal file
View file

@ -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

42
include/pack.h Normal file
View file

@ -0,0 +1,42 @@
#ifndef H_SATORI_PACK
#define H_SATORI_PACK
#include <stdint.h>
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

34
include/persist.h Normal file
View file

@ -0,0 +1,34 @@
#ifndef H_SATORI_PERIST
#define H_SATORI_PERIST
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <threads.h>
#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

15
include/satori.h Normal file
View file

@ -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

33
include/sock.h Normal file
View file

@ -0,0 +1,33 @@
#ifndef H_SATORI_SOCK
#define H_SATORI_SOCK
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
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

80
include/websock.h Normal file
View file

@ -0,0 +1,80 @@
#ifndef H_SATORI_WEBSOCK
#define H_SATORI_WEBSOCK
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#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

179
src/buffer.c Normal file
View file

@ -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++];
}

59
src/config/config.c Normal file
View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);
}

159
src/config/config_stream.c Normal file
View file

@ -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);
}

22
src/context.c Normal file
View file

@ -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);
}

163
src/curl_helper.c Normal file
View file

@ -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;
}

124
src/futami.c Normal file
View file

@ -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;
}

154
src/main.c Normal file
View file

@ -0,0 +1,154 @@
#include <stdio.h>
#include <time.h>
#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;
}

158
src/pack.c Normal file
View file

@ -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);
}

147
src/persist.c Normal file
View file

@ -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);
}

104
src/sock/sock_common.c Normal file
View file

@ -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;
}

88
src/websock/websock.c Normal file
View file

@ -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);
}

View file

@ -0,0 +1,71 @@
#include <sys/random.h>
#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;
}

7
win-amd64.sh Normal file
View file

@ -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

7
win-ia32.sh Normal file
View file

@ -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