ratox

FIFO based tox client
git clone git://git.2f30.org/ratox
Log | Files | Refs | README | LICENSE

commit 10466e4400fa3a6a8c80220547409beae60c2bab
parent c25b3394399c7409d0dd3d7862a456932c77c648
Author: sin <sin@2f30.org>
Date:   Mon, 15 Sep 2014 11:35:31 +0100

Add command infrastructure

Diffstat:
Mratatox.c | 361++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 320 insertions(+), 41 deletions(-)

diff --git a/ratatox.c b/ratatox.c @@ -3,6 +3,7 @@ #include <sys/stat.h> #include <sys/types.h> +#include <ctype.h> #include <errno.h> #include <fcntl.h> #include <limits.h> @@ -35,9 +36,10 @@ enum { static struct fifo { const char *name; + int flags; mode_t mode; } fifos[] = { - { "text_in", 0644 }, + { .name = "text_in", .flags = O_RDONLY | O_NONBLOCK, .mode = 0644 }, }; struct friend { @@ -51,28 +53,145 @@ struct friend { TAILQ_ENTRY(friend) entry; }; +struct request { + uint8_t id[TOX_CLIENT_ID_SIZE]; + /* null terminated id */ + uint8_t idstr[2 * TOX_CLIENT_ID_SIZE + 1]; + /* null terminated friend request message */ + uint8_t *msgstr; + TAILQ_ENTRY(request) entry; +}; + static TAILQ_HEAD(friendhead, friend) friendhead = TAILQ_HEAD_INITIALIZER(friendhead); +static TAILQ_HEAD(reqhead, request) reqhead = TAILQ_HEAD_INITIALIZER(reqhead); static Tox *tox; + +static void cb_conn_status(Tox *, int32_t, uint8_t, void *); +static void cb_friend_message(Tox *, int32_t, const uint8_t *, uint16_t, void *); +static void cb_friend_request(Tox *, const uint8_t *, const uint8_t *, uint16_t, void *); +static void cb_name_change(Tox *, int32_t, const uint8_t *, uint16_t, void *); +static void cb_status_message(Tox *, int32_t, const uint8_t *, uint16_t, void *); +static void send_friend_text(struct friend *); static void dataload(void); static void datasave(void); +static void toxrestore(void); +static int toxinit(void); +static int toxconnect(void); +static void id2str(uint8_t *, uint8_t *); +static void str2id(uint8_t *, uint8_t *); static void friendcreate(int32_t); +static void friendload(void); +static int cmdrun(void); +static int doaccept(char *, size_t); +static int dofriend(char *, size_t); +static int dohelp(char *, size_t); +static void loop(void); + +static char qsep[] = " \t\r\n"; + +/* tokenization routines taken from Plan9 */ +static char * +qtoken(char *s, char *sep) +{ + int quoting; + char *t; + + quoting = 0; + t = s; /* s is output string, t is input string */ + while(*t!='\0' && (quoting || strchr(sep, *t)==NULL)) { + if(*t != '\'') { + *s++ = *t++; + continue; + } + /* *t is a quote */ + if(!quoting) { + quoting = 1; + t++; + continue; + } + /* quoting and we're on a quote */ + if(t[1] != '\'') { + /* end of quoted section; absorb closing quote */ + t++; + quoting = 0; + continue; + } + /* doubled quote; fold one quote into two */ + t++; + *s++ = *t++; + } + if(*s != '\0') { + *s = '\0'; + if(t == s) + t++; + } + return t; +} -static void -masterout(const char *fmt, ...) +static char * +etoken(char *t, char *sep) { - FILE *fp; - va_list ap; + int quoting; + + /* move to end of next token */ + quoting = 0; + while(*t!='\0' && (quoting || strchr(sep, *t)==NULL)) { + if(*t != '\'') { + t++; + continue; + } + /* *t is a quote */ + if(!quoting) { + quoting = 1; + t++; + continue; + } + /* quoting and we're on a quote */ + if(t[1] != '\'') { + /* end of quoted section; absorb closing quote */ + t++; + quoting = 0; + continue; + } + /* doubled quote; fold one quote into two */ + t += 2; + } + return t; +} - fp = fopen("master_out", "a"); - if (!fp) { - perror("fopen"); - exit(1); +static int +gettokens(char *s, char **args, int maxargs, char *sep) +{ + int nargs; + + for(nargs=0; nargs<maxargs; nargs++) { + while(*s!='\0' && strchr(sep, *s)!=NULL) + *s++ = '\0'; + if(*s == '\0') + break; + args[nargs] = s; + s = etoken(s, sep); } - va_start(ap, fmt); - vfprintf(fp, fmt, ap); - va_end(ap); - fclose(fp); + + return nargs; +} + +static int +tokenize(char *s, char **args, int maxargs) +{ + int nargs; + + for(nargs=0; nargs<maxargs; nargs++) { + while(*s!='\0' && strchr(qsep, *s)!=NULL) + s++; + if(*s == '\0') + break; + args[nargs] = s; + s = qtoken(s, qsep); + } + + return nargs; } static void @@ -92,14 +211,14 @@ cb_conn_status(Tox *tox, int32_t fid, uint8_t status, void *udata) if (n == 0) { if (status == 0) - masterout("Anonymous went offline\n"); + printf("Anonymous went offline\n"); else - masterout("Anonymous came online\n"); + printf("Anonymous came online\n"); } else { if (status == 0) - masterout("%s went offline\n", name); + printf("%s went offline\n", name); else - masterout("%s came online\n", name); + printf("%s came online\n", name); } TAILQ_FOREACH(f, &friendhead, entry) @@ -139,16 +258,30 @@ cb_friend_message(Tox *tox, int32_t fid, const uint8_t *data, uint16_t len, void static void cb_friend_request(Tox *tox, const uint8_t *id, const uint8_t *data, uint16_t len, void *udata) { - uint8_t msg[len + 1]; + struct request *req; - memcpy(msg, data, len); - msg[len] = '\0'; - tox_add_friend_norequest(tox, id); - if (len > 0) - masterout("Accepted friend request with msg: %s\n", msg); - else - masterout("Accepted friend request\n"); - datasave(); + req = calloc(1, sizeof(*req)); + if (!req) { + perror("calloc"); + exit(1); + } + memcpy(req->id, id, TOX_CLIENT_ID_SIZE); + id2str(req->id, req->idstr); + + if (len > 0) { + req->msgstr = malloc(len + 1); + if (!req->msgstr) { + perror("malloc"); + exit(1); + } + memcpy(req->msgstr, data, len); + req->msgstr[len] = '\0'; + } + + TAILQ_INSERT_TAIL(&reqhead, req, entry); + + printf("Pending request from %s with message: %s\n", + req->idstr, req->msgstr); } static void @@ -176,9 +309,9 @@ cb_name_change(Tox *m, int32_t fid, const uint8_t *data, uint16_t len, void *use if (memcmp(f->namestr, name, len + 1) == 0) break; if (f->namestr[0] == '\0') { - masterout("%s -> %s\n", "Anonymous", name); + printf("%s -> %s\n", "Anonymous", name); } else { - masterout("%s -> %s\n", f->namestr, name); + printf("%s -> %s\n", f->namestr, name); } memcpy(f->namestr, name, len + 1); break; @@ -209,7 +342,7 @@ cb_status_message(Tox *m, int32_t fid, const uint8_t *data, uint16_t len, void * fputs(status, fp); fputc('\n', fp); fclose(fp); - masterout("%s current status to %s\n", f->namestr, status); + printf("%s current status to %s\n", f->namestr, status); break; } } @@ -318,10 +451,10 @@ toxinit(void) tox_set_user_status(tox, TOX_USERSTATUS_NONE); tox_get_address(tox, address); - masterout("ID: "); + printf("ID: "); for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; i++) - masterout("%02x", address[i]); - masterout("\n"); + printf("%02x", address[i]); + printf("\n"); return 0; } @@ -339,7 +472,7 @@ toxconnect(void) return 0; } -static int +static void id2str(uint8_t *id, uint8_t *idstr) { uint8_t hex[] = "0123456789abcdef"; @@ -353,6 +486,16 @@ id2str(uint8_t *id, uint8_t *idstr) } static void +str2id(uint8_t *idstr, uint8_t *id) +{ + size_t i, len = strlen(idstr) / 2; + char *p = idstr; + + for (i = 0; i < len; ++i, p += 2) + sscanf(p, "%2hhx", &id[i]); +} + +static void friendcreate(int32_t fid) { struct friend *f; @@ -362,7 +505,7 @@ friendcreate(int32_t fid) f = calloc(1, sizeof(*f)); if (!f) { - perror("malloc"); + perror("calloc"); exit(1); } @@ -391,7 +534,7 @@ friendcreate(int32_t fid) perror("mkfifo"); exit(1); } - r = open(path, O_RDONLY | O_NONBLOCK, 0); + r = open(path, fifos[i].flags, 0); if (r < 0) { perror("open"); exit(1); @@ -423,6 +566,134 @@ friendload(void) friendcreate(fids[i]); } +struct cmd { + const char *cmd; + int (*cb)(char *, size_t); + const char *helpstr; +} cmds[] = { + { .cmd = "a", .cb = doaccept, .helpstr = "usage: a [ID]\tAccept or list pending requests\n" }, + { .cmd = "f", .cb = dofriend, .helpstr = "usage: f ID\tSend friend request to ID\n" }, + { .cmd = "h", .cb = dohelp, .helpstr = NULL }, +}; + +static int +doaccept(char *cmd, size_t sz) +{ + struct request *req, *tmp; + char *args[2]; + int n; + int found = 0; + + n = tokenize(cmd, args, 2); + + if (n == 1) { + TAILQ_FOREACH(req, &reqhead, entry) { + printf("Pending request from %s with message: %s\n", + req->idstr, req->msgstr); + found = 1; + } + if (found == 0) + printf("No pending requests\n"); + } else { + for (req = TAILQ_FIRST(&reqhead); req; req = tmp) { + tmp = TAILQ_NEXT(req, entry); + if (strcmp(req->idstr, args[1]) == 0) { + tox_add_friend_norequest(tox, req->id); + printf("Accepted friend request for %s\n", req->idstr); + TAILQ_REMOVE(&reqhead, req, entry); + free(req->msgstr); + free(req); + break; + } + } + } + + return 0; +} + +static int +dofriend(char *cmd, size_t sz) +{ + char *args[2]; + uint8_t id[TOX_FRIEND_ADDRESS_SIZE]; + uint8_t idstr[2 * TOX_FRIEND_ADDRESS_SIZE + 1]; + char *msgstr = "ratatox is awesome!"; + int r; + + r = tokenize(cmd, args, 2); + if (r != 2) { + printf("Command error, type h for help\n"); + return -1; + } + str2id(args[1], id); + + r = tox_add_friend(tox, id, msgstr, strlen(msgstr)); + switch (r) { + case TOX_FAERR_TOOLONG: + printf("Message is too long\n"); + break; + case TOX_FAERR_NOMESSAGE: + printf("Please add a message to your request\n"); + break; + case TOX_FAERR_OWNKEY: + printf("That appears to be your own ID\n"); + break; + case TOX_FAERR_ALREADYSENT: + printf("Friend request already sent\n"); + break; + case TOX_FAERR_UNKNOWN: + printf("Unknown error while sending your request\n"); + break; + default: + printf("Friend request sent\n"); + break; + } + return 0; +} + +static int +dohelp(char *cmd, size_t sz) +{ + size_t i; + + for (i = 0; i < LEN(cmds); i++) + if (cmds[i].helpstr) + printf("%s", cmds[i].helpstr); + return 0; +} + +static int +cmdrun(void) +{ + char cmd[BUFSIZ]; + ssize_t n; + size_t i; + +again: + n = read(STDIN_FILENO, cmd, sizeof(cmd) - 1); + if (n < 0) { + if (errno == EINTR) + goto again; + perror("read"); + exit(1); + } + if (n == 0) + return 0; + cmd[n] = '\0'; + if (cmd[strlen(cmd) - 1] == '\n') + cmd[strlen(cmd) - 1] = '\0'; + if (cmd[0] == '\0') + return 0; + + for (i = 0; i < LEN(cmds); i++) + if (cmd[0] == cmds[i].cmd[0]) + if (cmd[1] == '\0' || isspace((int)cmd[1])) + return (*cmds[i].cb)(cmd, strlen(cmd)); + + fprintf(stderr, "unknown command: %s\n", cmd); + return -1; +} + static void loop(void) { @@ -430,47 +701,55 @@ loop(void) time_t t0, t1; int connected = 0; int i, n; - int fdmax = 0; + int fdmax; fd_set rfds; struct timeval tv; t0 = time(NULL); + printf("Connecting to DHT...\n"); toxconnect(); while (1) { if (tox_isconnected(tox) == 1) { if (connected == 0) { - masterout("Connected to DHT\n"); + printf("Connected to DHT\n"); connected = 1; } } else { t1 = time(NULL); if (t1 > t0 + 5) { t0 = time(NULL); - masterout("Connecting to DHT...\n"); + printf("Connecting to DHT...\n"); toxconnect(); } } tox_do(tox); FD_ZERO(&rfds); + FD_SET(STDIN_FILENO, &rfds); + fdmax = STDIN_FILENO; + TAILQ_FOREACH(f, &friendhead, entry) { for (i = 0; i < NR_FIFOS; i++) { + FD_SET(f->fd[i], &rfds); if (f->fd[i] > fdmax) fdmax = f->fd[i]; - FD_SET(f->fd[i], &rfds); } } tv.tv_sec = 0; tv.tv_usec = tox_do_interval(tox) * 1000; - n = select(fdmax + 1, &rfds, NULL, NULL, - &tv); + n = select(fdmax + 1, &rfds, NULL, NULL, &tv); if (n < 0) { if (errno == EINTR) continue; perror("select"); exit(1); } + if (n == 0) + continue; + + if (FD_ISSET(STDIN_FILENO, &rfds) != 0) + cmdrun(); TAILQ_FOREACH(f, &friendhead, entry) { for (i = 0; i < NR_FIFOS; i++) {