i finally get to go home

not touching a computer for a while weow
This commit is contained in:
reemo 2023-12-28 18:08:23 +00:00
parent 8ff46c40b9
commit 1b775f59cc
6 changed files with 410 additions and 62 deletions

View file

@ -2,7 +2,7 @@ CC = gcc
CFLAGS = -Wall -g CFLAGS = -Wall -g
LDFLAGS = -lncurses LDFLAGS = -lncurses
OBJS = main.o wsock.o OBJS = main.o wsock.o ctx.o
DEPS = main.c DEPS = main.c
all: clii all: clii

179
src/ctx.c Normal file
View file

@ -0,0 +1,179 @@
#include "ctx.h"
#define DEF_CTX_LENS 8
struct {
int self;
user_t* users;
channel_t* channels;
int users_length,
channels_length;
} _ctx;
static void _users_init(user_t* users, int n) {
for(int i = 0; i < n; ++i)
users[i].id = -1;
}
static void _channels_init(channel_t* channels, int n) {
for(int i = 0; i < n; ++i)
channels[i].name[0] = '\0';
}
void ctx_init() {
_ctx.self = -1;
_ctx.users =
malloc(sizeof(user_t) * DEF_CTX_LENS);
_ctx.channels =
malloc(sizeof(channel_t) * DEF_CTX_LENS);
_ctx.users_length
= _ctx.channels_length
= DEF_CTX_LENS;
_users_init(_ctx.users, _ctx.users_length);
_channels_init(_ctx.channels, _ctx.channels_length);
}
void ctx_free() {
free(_ctx.users);
free(_ctx.channels);
}
int parse_color(const char* color) {
return 0;
}
uint64_t parse_flags(const char* flags) {
int length = strlen(flags);
uint64_t out = 0;
for(int i = 0; i < length; --i)
if(flags[length - i - 1] == '1')
out |= 1ull << i;
return out;
}
void parse_perms
(const char* perms, int n, int* out)
{
const char* ptr = perms;
for(int i = 0; i < n; ++i)
out[i] = (int)strtol(ptr, (char**)&ptr, 10);
}
/** USER FUNCTIONS **/
/**********************/
static user_t* _get_user(int id) {
for(int i = 0; i < _ctx.users_length; ++i)
if(_ctx.users[i].id == id)
return _ctx.users + i;
return NULL;
}
static inline user_t* _get_empty_user() {
return _get_user(-1);
}
void add_user(const user_t* user) {
if(user->id == -1)
return;
user_t* to = _get_user(user->id);
if(to == NULL)
to = _get_empty_user();
if(to == NULL) {
_ctx.users = realloc(_ctx.users,
sizeof(user_t) * _ctx.users_length * 2);
_users_init(
_ctx.users + _ctx.users_length,
_ctx.users_length
);
to = _ctx.users + _ctx.users_length;
_ctx.users_length *= 2;
}
memcpy(to, user, sizeof(user_t));
}
const user_t* get_user(int id) {
if(id == -1)
return NULL;
return _get_user(id);
}
void rm_user(int id) {
if(id == -1)
return;
user_t* user = _get_user(id);
if(user != NULL)
user->id = -1;
}
void set_self(int id) {
_ctx.self = id;
}
const user_t* get_self() {
return get_user(_ctx.self);
}
int get_self_id() {
return _ctx.self;
}
/** CHANNEL FUNCTIONS **/
/*************************/
static channel_t* _get_channel(const char* name) {
for(int i = 0; i < _ctx.channels_length; ++i)
if(strcmp(_ctx.channels[i].name, name) == 0)
return _ctx.channels + i;
return NULL;
}
static channel_t* _get_empty_channel() {
return _get_channel("");
}
void add_channel(const channel_t* channel) {
if(channel->name[0] == '\0')
return;
channel_t* to = _get_channel(channel->name);
if(to == NULL)
to = _get_empty_channel();
if(to == NULL) {
_ctx.channels = realloc(_ctx.channels,
sizeof(channel_t) * _ctx.channels_length * 2);
_channels_init(
_ctx.channels + _ctx.channels_length,
_ctx.channels_length
);
to = _ctx.channels + _ctx.channels_length;
_ctx.channels_length *= 2;
}
memcpy(to, channel, sizeof(channel_t));
}
const channel_t* get_channel(const char* name) {
if(name[0] == '\0')
return NULL;
return _get_channel(name);
}
void rm_channel(const char* name) {
if(name[0] == '\0')
return;
channel_t* channel = _get_channel(name);
if(channel != NULL)
channel->name[0] = '\0';
}

