tAdd command infrastructure - ratox - FIFO based tox client
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 10466e4400fa3a6a8c80220547409beae60c2bab
 (DIR) parent c25b3394399c7409d0dd3d7862a456932c77c648
 (HTM) 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(-)
       ---
 (DIR) diff --git a/ratatox.c b/ratatox.c
       t@@ -3,6 +3,7 @@
        #include <sys/stat.h>
        #include <sys/types.h>
        
       +#include <ctype.h>
        #include <errno.h>
        #include <fcntl.h>
        #include <limits.h>
       t@@ -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 {
       t@@ -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
       t@@ -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)
       t@@ -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
       t@@ -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;
       t@@ -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;
                        }
                }
       t@@ -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;
        }
       t@@ -339,7 +472,7 @@ toxconnect(void)
                return 0;
        }
        
       -static int
       +static void
        id2str(uint8_t *id, uint8_t *idstr)
        {
                uint8_t hex[] = "0123456789abcdef";
       t@@ -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;
       t@@ -362,7 +505,7 @@ friendcreate(int32_t fid)
        
                f = calloc(1, sizeof(*f));
                if (!f) {
       -                perror("malloc");
       +                perror("calloc");
                        exit(1);
                }
        
       t@@ -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);
       t@@ -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)
        {
       t@@ -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++) {