250 lines
4.9 KiB
C
250 lines
4.9 KiB
C
#include <ncurses.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <time.h>
|
|
|
|
#include "wsock.h"
|
|
#include "ctx.h"
|
|
|
|
/** CONSTS & UTILITIES **/
|
|
/**************************/
|
|
|
|
const char FII_ADDR[] = "chatsrv-neru.flashii.net";
|
|
const char USAGE[] =
|
|
"clii -- Command-Line Flashii Chat\n"
|
|
"USAGE: clii [session key]\n\n"
|
|
"Session key will be stored for future use until\n"
|
|
"a new session key is given to the program.\n\n"
|
|
"If you do not know your session key, visit\n"
|
|
" https://flashii.net/_sockchat/token\n"
|
|
"on any web browser that is logged in.";
|
|
|
|
const char* _home(const char*);
|
|
void _ping();
|
|
|
|
|
|
/** GLOBALS **/
|
|
/***************/
|
|
|
|
struct {
|
|
int running, exit_poll;
|
|
char session[256];
|
|
wsock_t* sock;
|
|
WINDOW *wchat, *wentry;
|
|
user_t bot;
|
|
} _G;
|
|
|
|
|
|
/** MAIN **/
|
|
/************/
|
|
|
|
void print_msg(const user_t*, const char*);
|
|
void parse(char*);
|
|
|
|
int main(int argc, char** argv) {
|
|
srand(time(NULL));
|
|
FILE* fp;
|
|
|
|
switch(argc) {
|
|
case 1:
|
|
printf("Loading session key ...\n");
|
|
if((fp = fopen(_home(".fiikey"), "r")) == NULL) {
|
|
printf("No previous session key found.\n");
|
|
printf("%s\n", USAGE);
|
|
return -1;
|
|
}
|
|
|
|
fgets(_G.session, sizeof(_G.session), fp);
|
|
fclose(fp);
|
|
break;
|
|
case 2:
|
|
if(strncmp(argv[1], "--h", 3) == 0) {
|
|
printf("%s\n", USAGE);
|
|
return 0;
|
|
}
|
|
|
|
printf("New session key provided. Storing ...\n");
|
|
strcpy(_G.session, argv[1]);
|
|
if((fp = fopen(_home(".fiikey"), "w")) != NULL) {
|
|
fputs(_G.session, fp);
|
|
fclose(fp);
|
|
} else
|
|
printf("Could not write to ~/.fiikey. Session key will not be stored.\n");
|
|
break;
|
|
default:
|
|
printf("%s\n", USAGE);
|
|
return -1;
|
|
}
|
|
|
|
int in_at = 0;
|
|
char buf[2048], input[256], *get;
|
|
printf("Connecting to Flashii ...\n");
|
|
_G.sock = wsock_open(FII_ADDR, "/", 80);
|
|
|
|
fd_set fds;
|
|
struct timeval tout;
|
|
|
|
initscr();
|
|
start_color();
|
|
raw(); noecho();
|
|
keypad(stdscr, TRUE);
|
|
|
|
_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);
|
|
|
|
mvhline(LINES - 2, 0, ACS_HLINE, COLS - 1);
|
|
refresh();
|
|
|
|
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();
|
|
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) {
|
|
static char full_path[4096];
|
|
|
|
strcpy(full_path, getenv("HOME"));
|
|
strcat(full_path, "/");
|
|
strcat(full_path, 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;
|
|
}
|
|
}
|