46
src/ctx.h Normal file
View file

@ -0,0 +1,46 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#define USER_PERMS 4
#define USER_MOD 0
#define USER_LOGS 1
#define USER_NICK 2
#define USER_CHAN 3
typedef struct {
int id;
char name[64];
int perms[USER_PERMS];
int color;
} user_t;
#define CHAN_PARAMS 2
#define CHAN_PWD 0
#define CHAN_TMP 1
typedef struct {
char name[64];
int params[CHAN_PARAMS];
} channel_t;
void ctx_init();
void ctx_free();
int parse_color(const char*);
uint64_t parse_flags(const char*);
void parse_perms(const char*, int, int*);
void add_user(const user_t*);
const user_t* get_user(int);
void rm_user(int);
void set_self(int);
const user_t* get_self();
int get_self_id();
void add_channel(const channel_t*);
const channel_t* get_channel(const char*);
void rm_channel(const char*);

View file

@ -2,8 +2,10 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <time.h>
#include "wsock.h" #include "wsock.h"
#include "ctx.h"
/** CONSTS & UTILITIES **/ /** CONSTS & UTILITIES **/
/**************************/ /**************************/
@ -19,19 +21,27 @@ const char USAGE[] =
"on any web browser that is logged in."; "on any web browser that is logged in.";
const char* _home(const char*); const char* _home(const char*);
void _ping();
/** GLOBALS **/ /** GLOBALS **/
/***************/ /***************/
struct { struct {
int running, exit_poll;
char session[256]; char session[256];
wsock_t* sock;
WINDOW *wchat, *wentry;
user_t bot;
} _G; } _G;
/** MAIN **/ /** MAIN **/
/************/ /************/
void print_msg(const user_t*, const char*);
void parse(char*);
int main(int argc, char** argv) { int main(int argc, char** argv) {
srand(time(NULL)); srand(time(NULL));
FILE* fp; FILE* fp;
@ -67,62 +77,152 @@ int main(int argc, char** argv) {
return -1; return -1;
} }
char buf[2048], *get; int in_at = 0;
char buf[2048], input[256], *get;
printf("Connecting to Flashii ...\n"); printf("Connecting to Flashii ...\n");
wsock_t* sock = wsock_open(FII_ADDR, "/", 80); _G.sock = wsock_open(FII_ADDR, "/", 80);
printf("Authenticating ...\n");
sprintf(buf, "1\tMisuzu\t%s", _G.session);
wsock_send_str(sock, buf);
fd_set fds; fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
FD_SET(sock->sock, &fds);
struct timeval tout; struct timeval tout;
tout.tv_sec = 5;
tout.tv_usec = 0;
int buf_at = 0;
for(;;) {
printf("**** pinged ****\n");
wsock_ping(sock, NULL);
int sel = select(sock->sock + 1, &fds, NULL, NULL, &tout);
if(sel > 0) {
if(FD_ISSET(0, &fds)) {
buf_at += fread(buf + buf_at, 1, sizeof(buf) - buf_at, 0);
buf[buf_at] = '\0';
printf("%s\n", buf);
}
if(FD_ISSET(sock->sock, &fds)) {
wsock_recv(sock, &get);
printf("%s\n", get);
free(get);
}
}
}
initscr(); initscr();
start_color();
raw(); noecho(); raw(); noecho();
keypad(stdscr, TRUE); keypad(stdscr, TRUE);
/*for(;;) { _G.wchat = newwin(LINES - 2, COLS, 0, 0);
scrollok(_G.wchat, TRUE);
idlok(_G.wchat, TRUE);
}*/ _G.wentry = newwin(1, COLS, LINES - 1, 0);
scrollok(_G.wentry, TRUE);
idlok(_G.wentry, TRUE);
printw("hi mom"); mvhline(LINES - 2, 0, ACS_HLINE, COLS - 1);
refresh(); refresh();
halfdelay(1);
while(getch() == ERR);
//getch();
wprintw(_G.wchat, "Authenticating ...\n");
sprintf(buf, "1\tMisuzu\t%s", _G.session);
wsock_send_str(_G.sock, buf);
_G.bot.id = -1;
_G.bot.name = "SERVER";
memset(_G.bot.perms, 0, sizeof(_G.bot.perms));
_G.bot.color = 0; // todo: change to gray
ctx_init();
_G.exit_poll = 0;
_G.running = 1;
while(_G.running) {
_ping();
int fds_max;
FD_ZERO(&fds);
FD_SET(fileno(stdin), &fds);
if(wsock_is_open(_G.sock)) {
FD_SET(_G.sock->sock, &fds);
fds_max = _G.sock->sock;
} else
fds_max = fileno(stdin);
tout.tv_sec = 5;
tout.tv_usec = 0;
int sel = select(fds_max + 1, &fds, NULL, NULL, &tout);
if(sel > 0) {
if(FD_ISSET(fileno(stdin), &fds)) {
//if(_G.exit_poll)
// break;
int ch = getch();
if(ch == 27)
break;
if(ch == '\n') {
input[in_at] = '\0';
sprintf(buf, "2\t%i\t%s", get_self_id(), input);
in_at = 0;
wclear(_G.wentry);
}
input[in_at++] = ch;
waddch(_G.wentry, ch);
//printf("%s\n", buf);
}
if(FD_ISSET(_G.sock->sock, &fds)) {
int got = wsock_recv(_G.sock, &get);
if(got > 0) {
parse(get);
free(get);
}
}
}
wrefresh(_G.wchat);
wrefresh(_G.wentry);
}
wprintw(_G.wchat, "\nQuitting ...");
wrefresh(_G.wchat);
wsock_free(_G.sock);
ctx_free();
delwin(_G.wentry);
delwin(_G.wchat);
endwin(); endwin();
return 0; return 0;
} }
void chat_msg(const user_t* user, const char* msg) {
static int last_user = -1;
wprintw(_G.wchat, "\n%s: %s", user->name, msg);
}
void parse(char* msg) {
if(strlen(msg) == 0)
return;
wprintw(_G.wchat, "\n");
wprintw(_G.wchat, msg);
char *ptr = msg, count;
for(count = 1; (ptr = strchr(ptr + 1, '\t')) != NULL; ++count);
char** parts = malloc(sizeof(char*) * count);
parts[0] = ptr = msg;
for(int i = 1; (ptr = strchr(ptr + 1, '\t')) != NULL; ++i) {
*ptr = '\0';
parts[i] = ptr + 1;
}
user_t user, *puser;
channel_t chan, *pchan;
int id = strtol(parts[0], NULL, 10);
switch(id) {
case 1:
if(parts[1][0] == 'y') {
user.id = (int)strtol(parts[2], NULL, 10);
strncpy(user.name, parts[3], sizeof(user.name));
user.color = parse_color(parts[4]);
parse_perms(parts[5], USER_PERMS, user.perms);
add_user(&user);
set_self(user.id);
} else {
wprintw(_G.wchat, "Your authentication was rejected: ");
wprintw(_G.wchat, parts[2]);
wprintw(_G.wchat, "\nPress ESC to quit.\n");
_G.exit_poll = 1;
}
break;
case 2:
break;
}
free(parts);
}
const char* _home(const char* path) { const char* _home(const char* path) {
static char full_path[4096]; static char full_path[4096];
@ -131,3 +231,19 @@ const char* _home(const char* path) {
strcat(full_path, path); strcat(full_path, path);
return full_path; return full_path;
} }
void _ping() {
static char buffer[32];
static time_t last_ping = 0;
time_t now;
int self = get_self_id();
if(self == -1)
return;
sprintf(buffer, "0\t%i", self);
if(difftime(time(&now), last_ping) >= 15) {
wsock_send_str(_G.sock, buffer);
last_ping = now;
}
}

View file

@ -5,7 +5,7 @@
#define WS_REORDER_BUFLEN (sizeof(long double)) #define WS_REORDER_BUFLEN (sizeof(long double))
int _sys_is_net_order() { static int _sys_is_net_order() {
const uint16_t test = 0xB00B; const uint16_t test = 0xB00B;
return ((char*)&test)[0] == 0xB0; return ((char*)&test)[0] == 0xB0;
} }
@ -67,7 +67,7 @@ void buffer_append_str(buffer_t* buf, const char* data) {
buffer_append(buf, data, strlen(data)); buffer_append(buf, data, strlen(data));
} }
int _buffer_read(buffer_t* buf, char* out, int n) { static int _buffer_read(buffer_t* buf, char* out, int n) {
if(buffer_length(buf) == 0) if(buffer_length(buf) == 0)
return 0; return 0;
@ -132,7 +132,7 @@ int buffer_flush_str(buffer_t* buf, char** out) {
return length; return length;
} }
void _buffer_clear(buffer_t* buf, int root) { static void _buffer_clear(buffer_t* buf, int root) {
if(buf == NULL) if(buf == NULL)
return; return;
_buffer_clear(buf->next, 0); _buffer_clear(buf->next, 0);
@ -160,14 +160,16 @@ void buffer_free(buffer_t* buf) {
/** WEB SOCKET **/ /** WEB SOCKET **/
/******************/ /******************/
int _wsock_header_length(char* head) { static int _wsock_header_length(char* head) {
return 2 return 2
+ ((head[1] & 0x80) == 0 ? 0 : 4) + ((head[1] & 0x80) == 0 ? 0 : 4)
+ ((head[1] & 0x7F) < 0x7E ? 0 + ((head[1] & 0x7F) < 0x7E ? 0
: ((head[1] & 0x7F) == 0x7E ? 2 : 8)); : ((head[1] & 0x7F) == 0x7E ? 2 : 8));
} }
int _wsock_read_header(char* head, _ws_head_t* out) { static int _wsock_read_header
(char* head, _ws_head_t* out)
{
out->head_length = _wsock_header_length(head); out->head_length = _wsock_header_length(head);
out->fin = 0x80 & head[0]; out->fin = 0x80 & head[0];
out->opcode = 0x0F & head[0]; out->opcode = 0x0F & head[0];
@ -207,12 +209,12 @@ int _wsock_read_header(char* head, _ws_head_t* out) {
return 1; return 1;
} }
void _wsock_mask(char* mask, char* msg, int length) { static void _wsock_mask(char* mask, char* msg, int length) {
for(int i = 0; i < length; ++i) for(int i = 0; i < length; ++i)
msg[i] = msg[i] ^ mask[i % 4]; msg[i] = msg[i] ^ mask[i % 4];
} }
void _wsock_gen_mask(char* mask) { static void _wsock_gen_mask(char* mask) {
for(int i = 0; i < 4; ++i) for(int i = 0; i < 4; ++i)
mask[i] = rand() % 0xFF; mask[i] = rand() % 0xFF;
} }
@ -308,7 +310,9 @@ int wsock_mode_get(wsock_t* ws) {
return ws->mode; return ws->mode;
} }
int _srecv(wsock_t* ws, char* out, int len, int flags) { static int _srecv
(wsock_t* ws, char* out, int len, int flags)
{
int got = recv(ws->sock, out, len, flags); int got = recv(ws->sock, out, len, flags);
if(got == EAGAIN || got == EWOULDBLOCK) if(got == EAGAIN || got == EWOULDBLOCK)
return 0; return 0;
@ -410,29 +414,24 @@ int wsock_recv(wsock_t* ws, char** out) {
case WS_PING: case WS_PING:
wsock_send_raw(ws, WS_PONG, body, body_length); wsock_send_raw(ws, WS_PONG, body, body_length);
break; break;
case WS_PONG:
break;
} }
if(WS_OP_IS_CTRL(ws->head_buf.opcode)) if(WS_OP_IS_CTRL(ws->head_buf.opcode)) {
got = 0;
free(body); free(body);
}
int opcode_was = ws->head_buf.opcode;
ws->head_buf.opcode = WS_OP_UNDEF; ws->head_buf.opcode = WS_OP_UNDEF;
if(WS_OP_IS_CTRL(opcode_was) || !ws->head_buf.fin) { if(!ws->head_buf.fin) {
got = 0; got = 0;
continue; continue;
} else } else
break; break;
} while(ws->mode & WS_FULL_RECV); } while(ws->mode & WS_FULL_RECV);
// catches a weird potential case where the server sends return got;
// empty data frames which will exit the main loop with
// the same condition as a nonblocking call that would
// block. to prevent confusion and the proper functioning
// of the WS_FULL_RECV flag, call recursively in this case
if(ws->mode & WS_FULL_RECV && got == 0)
return wsock_recv(ws, out);
else
return got;
} }
// todo: support sending fragments at some point // todo: support sending fragments at some point

View file

@ -94,6 +94,14 @@ typedef enum {
// has an EXTREME THIRST for partial sends, they can // has an EXTREME THIRST for partial sends, they can
// implement this behavior themselves // implement this behavior themselves
// !!! VERY IMPORTANT NOTE !!!
// if using WS_FULL_RECV, wsock_recv() can STILL RETURN 0
// this happens when a control frame is received from the
// server and is handled by this library. in order for
// select() or poll() to function correctly when used
// outside of the library this is a necessary behavior.
// please check for a 0 return and handle appropriately
typedef struct { typedef struct {
uint8_t fin, opcode, first_opcode, uint8_t fin, opcode, first_opcode,
masked, mask[4], head_length; masked, mask[4], head_length;