commit 10466e4400fa3a6a8c80220547409beae60c2bab
parent c25b3394399c7409d0dd3d7862a456932c77c648
Author: sin <sin@2f30.org>
Date: Mon, 15 Sep 2014 11:35:31 +0100
Add command infrastructure
Diffstat:
M | ratatox.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++) {